From ae0cedb2303a58f86c584179d637da4b6df5d8c5 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Thu, 14 Aug 2014 14:39:36 +0300 Subject: [PATCH 001/592] custom StringUtils --- .../src/main/java/com/cloudinary/Api.java | 1 - .../main/java/com/cloudinary/Cloudinary.java | 2 - .../main/java/com/cloudinary/Coordinates.java | 7 +- .../main/java/com/cloudinary/StringUtils.java | 93 +++++++++++++++++++ .../java/com/cloudinary/Transformation.java | 1 - .../main/java/com/cloudinary/Uploader.java | 10 +- .../src/main/java/com/cloudinary/Url.java | 2 - .../src/main/java/com/cloudinary/Util.java | 4 +- 8 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/StringUtils.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 42e10ba4..76581968 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -15,7 +15,6 @@ import java.text.SimpleDateFormat; import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.Header; import org.apache.http.client.methods.HttpDelete; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c92ad878..c8c25066 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -13,13 +13,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang.StringUtils; import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.ClientConnectionManager; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java index 70bb3fec..f6366153 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java @@ -1,11 +1,8 @@ package com.cloudinary; -import java.util.Collection; -import java.util.ArrayList; - -import org.apache.commons.lang.StringUtils; - import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collection; public class Coordinates { Collection coordinates = new ArrayList(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/StringUtils.java b/cloudinary-core/src/main/java/com/cloudinary/StringUtils.java new file mode 100644 index 00000000..a9e3ebe8 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/StringUtils.java @@ -0,0 +1,93 @@ +package com.cloudinary; + +import java.util.List; +import java.util.Collection; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang.StringEscapeUtils; + +public class StringUtils { + public static final String EMPTY = ""; + + public static String join(List list, String separator) { + if (list == null) { + return null; + } + + return join( list.toArray(), separator, 0, list.size()); + } + + + public static String join(Object[] array, String separator) { + if (array == null) { + return null; + } + return join(array, separator, 0, array.length); + } + + public static String join(Collection collection,String separator) { + if (collection == null) { + return null; + } + + return join( collection.toArray(new String[collection.size()]), separator, 0, collection.size()); + } + + public static String join(final Object[] array, String separator, + final int startIndex, final int endIndex) { + if (array == null) { + return null; + } + if (separator == null) { + separator = EMPTY; + } + + final int noOfItems = endIndex - startIndex; + if (noOfItems <= 0) { + return EMPTY; + } + + final StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + public static String encodeHexString(byte[] bytes) { + return Hex.encodeHexString(bytes); + } + + public static String escapeHtml(String input) { + return StringEscapeUtils.escapeHtml(input); + } + + public static boolean isNotBlank(String input) { + return !isBlank(input); + } + + public static String encodeBase64URLSafeString(byte[] input) { + return Base64.encodeBase64URLSafeString(input); + } + + public static boolean isBlank(String input) { + int strLen; + if (input == null || (strLen = input.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (Character.isWhitespace(input.charAt(i)) == false) { + return false; + } + } + return true; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 76d11a78..76aaebf1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -10,7 +10,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.Transformer; -import org.apache.commons.lang.StringUtils; @SuppressWarnings({ "rawtypes", "unchecked" }) public class Transformation { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 777a9f4e..ec34238d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -1,32 +1,28 @@ package com.cloudinary; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.FileInputStream; -import java.io.ByteArrayInputStream; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; 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.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 org.apache.http.conn.ClientConnectionManager; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index c6bc6bb6..45a9eae4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -9,8 +9,6 @@ import java.util.zip.CRC32; import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang.StringUtils; public class Url { String cloudName; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 1b5dd40e..b49a7cb3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -1,14 +1,12 @@ package com.cloudinary; -import org.apache.commons.lang.StringUtils; - -import java.awt.Rectangle; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + public class Util { static final String[] BOOLEAN_UPLOAD_OPTIONS = new String[] { "backup", "exif", "faces", "colors", "image_metadata", "use_filename", "unique_filename", From 9db9a3b4eb39fe6071af18a709d40f3b7a66127e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 20 Aug 2014 13:56:17 +0300 Subject: [PATCH 002/592] unified Java API and created basic implementation --- .../src/main/java/com/cloudinary/Api.java | 496 +---- .../main/java/com/cloudinary/Cloudinary.java | 288 +-- .../java/com/cloudinary/CloudinaryBase.java | 196 ++ .../main/java/com/cloudinary/Coordinates.java | 32 +- .../com/cloudinary/EagerTransformation.java | 4 +- .../java/com/cloudinary/SmartUrlEncoder.java | 4 +- .../main/java/com/cloudinary/StoredFile.java | 228 ++- .../java/com/cloudinary/Transformation.java | 67 +- .../com/cloudinary/URLBuilderWrapper.java | 28 + .../main/java/com/cloudinary/Uploader.java | 343 +--- .../java/com/cloudinary/UploaderBase.java | 330 ++++ .../src/main/java/com/cloudinary/Url.java | 171 +- .../src/main/java/com/cloudinary/Util.java | 54 +- .../main/java/com/cloudinary/api/ApiBase.java | 233 +++ .../java/com/cloudinary/api/ApiResponse.java | 12 + .../cloudinary/api/AuthorizationRequired.java | 11 + .../java/com/cloudinary/api/RateLimit.java | 37 + .../java/com/cloudinary/api/Response.java | 67 + .../api/exceptions/AlreadyExists.java | 9 + .../api/exceptions/ApiException.java | 9 + .../cloudinary/api/exceptions/BadRequest.java | 10 + .../api/exceptions/GeneralError.java | 8 + .../cloudinary/api/exceptions/NotAllowed.java | 9 + .../cloudinary/api/exceptions/NotFound.java | 9 + .../api/exceptions/RateLimited.java | 9 + .../utils/AbstractURLBuilderWrapper.java | 12 + .../com/cloudinary/utils/Base64Coder.java | 317 ++++ .../java/com/cloudinary/utils/HtmlEscape.java | 183 ++ .../com/cloudinary/utils/ObjectUtils.java | 112 ++ .../java/com/cloudinary/utils/Rectangle.java | 17 + .../cloudinary/{ => utils}/StringUtils.java | 54 +- .../src/main/java/org/json/JSONArray.java | 977 ++++++++++ .../src/main/java/org/json/JSONException.java | 43 + .../src/main/java/org/json/JSONObject.java | 1689 +++++++++++++++++ .../src/main/java/org/json/JSONString.java | 18 + .../src/main/java/org/json/JSONTokener.java | 446 +++++ .../java/com/cloudinary/test/ApiTest.java | 268 +-- .../com/cloudinary/test/CloudinaryTest.java | 27 +- .../com/cloudinary/test/UploaderTest.java | 150 +- 39 files changed, 5459 insertions(+), 1518 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/Response.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java rename cloudinary-core/src/main/java/com/cloudinary/{ => utils}/StringUtils.java (54%) create mode 100644 cloudinary-core/src/main/java/org/json/JSONArray.java create mode 100644 cloudinary-core/src/main/java/org/json/JSONException.java create mode 100644 cloudinary-core/src/main/java/org/json/JSONObject.java create mode 100644 cloudinary-core/src/main/java/org/json/JSONString.java create mode 100644 cloudinary-core/src/main/java/org/json/JSONTokener.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 76581968..d2877f6c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -3,442 +3,110 @@ import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.URI; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.Date; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpResponse; -import org.apache.http.Header; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.ClientConnectionManager; import org.apache.http.impl.client.DefaultHttpClient; import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class Api { - enum HttpMethod { GET, POST, PUT, DELETE } - - public static class RateLimit { - private long limit = 0L; - private long remaining = 0L; - private Date reset = null; - public RateLimit(){ - super(); - } - - public long getLimit() { - return limit; - } - public void setLimit(long limit) { - this.limit = limit; - } - public long getRemaining() { - return remaining; - } - public void setRemaining(long remaining) { - this.remaining = remaining; - } - public Date getReset() { - return reset; - } - public void setReset(Date reset) { - this.reset = reset; - } - } - - public static interface ApiResponse extends Map { - HttpResponse getRawHttpResponse(); - Map rateLimits() throws java.text.ParseException; - RateLimit apiRateLimit() throws java.text.ParseException; - } - - public static class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; - 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)"); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN); - 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 (!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")) { - 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"); - } - } - - public static class ApiException extends Exception { - private static final long serialVersionUID = 4416861825144420038L; - public ApiException(String message) { - super(message); - } - } - - public static class BadRequest extends ApiException { - private static final long serialVersionUID = 1410136354253339531L; - public BadRequest(String message) { - super(message); - } - } - - public static class AuthorizationRequired extends ApiException { - private static final long serialVersionUID = 7160740370855761014L; - public AuthorizationRequired(String message) { - super(message); - } - } - - public static class NotAllowed extends ApiException { - private static final long serialVersionUID = 4371365822491647653L; - public NotAllowed(String message) { - super(message); - } - } - - public static class NotFound extends ApiException { - private static final long serialVersionUID = -2072640462778940357L; - public NotFound(String message) { - super(message); - } - } - - public static class AlreadyExists extends ApiException { - private static final long serialVersionUID = 999568182896607322L; - public AlreadyExists(String message) { - super(message); - } - } - - public static class RateLimited extends ApiException { - private static final long serialVersionUID = -8298038106172355219L; - public RateLimited(String message) { - super(message); - } - } - - public static class GeneralError extends ApiException { - private static final long serialVersionUID = 4553362706625067182L; - public GeneralError(String message) { - super(message); - } - } - - public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); - static { - CLOUDINARY_API_ERROR_CLASSES.put(400, BadRequest.class); - CLOUDINARY_API_ERROR_CLASSES.put(401, AuthorizationRequired.class); - CLOUDINARY_API_ERROR_CLASSES.put(403, NotAllowed.class); - CLOUDINARY_API_ERROR_CLASSES.put(404, NotFound.class); - CLOUDINARY_API_ERROR_CLASSES.put(409, AlreadyExists.class); - CLOUDINARY_API_ERROR_CLASSES.put(420, RateLimited.class); - CLOUDINARY_API_ERROR_CLASSES.put(500, GeneralError.class); - } - - private final Cloudinary cloudinary; - - public Api(Cloudinary cloudinary) { - this.cloudinary = cloudinary; - } - - public ApiResponse ping(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("ping"), Cloudinary.emptyMap(), options); - } - - public ApiResponse usage(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("usage"), Cloudinary.emptyMap(), options); - } - - public ApiResponse resourceTypes(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("resources"), Cloudinary.emptyMap(), options); - } - - public ApiResponse resources(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type")); - List uri = new ArrayList(); - uri.add("resources"); - uri.add(resourceType); - if (type != null) - uri.add(type); - return callApi(HttpMethod.GET, uri, Cloudinary.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); - } - - public ApiResponse resourcesByTag(String tag, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), Cloudinary.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); - } - - public ApiResponse resourcesByIds(Iterable publicIds, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - Map params = Cloudinary.only(options, "tags", "context", "moderations"); - params.put("public_ids", publicIds); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); - } - - public ApiResponse resourcesByModeration(String kind, String status, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), Cloudinary.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); - } - - public ApiResponse resource(String public_id, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), - Cloudinary.only(options, "exif", "colors", "faces", "coordinates", - "image_metadata", "pages", "phash", "max_results"), options); - } - - public ApiResponse update(String public_id, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - Map params = new HashMap(); - Util.processWriteParameters(options, params); - params.put("moderation_status", options.get("moderation_status")); - return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), - params, options); - } - - public ApiResponse deleteResources(Iterable publicIds, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - Map params = Cloudinary.only(options, "keep_original", "next_cursor"); - params.put("public_ids", publicIds); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); - } +import com.cloudinary.api.ApiBase; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.Response; +import com.cloudinary.api.exceptions.GeneralError; +import com.cloudinary.utils.Base64Coder; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; - public ApiResponse deleteResourcesByPrefix(String prefix, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - Map params = Cloudinary.only(options, "keep_original", "next_cursor"); - params.put("prefix", prefix); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); - } +public class Api extends ApiBase { - public ApiResponse deleteResourcesByTag(String tag, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), Cloudinary.only(options, "keep_original", "next_cursor"), options); - } - - public ApiResponse deleteAllResources(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - String type = Cloudinary.asString(options.get("type"), "upload"); - Map filtered = Cloudinary.only(options, "keep_original", "next_cursor"); - filtered.put("all", true); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), filtered, options); - } - - public ApiResponse deleteDerivedResources(Iterable derivedResourceIds, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.DELETE, Arrays.asList("derived_resources"), Cloudinary.asMap("derived_resource_ids", derivedResourceIds), options); - } - - public ApiResponse tags(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String resourceType = Cloudinary.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.GET, Arrays.asList("tags", resourceType), Cloudinary.only(options, "next_cursor", "max_results", "prefix"), options); - } - - public ApiResponse transformations(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("transformations"), Cloudinary.only(options, "next_cursor", "max_results"), options); - } - - public ApiResponse transformation(String transformation, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("transformations", transformation), Cloudinary.only(options, "max_results"), options); - } - - public ApiResponse deleteTransformation(String transformation, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.DELETE, Arrays.asList("transformations", transformation), Cloudinary.emptyMap(), options); - } - - // updates - currently only supported update are: - // "allowed_for_strict": boolean flag - // "unsafe_update": transformation string - public ApiResponse updateTransformation(String transformation, Map updates, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.PUT, Arrays.asList("transformations", transformation), updates, options); - } - - public ApiResponse createTransformation(String name, String definition, Map options) throws Exception { - return callApi(HttpMethod.POST, Arrays.asList("transformations", name), Cloudinary.asMap("transformation", definition), options); - } - - public ApiResponse uploadPresets(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("upload_presets"), Cloudinary.only(options, "next_cursor", "max_results"), options); - } - - public ApiResponse uploadPreset(String name, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("upload_presets", name), Cloudinary.only(options, "max_results"), options); - } - - public ApiResponse deleteUploadPreset(String name, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - return callApi(HttpMethod.DELETE, Arrays.asList("upload_presets", name), Cloudinary.emptyMap(), options); - } - - public ApiResponse updateUploadPreset(String name, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - Map params = Util.buildUploadParams(options); - Util.clearEmpty(params); - params.putAll(Cloudinary.only(options, "unsigned", "disallow_public_id")); - return callApi(HttpMethod.PUT, Arrays.asList("upload_presets", name), params, options); - } - - public ApiResponse createUploadPreset(Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - Map params = Util.buildUploadParams(options); - Util.clearEmpty(params); - params.putAll(Cloudinary.only(options, "name", "unsigned", "disallow_public_id")); - return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); - } - - public ApiResponse rootFolders(Map options) throws Exception { - if (options == null) - options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders"), Cloudinary.emptyMap(), options); + public Api(CloudinaryBase cloudinary) { + super(cloudinary); + // TODO Auto-generated constructor stub } - public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception { + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + + protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { if (options == null) - options = Cloudinary.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), Cloudinary.emptyMap(), options); - } - - public Api withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; - } + options = ObjectUtils.emptyMap(); + String prefix = ObjectUtils.asString(options.get("upload_prefix"), this.cloudinary.getStringConfig("upload_prefix", "https://api.cloudinary.com")); + String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.cloudinary.getStringConfig("cloud_name")); + if (cloudName == null) + throw new IllegalArgumentException("Must supply cloud_name"); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary.getStringConfig("api_key")); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary.getStringConfig("api_secret")); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + + String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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())); + } + } + DefaultHttpClient client = new DefaultHttpClient(connectionManager); + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + request.setHeader("User-Agent", Cloudinary.USER_AGENT); - protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) options = Cloudinary.emptyMap(); - String prefix = Cloudinary.asString(options.get("upload_prefix"), - this.cloudinary.getStringConfig("upload_prefix", "https://api.cloudinary.com")); - String cloudName = Cloudinary.asString(options.get("cloud_name"), this.cloudinary.getStringConfig("cloud_name")); - if (cloudName == null) - throw new IllegalArgumentException("Must supply cloud_name"); - String apiKey = Cloudinary.asString(options.get("api_key"), this.cloudinary.getStringConfig("api_key")); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = Cloudinary.asString(options.get("api_secret"), this.cloudinary.getStringConfig("api_secret")); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); + HttpResponse response = client.execute(request); - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - 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(), Cloudinary.asString(param.getValue())); - } - } - DefaultHttpClient client = new DefaultHttpClient(connectionManager); - 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", "Basic " + Base64.encodeBase64String((apiKey + ":" + apiSecret).getBytes())); - request.setHeader("User-Agent", Cloudinary.USER_AGENT); - - HttpResponse response = client.execute(request); + int code = response.getStatusLine().getStatusCode(); + InputStream responseStream = response.getEntity().getContent(); + String responseData = StringUtils.read(responseStream); - int code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - String responseData = Uploader.readFully(responseStream); + Class exceptionClass = CLOUDINARY_API_ERROR_CLASSES.get(code); + if (code != 200 && exceptionClass == null) { + throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); + } + Map result; + try { + result = (Map) JSONValue.parseWithException(responseData); + } catch (ParseException e) { + throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); + } - Class exceptionClass = CLOUDINARY_API_ERROR_CLASSES.get(code); - if (code != 200 && exceptionClass == null) { - throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); - } - Map result; - try { - result = (Map) JSONValue.parseWithException(responseData); - } catch (ParseException 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); + } + } - 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); - } - } - - private ClientConnectionManager connectionManager = null; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c8c25066..6268a468 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -1,293 +1,39 @@ package com.cloudinary; -import java.io.UnsupportedEncodingException; -import java.net.URI; import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.TreeMap; -import org.apache.commons.codec.binary.Hex; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.ClientConnectionManager; +import com.cloudinary.api.ApiBase; +import com.cloudinary.utils.AbstractURLBuilderWrapper; -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class Cloudinary { - public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; - public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; - public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; - public final static String SHARED_CDN = AKAMAI_SHARED_CDN; +public class Cloudinary extends CloudinaryBase { - public final static String VERSION = "1.0.14"; - public final static String USER_AGENT = "cld-java-" + VERSION; - - private final Map config = new HashMap(); - private ClientConnectionManager connectionManager = null; - - public Cloudinary(Map config) { - this.config.putAll(config); + public Cloudinary() { + super(); } - public Cloudinary(String cloudinaryUrl) { - initFromUrl(cloudinaryUrl); + public Cloudinary(String string) { + super(string); } - public Cloudinary() { - String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); - if (cloudinaryUrl != null) { - initFromUrl(cloudinaryUrl); - } - + @SuppressWarnings("rawtypes") + public Cloudinary(Map config) { + super(config); } - public Url url() { - return new Url(this); + @Override + public AbstractURLBuilderWrapper urlBuilder(String source) throws URISyntaxException { + return new URLBuilderWrapper(source); } - public Uploader uploader() { + @Override + public UploaderBase uploader() { return new Uploader(this).withConnectionManager(connectionManager); } - public Api api() { + @Override + public ApiBase api() { return new Api(this).withConnectionManager(connectionManager); } - public String cloudinaryApiUrl(String action, Map options) { - String cloudinary = asString(options.get("upload_prefix"), asString(this.config.get("upload_prefix"), "https://api.cloudinary.com")); - String cloud_name = asString(options.get("cloud_name"), asString(this.config.get("cloud_name"))); - if (cloud_name == null) - throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); - String resource_type = asString(options.get("resource_type"), "image"); - return StringUtils.join(new String[] { cloudinary, "v1_1", cloud_name, resource_type, action }, "/"); - } - - private final static SecureRandom RND = new SecureRandom(); - - public String randomPublicId() { - byte[] bytes = new byte[8]; - RND.nextBytes(bytes); - return Hex.encodeHexString(bytes); - } - - public String signedPreloadedImage(Map result) { - return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") - + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); - } - - public String apiSignRequest(Map paramsToSign, String apiSecret) { - 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 { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - params.add(param.getKey() + "=" + value); - } - } - } - String to_sign = StringUtils.join(params, "&"); - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest((to_sign + apiSecret).getBytes()); - return Hex.encodeHexString(digest); - } - - public void signRequest(Map params, Map options) { - String apiKey = Cloudinary.asString(options.get("api_key"), this.getStringConfig("api_key")); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = Cloudinary.asString(options.get("api_secret"), this.getStringConfig("api_secret")); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - Util.clearEmpty(params); - params.put("signature", this.apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); - } - - public String privateDownload(String publicId, String format, Map options) throws URISyntaxException { - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("format", format); - params.put("attachment", options.get("attachment")); - params.put("type", options.get("type")); - params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); - signRequest(params, options); - URIBuilder builder = new URIBuilder(cloudinaryApiUrl("download", options)); - for (Map.Entry param : params.entrySet()) { - builder.addParameter(param.getKey(), param.getValue().toString()); - } - return builder.toString(); - } - - public String zipDownload(String tag, Map options) throws URISyntaxException { - Map params = new HashMap(); - params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); - params.put("tag", tag); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("transformation", transformation); - signRequest(params, options); - URIBuilder builder = new URIBuilder(cloudinaryApiUrl("download_tag.zip", options)); - for (Map.Entry param : params.entrySet()) { - builder.addParameter(param.getKey(), param.getValue().toString()); - } - return builder.toString(); - } - - protected void initFromUrl(String cloudinaryUrl) { - URI cloudinaryUri = URI.create(cloudinaryUrl); - setConfig("cloud_name", cloudinaryUri.getHost()); - String[] creds = cloudinaryUri.getUserInfo().split(":"); - setConfig("api_key", creds[0]); - setConfig("api_secret", creds[1]); - setConfig("private_cdn", StringUtils.isNotBlank(cloudinaryUri.getPath())); - setConfig("secure_distribution", cloudinaryUri.getPath()); - if (cloudinaryUri.getQuery() != null) { - for (String param : cloudinaryUri.getQuery().split("&")) { - String[] keyValue = param.split("="); - try { - setConfig(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - } - } - - public boolean getBooleanConfig(String key, boolean default_value) { - return asBoolean(this.config.get(key), default_value); - } - - public String getStringConfig(String key, String default_value) { - return asString(this.config.get(key), default_value); - } - - public String getStringConfig(String key) { - return asString(this.config.get(key)); - } - - public void setConfig(String key, Object value) { - this.config.put(key, value); - } - - public Cloudinary withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; - } - - public static String asString(Object value) { - if (value == null) { - return null; - } else { - return value.toString(); - } - } - - public static String asString(Object value, String defaultValue) { - if (value == null) { - return defaultValue; - } else { - return value.toString(); - } - } - - public static List asArray(Object value) { - if (value == null) { - return Collections.EMPTY_LIST; - } else if (value instanceof int[]) { - List array = new ArrayList(); - for (int i : (int[]) value) { - array.add(new Integer(i)); - } - return array; - } else if (value instanceof Object[]) { - return Arrays.asList((Object[]) value); - } else if (value instanceof List) { - return (List) value; - } else { - List array = new ArrayList(); - array.add(value); - return array; - } - } - - public static Boolean asBoolean(Object value, Boolean defaultValue) { - if (value == null) { - return defaultValue; - } else if (value instanceof Boolean) { - return (Boolean) value; - } else { - return "true".equals(value); - } - } - - public static Float asFloat(Object value) { - if (value == null) { - return null; - } else if (value instanceof Float) { - return (Float) value; - } else { - return Float.parseFloat(value.toString()); - } - } - - public static Map asMap(Object... values) { - if (values.length % 2 != 0) - throw new RuntimeException("Usage - (key, value, key, value, ...)"); - Map result = new HashMap(values.length / 2); - for (int i = 0; i < values.length; i += 2) { - result.put(values[i], values[i + 1]); - } - return result; - } - - public static Map emptyMap() { - return Collections.EMPTY_MAP; - } - - public static String encodeMap(Object arg) { - if (arg != null && arg instanceof Map) { - Map mapArg = (Map) arg; - HashSet out = new HashSet(); - for (Map.Entry entry : mapArg.entrySet()) { - out.add(entry.getKey() + "=" + entry.getValue()); - } - return StringUtils.join(out.toArray(), "|"); - } else if (arg == null) { - return null; - } else { - return arg.toString(); - } - } - - public static Map only(Map hash, String... keys) { - Map result = new HashMap(); - for (String key : keys) { - if (hash.containsKey(key)) { - result.put(key, hash.get(key)); - } - } - return result; - } - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java b/cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java new file mode 100644 index 00000000..171e33c4 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java @@ -0,0 +1,196 @@ +package com.cloudinary; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +//import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ClientConnectionManager; + +import com.cloudinary.api.ApiBase; +import com.cloudinary.utils.AbstractURLBuilderWrapper; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public abstract class CloudinaryBase { + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; + public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; + 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.0.14"; + public final static String USER_AGENT = "cld-java-" + VERSION; + + private final Map config = new HashMap(); + protected ClientConnectionManager connectionManager = null; + + protected abstract AbstractURLBuilderWrapper urlBuilder(String source) throws Exception; + public abstract UploaderBase uploader(); + public abstract ApiBase api(); + + public CloudinaryBase(Map config) { + this.config.putAll(config); + } + + public CloudinaryBase(String cloudinaryUrl) { + initFromUrl(cloudinaryUrl); + } + + public CloudinaryBase() { + String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); + if (cloudinaryUrl != null) { + initFromUrl(cloudinaryUrl); + } + + } + + public Url url() { + return new Url(this); + } + + + + public String cloudinaryApiUrl(String action, Map options) { + String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.config.get("upload_prefix"), "https://api.cloudinary.com")); + String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.get("cloud_name"))); + if (cloud_name == null) + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); + return StringUtils.join(new String[] { cloudinary, "v1_1", cloud_name, resource_type, action }, "/"); + } + + private final static SecureRandom RND = new SecureRandom(); + + public String randomPublicId() { + byte[] bytes = new byte[8]; + RND.nextBytes(bytes); + return StringUtils.encodeHexString(bytes); + } + + public String signedPreloadedImage(Map result) { + return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") + + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); + } + + public String apiSignRequest(Map paramsToSign, String apiSecret) { + 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 { + String value = param.getValue().toString(); + if (StringUtils.isNotBlank(value)) { + params.add(param.getKey() + "=" + value); + } + } + } + String to_sign = StringUtils.join(params, "&"); + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest((to_sign + apiSecret).getBytes()); + return StringUtils.encodeHexString(digest); + } + + public void signRequest(Map params, Map options) { + String apiKey = ObjectUtils.asString(options.get("api_key"), this.getStringConfig("api_key")); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.getStringConfig("api_secret")); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + Util.clearEmpty(params); + params.put("signature", this.apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + } + + + public String privateDownload(String publicId, String format, Map options) throws Exception { + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("format", format); + params.put("attachment", options.get("attachment")); + params.put("type", options.get("type")); + params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); + signRequest(params, options); + AbstractURLBuilderWrapper builder = urlBuilder(cloudinaryApiUrl("download", options)); + for (Map.Entry param : params.entrySet()) { + builder.addParam(param.getKey(), param.getValue().toString()); + } + return builder.url(); + } + + public String zipDownload(String tag, Map options) throws Exception { + Map params = new HashMap(); + params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); + params.put("tag", tag); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("transformation", transformation); + signRequest(params, options); + AbstractURLBuilderWrapper builder = urlBuilder(cloudinaryApiUrl("download_tag.zip", options)); + for (Map.Entry param : params.entrySet()) { + builder.addParam(param.getKey(), param.getValue().toString()); + } + return builder.url(); + } + + protected void initFromUrl(String cloudinaryUrl) { + URI cloudinaryUri = URI.create(cloudinaryUrl); + setConfig("cloud_name", cloudinaryUri.getHost()); + String[] creds = cloudinaryUri.getUserInfo().split(":"); + setConfig("api_key", creds[0]); + setConfig("api_secret", creds[1]); + setConfig("private_cdn", StringUtils.isNotBlank(cloudinaryUri.getPath())); + setConfig("secure_distribution", cloudinaryUri.getPath()); + if (cloudinaryUri.getQuery() != null) { + for (String param : cloudinaryUri.getQuery().split("&")) { + String[] keyValue = param.split("="); + try { + setConfig(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + } + } + + public boolean getBooleanConfig(String key, boolean default_value) { + return ObjectUtils.asBoolean(this.config.get(key), default_value); + } + + public String getStringConfig(String key, String default_value) { + return ObjectUtils.asString(this.config.get(key), default_value); + } + + public String getStringConfig(String key) { + return ObjectUtils.asString(this.config.get(key)); + } + + public void setConfig(String key, Object value) { + this.config.put(key, value); + } + + public CloudinaryBase withConnectionManager(ClientConnectionManager connectionManager) { + this.connectionManager = connectionManager; + return this; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java index f6366153..3c08b4a6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java @@ -1,19 +1,22 @@ package com.cloudinary; -import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; + +import com.cloudinary.utils.Rectangle; +import com.cloudinary.utils.StringUtils; + public class Coordinates { Collection coordinates = new ArrayList(); - + public Coordinates() { } public Coordinates(Collection coordinates) { this.coordinates = coordinates; } - + public Coordinates(int[] rect) { Collection coordinates = new ArrayList(); if (rect.length != 4) { @@ -22,34 +25,32 @@ public Coordinates(int[] rect) { coordinates.add(new Rectangle(rect[0], rect[1], rect[2], rect[3])); this.coordinates = coordinates; } - + public Coordinates(Rectangle rect) { Collection coordinates = new ArrayList(); coordinates.add(rect); this.coordinates = coordinates; } - + public Coordinates(String stringCoords) throws IllegalArgumentException { Collection coordinates = new ArrayList(); for (String stringRect : stringCoords.split("\\|")) { - if (stringRect.isEmpty()) continue; + if (StringUtils.isEmpty(stringRect)) + continue; String[] elements = stringRect.split(","); if (elements.length != 4) { - throw new IllegalArgumentException(String.format("Must supply exactly 4 values for coordinates (x,y,width,height) %d supplied: %s", elements.length, stringRect)); + throw new IllegalArgumentException(String.format("Must supply exactly 4 values for coordinates (x,y,width,height) %d supplied: %s", + elements.length, stringRect)); } - coordinates.add(new Rectangle( - Integer.parseInt(elements[0]), - Integer.parseInt(elements[1]), - Integer.parseInt(elements[2]), - Integer.parseInt(elements[3]))); + coordinates.add(new Rectangle(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2]), Integer + .parseInt(elements[3]))); } this.coordinates = coordinates; } - - + public static Coordinates parseCoordinates(Object coordinates) throws IllegalArgumentException { if (coordinates instanceof Coordinates) { - return (Coordinates)coordinates; + return (Coordinates) coordinates; } else if (coordinates instanceof int[]) { return new Coordinates((int[]) coordinates); } else if (coordinates instanceof Rectangle) { @@ -63,7 +64,6 @@ public void addRect(Rectangle rect) { this.coordinates.add(rect); } - public Collection underlaying() { return this.coordinates; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index bfd9c0e1..d774e6e5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -5,6 +5,8 @@ public class EagerTransformation extends Transformation { protected String format; + + @SuppressWarnings("rawtypes") public EagerTransformation(List transformations) { super(transformations); } @@ -17,7 +19,7 @@ public EagerTransformation format(String format) { this.format = format; return this; } - + public String getFormat() { return format; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java index 8c72f9fd..26de3239 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java @@ -4,11 +4,11 @@ import java.net.URLEncoder; public class SmartUrlEncoder { - public static String encode(String input) { + public static String encode(String input) { try { return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); - } + } } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java index 739c15e5..5d9805aa 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java +++ b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java @@ -6,119 +6,117 @@ import java.util.regex.Pattern; public class StoredFile { - protected Long version; - - protected String publicId; - - protected String format; - - protected String signature; - - protected String type = "upload"; - - protected String resourceType = "image"; - - private static final String IMAGE_RESOURCE_TYPE = "image"; - - private static final String AUTO_RESOURCE_TYPE = "auto"; - - private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); - - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - public String getPublicId() { - return publicId; - } - - public void setPublicId(String publicId) { - this.publicId = publicId; - } - - protected String getPublicIdForSigning() { - return publicId + ((format != null && !format.isEmpty() && resourceType.equals("raw")) ? "." + format : ""); - } - - public String getFormat() { - return format; - } - - public void setFormat(String format) { - this.format = format; - } - - public String getSignature() { - return signature; - } - - public void setSignature(String signature) { - this.signature = signature; - } - - public String getResourceType() { - return resourceType; - } - - public void setResourceType(String resourceType) { - this.resourceType = resourceType; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getPreloadedFile() { - StringBuilder sb = new StringBuilder(); - sb.append(resourceType).append("/") - .append(type) - .append("/v").append(version).append("/") - .append(publicId); - if (format != null && !format.isEmpty()) { - sb.append(".").append(format); - } - if (signature != null && !signature.isEmpty()) { - sb.append("#").append(signature); - } - return sb.toString(); - } - - public void setPreloadedFile(String uri) { - if (uri.matches(PRELOADED_PATTERN.pattern())) { - Matcher match = PRELOADED_PATTERN.matcher(uri); - match.find(); - resourceType = match.group(1); - type = match.group(2); - version = Long.parseLong(match.group(3)); - String filename = match.group(4); - if (match.groupCount() == 5) signature = match.group(5); - int lastDotIndex = filename.lastIndexOf('.'); - if (lastDotIndex == -1) { - publicId = filename; - } else { - publicId = filename.substring(0, lastDotIndex); - format = filename.substring(lastDotIndex + 1); - } - } - } - - public String getComputedSignature(Cloudinary cloudinary) { - Map params = new HashMap(); - params.put("version", getVersion().toString()); - params.put("public_id", getPublicIdForSigning()); - cloudinary.signRequest(params, new HashMap()); - return params.get("signature").toString(); - } - - public boolean getIsImage() { - return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); - } + protected Long version; + + protected String publicId; + + protected String format; + + protected String signature; + + protected String type = "upload"; + + protected String resourceType = "image"; + + private static final String IMAGE_RESOURCE_TYPE = "image"; + + private static final String AUTO_RESOURCE_TYPE = "auto"; + + private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getPublicId() { + return publicId; + } + + public void setPublicId(String publicId) { + this.publicId = publicId; + } + + protected String getPublicIdForSigning() { + return publicId + ((format != null && !format.isEmpty() && resourceType.equals("raw")) ? "." + format : ""); + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPreloadedFile() { + StringBuilder sb = new StringBuilder(); + sb.append(resourceType).append("/").append(type).append("/v").append(version).append("/").append(publicId); + if (format != null && !format.isEmpty()) { + sb.append(".").append(format); + } + if (signature != null && !signature.isEmpty()) { + sb.append("#").append(signature); + } + return sb.toString(); + } + + public void setPreloadedFile(String uri) { + if (uri.matches(PRELOADED_PATTERN.pattern())) { + Matcher match = PRELOADED_PATTERN.matcher(uri); + match.find(); + resourceType = match.group(1); + type = match.group(2); + version = Long.parseLong(match.group(3)); + String filename = match.group(4); + if (match.groupCount() == 5) + signature = match.group(5); + int lastDotIndex = filename.lastIndexOf('.'); + if (lastDotIndex == -1) { + publicId = filename; + } else { + publicId = filename.substring(0, lastDotIndex); + format = filename.substring(lastDotIndex + 1); + } + } + } + + public String getComputedSignature(CloudinaryBase cloudinary) { + Map params = new HashMap(); + params.put("version", getVersion().toString()); + params.put("public_id", getPublicIdForSigning()); + cloudinary.signRequest(params, new HashMap()); + return params.get("signature").toString(); + } + + public boolean getIsImage() { + return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 76aaebf1..99763fdf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -7,9 +7,8 @@ import java.util.SortedMap; import java.util.TreeMap; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.Predicate; -import org.apache.commons.collections.Transformer; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; @SuppressWarnings({ "rawtypes", "unchecked" }) public class Transformation { @@ -22,8 +21,7 @@ public class Transformation { protected static boolean defaultIsResponsive = false; protected static Object defaultDPR = null; - private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = Cloudinary.asMap("width", "auto", "crop", - "limit"); + private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); protected static Map responsiveWidthTransformation = null; public Transformation(Transformation transformation) { @@ -31,7 +29,7 @@ public Transformation(Transformation transformation) { this.hiDPI = transformation.isHiDPI(); this.isResponsive = transformation.isResponsive(); } - + // Warning: options will destructively updated! public Transformation(List transformations) { this.transformations = transformations; @@ -216,7 +214,7 @@ public String generate(Iterable optionsList) { } public String generate(Map options) { - boolean isResponsive = Cloudinary.asBoolean(options.get("responsive_width"), defaultIsResponsive); + boolean isResponsive = ObjectUtils.asBoolean(options.get("responsive_width"), defaultIsResponsive); String size = (String) options.get("size"); if (size != null) { @@ -224,13 +222,13 @@ public String generate(Map options) { options.put("width", size_components[0]); options.put("height", size_components[1]); } - String width = this.htmlWidth = Cloudinary.asString(options.get("width")); - String height = this.htmlHeight = Cloudinary.asString(options.get("height")); + String width = this.htmlWidth = ObjectUtils.asString(options.get("width")); + String height = this.htmlHeight = ObjectUtils.asString(options.get("height")); boolean hasLayer = StringUtils.isNotBlank((String) options.get("overlay")) || StringUtils.isNotBlank((String) options.get("underlay")); String crop = (String) options.get("crop"); - String angle = StringUtils.join(Cloudinary.asArray(options.get("angle")), "."); + String angle = StringUtils.join(ObjectUtils.asArray(options.get("angle")), "."); boolean noHtmlSizes = hasLayer || StringUtils.isNotBlank(angle) || "fit".equals(crop) || "limit".equals(crop); if (width != null && (width.equals("auto") || Float.parseFloat(width) < 1 || noHtmlSizes || isResponsive)) { @@ -250,31 +248,36 @@ public String generate(Map options) { color = color.replaceFirst("^#", "rgb:"); } - List transformations = Cloudinary.asArray(options.get("transformation")); - Predicate isAMap = new Predicate() { - public boolean evaluate(Object value) { - return value instanceof Map; + List transformations = ObjectUtils.asArray(options.get("transformation")); + boolean allNamed = true; + for (Object baseTransformation : transformations) { + if (baseTransformation instanceof Map) { + allNamed = false; + break; } - }; - String namedTransformation = null; - if (CollectionUtils.exists(transformations, isAMap)) { - CollectionUtils.transform(transformations, new Transformer() { - public Object transform(Object baseTransformation) { - if (baseTransformation instanceof Map) { - return generate((Map) baseTransformation); - } else { - Map map = new HashMap(); - map.put("transformation", baseTransformation); - return generate(map); - } - } - }); + } + String namedTransformation = null; + if (allNamed) { + namedTransformation = StringUtils.join(transformations,"."); + transformations = new ArrayList(); } else { - namedTransformation = StringUtils.join(transformations, "."); + List ts = transformations; transformations = new ArrayList(); + for (Object baseTransformation : ts) { + String transformationString; + if (baseTransformation instanceof Map) { + transformationString = generate((Map) baseTransformation); + } else { + Map map = new HashMap(); + map.put("transformation", baseTransformation); + transformationString = generate(map); + } + transformations.add(transformationString); + } } - String flags = StringUtils.join(Cloudinary.asArray(options.get("flags")), "."); + + String flags = StringUtils.join(ObjectUtils.asArray(options.get("flags")), "."); SortedMap params = new TreeMap(); params.put("w", width); @@ -285,7 +288,7 @@ public Object transform(Object baseTransformation) { params.put("co", color); params.put("a", angle); params.put("fl", flags); - String dpr = Cloudinary.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); + String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); params.put("dpr", dpr); String[] simple_params = new String[] { "x", "x", "y", "y", "r", "radius", "d", "default_image", "g", @@ -293,7 +296,7 @@ public Object transform(Object baseTransformation) { "dn", "density", "pg", "page", "dl", "delay", "e", "effect", "bo", "border", "q", "quality", "o", "opacity" }; for (int i = 0; i < simple_params.length; i += 2) { - params.put(simple_params[i], Cloudinary.asString(options.get(simple_params[i + 1]))); + params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); } List components = new ArrayList(); for (Map.Entry param : params.entrySet()) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java b/cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java new file mode 100644 index 00000000..2d63f211 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java @@ -0,0 +1,28 @@ +package com.cloudinary; + +import java.net.URISyntaxException; + +import org.apache.http.client.utils.URIBuilder; + +import com.cloudinary.utils.AbstractURLBuilderWrapper; + +public class URLBuilderWrapper extends AbstractURLBuilderWrapper { + + private URIBuilder builder; + + public URLBuilderWrapper(String source) throws URISyntaxException { + super(source); + this.builder = new URIBuilder(source); + } + + @Override + public void addParam(String key, Object value) { + builder.addParameter(key, (String) value); + } + + @Override + public String url() { + return builder.toString(); + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index ec34238d..0812c8cc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -1,271 +1,39 @@ package com.cloudinary; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringEscapeUtils; 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.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 org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class Uploader { - private final Cloudinary cloudinary; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; - public Uploader(Cloudinary cloudinary) { - this.cloudinary = cloudinary; - } - - public Map buildUploadParams(Map options) { - return Util.buildUploadParams(options); - } +public class Uploader extends UploaderBase { - public Map unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - HashMap nextOptions = new HashMap(options); - nextOptions.put("unsigned", true); - nextOptions.put("upload_preset", uploadPreset); - return upload(file, nextOptions); - } - - public Map upload(Object file, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = buildUploadParams(options); - return callApi("upload", params, options, file); - } - - public Map uploadLargeRaw(Object file, Map options) throws IOException { - return uploadLargeRaw(file, options, 20000000); - } - - public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { - InputStream input; - if (file instanceof InputStream) { - input = (InputStream) file; - } else if (file instanceof File) { - input = new FileInputStream((File) file); - } else if (file instanceof byte[]) { - input = new ByteArrayInputStream((byte[]) file); - } else { - input = new FileInputStream(new File(file.toString())); - } - try { - Map result = uploadLargeRawParts(input, options, bufferSize); - return result; - } finally { - input.close(); - } - } - - private Map uploadLargeRawParts(InputStream input, Map options, int bufferSize) throws IOException { - Map params = Cloudinary.only(options, "public_id", "backup", "type"); - Map nextParams = new HashMap(); - nextParams.putAll(params); - Map sentParams = new HashMap(); - - Map sentOptions = new HashMap(); - sentOptions.putAll(options); - sentOptions.put("resource_type", "raw"); - - byte[] buffer = new byte[bufferSize]; - int bytesRead = 0; - int currentBufferSize = 0; - int partNumber = 1; - while ((bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize)) != -1) { - if (bytesRead + currentBufferSize == bufferSize) { - nextParams.put("part_number", Integer.toString(partNumber)); - sentParams.clear(); - sentParams.putAll(nextParams); - Map response = callApi("upload_large", sentParams, sentOptions, buffer); - if (partNumber == 1) { - nextParams.put("public_id", response.get("public_id")); - nextParams.put("upload_id", response.get("upload_id")); - } - currentBufferSize = 0; - partNumber++; - } else { - currentBufferSize += bytesRead; - } - } - byte[] finalBuffer = new byte[currentBufferSize]; - System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); - nextParams.put("final", true); - nextParams.put("part_number", Integer.toString(partNumber)); - return callApi("upload_large", nextParams, sentOptions, finalBuffer); - } - - - public Map destroy(String publicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("public_id", publicId); - params.put("invalidate", Cloudinary.asBoolean(options.get("invalidate"), false).toString()); - return callApi("destroy", params, options, null); - } - - public Map rename(String fromPublicId, String toPublicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("overwrite", Cloudinary.asBoolean(options.get("overwrite"), false).toString()); - params.put("from_public_id", fromPublicId); - params.put("to_public_id", toPublicId); - return callApi("rename", params, options, null); - } - - public Map explicit(String publicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("callback", (String) options.get("callback")); - params.put("type", (String) options.get("type")); - params.put("eager", Util.buildEager((List) options.get("eager"))); - params.put("headers", Util.buildCustomHeaders(options.get("headers"))); - params.put("tags", StringUtils.join(Cloudinary.asArray(options.get("tags")), ",")); - if (options.get("face_coordinates") != null) { - params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); - } - if (options.get("custom_coordinates") != null) { - params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); - } - if (options.get("context") != null) { - params.put("context", Cloudinary.encodeMap(options.get("context"))); - } - return callApi("explicit", params, options, null); - } - - public Map generate_sprite(String tag, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transParam = options.get("transformation"); - Transformation transformation = null; - if (transParam instanceof Transformation) { - transformation = new Transformation((Transformation) transParam); - } else if (transParam instanceof String) { - transformation = new Transformation().rawTransformation((String) transParam); - } else { - transformation = new Transformation(); - } - String format = (String) options.get("format"); - if (format != null) { - transformation.fetchFormat(format); - } - params.put("transformation", transformation.generate()); - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); - return callApi("sprite", params, options, null); - } - - public Map multi(String tag, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); - return callApi("multi", params, options, null); - } - - public Map explode(String public_id, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("public_id", public_id); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - return callApi("explode", params, options, null); - } - - // options may include 'exclusive' (boolean) which causes clearing this tag - // from all other resources - public Map addTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - boolean exclusive = Cloudinary.asBoolean(options.get("exclusive"), false); - String command = exclusive ? "set_exclusive" : "add"; - return callTagsApi(tag, command, publicIds, options); - } - - public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - return callTagsApi(tag, "remove", publicIds, options); - } - - public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - return callTagsApi(tag, "replace", publicIds, options); - } - - public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("tag", tag); - params.put("command", command); - params.put("type", (String) options.get("type")); - params.put("public_ids", Arrays.asList(publicIds)); - return callApi("tags", params, options, null); - } - - private final static String[] TEXT_PARAMS = { "public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", - "font_style", "background", "opacity", "text_decoration" }; - - public Map text(String text, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("text", text); - for (String param : TEXT_PARAMS) { - params.put(param, Cloudinary.asString(options.get(param))); - } - return callApi("text", params, options, null); - } - - public void signRequestParams(Map params, Map options) { - params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); - cloudinary.signRequest(params, options); - } - - public Uploader withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; + public Uploader(CloudinaryBase cloudinary) { + super(cloudinary); } + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override public Map callApi(String action, Map params, Map options, Object file) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - boolean returnError = Cloudinary.asBoolean(options.get("return_error"), false); - + if (options == null) + options = ObjectUtils.emptyMap(); + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { signRequestParams(params, options); } else { @@ -278,19 +46,19 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT); - + MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); // 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(Cloudinary.asString(value))); + multipart.addPart(param.getKey() + "[]", new StringBody(ObjectUtils.asString(value))); } } else { String value = param.getValue().toString(); if (StringUtils.isNotBlank(value)) { multipart.addPart(param.getKey(), new StringBody(value)); - } + } } } @@ -301,19 +69,19 @@ public Map callApi(String action, Map params, Map options, Objec multipart.addPart("file", new FileBody((File) file)); } else if (file instanceof String) { multipart.addPart("file", new StringBody((String) file)); - } else if (file instanceof byte[]) { - multipart.addPart("file", new ByteArrayBody((byte[]) file, "file")); + } else if (file instanceof byte[]) { + multipart.addPart("file", new ByteArrayBody((byte[]) file, "file")); } else if (file == null) { - // no-problem + // no-problem } else { - throw new IOException("Uprecognized file parameter " + file); + throw new IOException("Uprecognized file parameter " + file); } postMethod.setEntity(multipart); HttpResponse response = client.execute(postMethod); int code = response.getStatusLine().getStatusCode(); InputStream responseStream = response.getEntity().getContent(); - String responseData = readFully(responseStream); + String responseData = StringUtils.read(responseStream); if (code != 200 && code != 400 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); @@ -336,77 +104,4 @@ public Map callApi(String action, Map params, Map options, Objec return result; } - public String uploadTagParams(Map options) { - if (options == null) options = new HashMap(); - if (options.get("resource_type") == null) { - options = new HashMap(options); - options.put("resource_type", "auto"); - } - - String callback = Cloudinary.asString(options.get("callback"), this.cloudinary.getStringConfig("callback")); - if (callback == null) { - throw new IllegalArgumentException("Must supply callback"); - } - options.put("callback", callback); - - Map params = this.buildUploadParams(options); - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - return JSONObject.toJSONString(params); - } - - public String getUploadUrl(Map options) { - if (options == null) options = new HashMap(); - return this.cloudinary.cloudinaryApiUrl("upload", options); - } - - public String unsignedImageUploadTag(String field, String uploadPreset, Map options, Map htmlOptions) { - Map nextOptions = new HashMap(options); - nextOptions.put("upload_preset", uploadPreset); - nextOptions.put("unsigned", true); - return imageUploadTag(field, nextOptions, htmlOptions); - } - - public String imageUploadTag(String field, Map options, Map htmlOptions) { - if (htmlOptions == null) htmlOptions = Cloudinary.emptyMap(); - - String tagParams = StringEscapeUtils.escapeHtml(uploadTagParams(options)); - - String cloudinaryUploadUrl = getUploadUrl(options); - - StringBuilder builder = new StringBuilder(); - builder.append(""); - return builder.toString(); - } - - protected static String readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length = 0; - while ((length = in.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - return new String(baos.toByteArray()); - } - - private ClientConnectionManager connectionManager = null; - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java b/cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java new file mode 100644 index 00000000..3c126921 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java @@ -0,0 +1,330 @@ +package com.cloudinary; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.conn.ClientConnectionManager; +import org.json.JSONObject; + +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public abstract class UploaderBase { + public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException; + + protected ClientConnectionManager connectionManager = null; + protected final CloudinaryBase cloudinary; + + public UploaderBase(CloudinaryBase cloudinary) { + this.cloudinary = cloudinary; + } + + public Map buildUploadParams(Map options) { + return Util.buildUploadParams(options); + } + + public Map unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + HashMap nextOptions = new HashMap(options); + nextOptions.put("unsigned", true); + nextOptions.put("upload_preset", uploadPreset); + return upload(file, nextOptions); + } + + public Map upload(Object file, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = buildUploadParams(options); + return callApi("upload", params, options, file); + } + + public Map uploadLargeRaw(Object file, Map options) throws IOException { + return uploadLargeRaw(file, options, 20000000); + } + + @SuppressWarnings("resource") + public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { + InputStream input; + if (file instanceof InputStream) { + input = (InputStream) file; + } else if (file instanceof File) { + input = new FileInputStream((File) file); + } else if (file instanceof byte[]) { + input = new ByteArrayInputStream((byte[]) file); + } else { + input = new FileInputStream(new File(file.toString())); + } + try { + Map result = uploadLargeRawParts(input, options, bufferSize); + return result; + } finally { + input.close(); + } + } + + private Map uploadLargeRawParts(InputStream input, Map options, int bufferSize) throws IOException { + Map params = ObjectUtils.only(options, "public_id", "backup", "type"); + Map nextParams = new HashMap(); + nextParams.putAll(params); + Map sentParams = new HashMap(); + + Map sentOptions = new HashMap(); + sentOptions.putAll(options); + sentOptions.put("resource_type", "raw"); + + byte[] buffer = new byte[bufferSize]; + int bytesRead = 0; + int currentBufferSize = 0; + int partNumber = 1; + while ((bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize)) != -1) { + if (bytesRead + currentBufferSize == bufferSize) { + nextParams.put("part_number", Integer.toString(partNumber)); + sentParams.clear(); + sentParams.putAll(nextParams); + Map response = callApi("upload_large", sentParams, sentOptions, buffer); + if (partNumber == 1) { + nextParams.put("public_id", response.get("public_id")); + nextParams.put("upload_id", response.get("upload_id")); + } + currentBufferSize = 0; + partNumber++; + } else { + currentBufferSize += bytesRead; + } + } + byte[] finalBuffer = new byte[currentBufferSize]; + System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); + nextParams.put("final", true); + nextParams.put("part_number", Integer.toString(partNumber)); + return callApi("upload_large", nextParams, sentOptions, finalBuffer); + } + + public Map destroy(String publicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("public_id", publicId); + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + return callApi("destroy", params, options, null); + } + + public Map rename(String fromPublicId, String toPublicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("overwrite", ObjectUtils.asBoolean(options.get("overwrite"), false).toString()); + params.put("from_public_id", fromPublicId); + params.put("to_public_id", toPublicId); + return callApi("rename", params, options, null); + } + + public Map explicit(String publicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("callback", (String) options.get("callback")); + params.put("type", (String) options.get("type")); + params.put("eager", Util.buildEager((List) options.get("eager"))); + params.put("headers", Util.buildCustomHeaders(options.get("headers"))); + params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); + if (options.get("face_coordinates") != null) { + params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); + } + if (options.get("custom_coordinates") != null) { + params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); + } + if (options.get("context") != null) { + params.put("context", ObjectUtils.encodeMap(options.get("context"))); + } + return callApi("explicit", params, options, null); + } + + public Map generate_sprite(String tag, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transParam = options.get("transformation"); + Transformation transformation = null; + if (transParam instanceof Transformation) { + transformation = new Transformation((Transformation) transParam); + } else if (transParam instanceof String) { + transformation = new Transformation().rawTransformation((String) transParam); + } else { + transformation = new Transformation(); + } + String format = (String) options.get("format"); + if (format != null) { + transformation.fetchFormat(format); + } + params.put("transformation", transformation.generate()); + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + return callApi("sprite", params, options, null); + } + + public Map multi(String tag, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + return callApi("multi", params, options, null); + } + + public Map explode(String public_id, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("public_id", public_id); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + return callApi("explode", params, options, null); + } + + // options may include 'exclusive' (boolean) which causes clearing this tag + // from all other resources + public Map addTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + boolean exclusive = ObjectUtils.asBoolean(options.get("exclusive"), false); + String command = exclusive ? "set_exclusive" : "add"; + return callTagsApi(tag, command, publicIds, options); + } + + public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + return callTagsApi(tag, "remove", publicIds, options); + } + + public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + return callTagsApi(tag, "replace", publicIds, options); + } + + public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("tag", tag); + params.put("command", command); + params.put("type", (String) options.get("type")); + params.put("public_ids", Arrays.asList(publicIds)); + return callApi("tags", params, options, null); + } + + private final static String[] TEXT_PARAMS = { "public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", + "background", "opacity", "text_decoration" }; + + public Map text(String text, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("text", text); + for (String param : TEXT_PARAMS) { + params.put(param, ObjectUtils.asString(options.get(param))); + } + return callApi("text", params, options, null); + } + + public void signRequestParams(Map params, Map options) { + params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); + cloudinary.signRequest(params, options); + } + + public UploaderBase withConnectionManager(ClientConnectionManager connectionManager) { + this.connectionManager = connectionManager; + return this; + } + + public String uploadTagParams(Map options) { + if (options == null) + options = new HashMap(); + if (options.get("resource_type") == null) { + options = new HashMap(options); + options.put("resource_type", "auto"); + } + + String callback = ObjectUtils.asString(options.get("callback"), this.cloudinary.getStringConfig("callback")); + if (callback == null) { + throw new IllegalArgumentException("Must supply callback"); + } + options.put("callback", callback); + + Map params = this.buildUploadParams(options); + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + return JSONObject.valueToString(params); + } + + public String getUploadUrl(Map options) { + if (options == null) + options = new HashMap(); + return this.cloudinary.cloudinaryApiUrl("upload", options); + } + + public String unsignedImageUploadTag(String field, String uploadPreset, Map options, Map htmlOptions) { + Map nextOptions = new HashMap(options); + nextOptions.put("upload_preset", uploadPreset); + nextOptions.put("unsigned", true); + return imageUploadTag(field, nextOptions, htmlOptions); + } + + public String imageUploadTag(String field, Map options, Map htmlOptions) { + if (htmlOptions == null) + htmlOptions = ObjectUtils.emptyMap(); + + String tagParams = StringUtils.escapeHtml(uploadTagParams(options)); + + String cloudinaryUploadUrl = getUploadUrl(options); + + StringBuilder builder = new StringBuilder(); + builder.append(""); + return builder.toString(); + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 45a9eae4..fb1e542a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -8,7 +8,9 @@ import java.util.TreeMap; import java.util.zip.CRC32; -import org.apache.commons.codec.binary.Base64; +import com.cloudinary.utils.Base64Coder; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; public class Url { String cloudName; @@ -23,12 +25,12 @@ public class Url { String resourceType = "image"; String format = null; String version = null; - String source = null; - String apiSecret = null; + String source = null; + String apiSecret = null; Transformation transformation = null; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - public Url(Cloudinary cloudinary) { + public Url(CloudinaryBase cloudinary) { this.cloudName = cloudinary.getStringConfig("cloud_name"); this.secureDistribution = cloudinary.getStringConfig("secure_distribution"); this.cname = cloudinary.getStringConfig("cname"); @@ -45,11 +47,11 @@ public Url type(String type) { return this; } - @Deprecated + @Deprecated public Url resourcType(String resourceType) { return resourceType(resourceType); } - + public Url resourceType(String resourceType) { this.resourceType = resourceType; return this; @@ -76,7 +78,7 @@ public Url cname(String cname) { } public Url version(Object version) { - this.version = Cloudinary.asString(version); + this.version = ObjectUtils.asString(version); return this; } @@ -105,35 +107,38 @@ public Url shorten(boolean shorten) { return this; } - public Url source(String source) { - this.source = source; - return this; - } + public Url source(String source) { + this.source = source; + return this; + } - public Url source(StoredFile source) { - if (source.getResourceType() != null) this.resourceType = source.getResourceType(); - if (source.getType() != null) this.type = source.getType(); - if (source.getVersion() != null) this.version = source.getVersion().toString(); - this.format = source.getFormat(); - this.source = source.getPublicId(); - return this; - } + public Url source(StoredFile source) { + if (source.getResourceType() != null) + this.resourceType = source.getResourceType(); + if (source.getType() != null) + this.type = source.getType(); + if (source.getVersion() != null) + this.version = source.getVersion().toString(); + this.format = source.getFormat(); + this.source = source.getPublicId(); + return this; + } public Transformation transformation() { if (this.transformation == null) this.transformation = new Transformation(); return this.transformation; } - + public Url signed(boolean signUrl) { this.signUrl = signUrl; return this; } - public String generate(String source) { - this.source = source; - return this.generate(); - } + public String generate(String source) { + this.source = source; + return this.generate(); + } public String generate() { if (type.equals("fetch") && StringUtils.isNotBlank(format)) { @@ -160,24 +165,26 @@ public String generate() { } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } - if (format != null) source = source + "." + format; + if (format != null) + source = source + "." + format; } String prefix; - boolean sharedDomain = !privateCdn; - if (secure) { - if (StringUtils.isBlank(secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(secureDistribution)) { - secureDistribution = privateCdn ? cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; - } - sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(secureDistribution); - prefix = "https://" + secureDistribution; - } else { + boolean sharedDomain = !privateCdn; + if (secure) { + if (StringUtils.isBlank(secureDistribution) || CloudinaryBase.OLD_AKAMAI_SHARED_CDN.equals(secureDistribution)) { + secureDistribution = privateCdn ? cloudName + "-res.cloudinary.com" : CloudinaryBase.SHARED_CDN; + } + sharedDomain = sharedDomain || CloudinaryBase.SHARED_CDN.equals(secureDistribution); + prefix = "https://" + secureDistribution; + } else { CRC32 crc32 = new CRC32(); crc32.update(source.getBytes()); String subdomain = cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; String host = cname != null ? cname : (privateCdn ? cloudName + "-" : "") + "res.cloudinary.com"; prefix = "http://" + subdomain + host; - } - if (sharedDomain) prefix = prefix + "/" + cloudName; + } + if (sharedDomain) + prefix = prefix + "/" + cloudName; if (shorten && resourceType.equals("image") && type.equals("upload")) { resourceType = "iu"; @@ -190,80 +197,84 @@ public String generate() { if (version != null) version = "v" + version; - - String rest = StringUtils.join(new String[] {transformationStr, version, source }, "/"); + + String rest = StringUtils.join(new String[] { transformationStr, version, source }, "/"); rest = rest.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); - + if (signUrl) { MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } - catch(NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest((rest + apiSecret).getBytes()); - String signature = Base64.encodeBase64URLSafeString(digest); + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest((rest + apiSecret).getBytes()); + String signature = Base64Coder.encodeURLSafeString(digest); rest = "s--" + signature.substring(0, 8) + "--/" + rest; } - + return StringUtils.join(new String[] { prefix, resourceType, type, rest }, "/").replaceAll("([^:])\\/+", "$1/"); } - + public String generateSpriteCss(String source) { this.type = "sprite"; - if (!source.endsWith(".css")) this.format = "css"; - return generate(source); + if (!source.endsWith(".css")) + this.format = "css"; + return generate(source); } - + + @SuppressWarnings("unchecked") public String imageTag(String source) { - return imageTag(source, Cloudinary.emptyMap()); + return imageTag(source, ObjectUtils.emptyMap()); } - public String imageTag(String source, Map attributes) { - this.source = source; - return imageTag(attributes); - } + public String imageTag(String source, Map attributes) { + this.source = source; + return imageTag(attributes); + } - public String imageTag() { - return imageTag(Cloudinary.emptyMap()); - } + @SuppressWarnings("unchecked") + public String imageTag() { + return imageTag(ObjectUtils.emptyMap()); + } - public String imageTag(StoredFile source) { - return imageTag(source, Cloudinary.emptyMap()); - } + @SuppressWarnings("unchecked") + public String imageTag(StoredFile source) { + return imageTag(source, ObjectUtils.emptyMap()); + } - public String imageTag(StoredFile source, Map attributes) { - source(source); - return imageTag(attributes); - } + public String imageTag(StoredFile source, Map attributes) { + source(source); + return imageTag(attributes); + } public String imageTag(Map attributes) { String url = generate(); - attributes = new TreeMap(attributes); // Make sure they are ordered. + attributes = new TreeMap(attributes); // Make sure they + // are ordered. if (transformation().getHtmlHeight() != null) attributes.put("height", transformation().getHtmlHeight()); if (transformation().getHtmlWidth() != null) attributes.put("width", transformation().getHtmlWidth()); - + boolean hiDPI = transformation().isHiDPI(); boolean responsive = transformation().isResponsive(); - + if (hiDPI || responsive) { - attributes.put("data-src", url); - String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; - attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); - String responsivePlaceholder = attributes.remove("responsive_placeholder"); - if ("blank".equals(responsivePlaceholder)) { - responsivePlaceholder = CL_BLANK; - } - url = responsivePlaceholder; + attributes.put("data-src", url); + String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; + attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); + String responsivePlaceholder = attributes.remove("responsive_placeholder"); + if ("blank".equals(responsivePlaceholder)) { + responsivePlaceholder = CL_BLANK; + } + url = responsivePlaceholder; } - + StringBuilder builder = new StringBuilder(); builder.append(" attr : attributes.entrySet()) { builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index b49a7cb3..25958eec 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -6,14 +6,17 @@ import java.util.List; import java.util.Map; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; 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"}; - - protected static final Map buildUploadParams(Map options) { - if (options == null) options = Cloudinary.emptyMap(); + 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" }; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static final Map buildUploadParams(Map options) { + if (options == null) + options = ObjectUtils.emptyMap(); Map params = new HashMap(); Object transformation = options.get("transformation"); if (transformation != null) { @@ -27,23 +30,23 @@ protected static final Map buildUploadParams(Map options) { params.put("format", (String) options.get("format")); params.put("type", (String) options.get("type")); for (String attr : BOOLEAN_UPLOAD_OPTIONS) { - Boolean value = Cloudinary.asBoolean(options.get(attr), null); + Boolean value = ObjectUtils.asBoolean(options.get(attr), null); if (value != null) - params.put(attr, value.toString()); + params.put(attr, value.toString()); } params.put("eager", buildEager((List) options.get("eager"))); params.put("notification_url", (String) options.get("notification_url")); params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("proxy", (String) options.get("proxy")); params.put("folder", (String) options.get("folder")); - params.put("allowed_formats", StringUtils.join(Cloudinary.asArray(options.get("allowed_formats")), ",")); + params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); params.put("upload_preset", options.get("upload_preset")); - + processWriteParameters(options, params); return params; } - + protected static final String buildEager(List transformations) { if (transformations == null) { return null; @@ -65,22 +68,19 @@ protected static final String buildEager(List transfor } return StringUtils.join(eager, "|"); } - - protected static final void processWriteParameters( - Map options, Map params) { + + @SuppressWarnings("unchecked") + public static final void processWriteParameters(Map options, Map params) { if (options.get("headers") != null) params.put("headers", buildCustomHeaders(options.get("headers"))); if (options.get("tags") != null) - params.put("tags", StringUtils.join( - Cloudinary.asArray(options.get("tags")), ",")); + params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); if (options.get("face_coordinates") != null) - params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")) - .toString()); + params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); if (options.get("custom_coordinates") != null) - params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")) - .toString()); + params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); if (options.get("context") != null) - params.put("context", Cloudinary.encodeMap(options.get("context"))); + params.put("context", ObjectUtils.encodeMap(options.get("context"))); if (options.get("ocr") != null) params.put("ocr", options.get("ocr")); if (options.get("raw_convert") != null) @@ -94,10 +94,10 @@ protected static final void processWriteParameters( if (options.get("background_removal") != null) params.put("background_removal", options.get("background_removal")); if (options.get("auto_tagging") != null) - params.put("auto_tagging", - Cloudinary.asFloat(options.get("auto_tagging"))); + params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); } + @SuppressWarnings("unchecked") protected static final String buildCustomHeaders(Object headers) { if (headers == null) { return null; @@ -109,14 +109,14 @@ protected static final String buildCustomHeaders(Object headers) { Map headersMap = (Map) headers; StringBuilder builder = new StringBuilder(); for (Map.Entry header : headersMap.entrySet()) { - builder.append(header.getKey()).append(": ") - .append(header.getValue()).append("\n"); + builder.append(header.getKey()).append(": ").append(header.getValue()).append("\n"); } return builder.toString(); } } - - protected static void clearEmpty(Map params){ + + @SuppressWarnings("rawtypes") + public static void clearEmpty(Map params) { for (Iterator iterator = params.values().iterator(); iterator.hasNext();) { Object value = iterator.next(); if (value == null || "".equals(value)) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java b/cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java new file mode 100644 index 00000000..0d7cdb0b --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java @@ -0,0 +1,233 @@ +package com.cloudinary.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.conn.ClientConnectionManager; + +import com.cloudinary.CloudinaryBase; +import com.cloudinary.Util; +import com.cloudinary.api.exceptions.AlreadyExists; +import com.cloudinary.api.exceptions.BadRequest; +import com.cloudinary.api.exceptions.GeneralError; +import com.cloudinary.api.exceptions.NotAllowed; +import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.api.exceptions.RateLimited; +import com.cloudinary.utils.ObjectUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public abstract class ApiBase { + public enum HttpMethod { GET, POST, PUT, DELETE } + + public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); + static { + CLOUDINARY_API_ERROR_CLASSES.put(400, BadRequest.class); + CLOUDINARY_API_ERROR_CLASSES.put(401, AuthorizationRequired.class); + CLOUDINARY_API_ERROR_CLASSES.put(403, NotAllowed.class); + CLOUDINARY_API_ERROR_CLASSES.put(404, NotFound.class); + CLOUDINARY_API_ERROR_CLASSES.put(409, AlreadyExists.class); + CLOUDINARY_API_ERROR_CLASSES.put(420, RateLimited.class); + CLOUDINARY_API_ERROR_CLASSES.put(500, GeneralError.class); + } + + protected final CloudinaryBase cloudinary; + protected ClientConnectionManager connectionManager = null; + + protected abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception ; + + public ApiBase(CloudinaryBase cloudinary) { + this.cloudinary = cloudinary; + } + + public ApiResponse ping(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("ping"), ObjectUtils.emptyMap(), options); + } + + public ApiResponse usage(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("usage"), ObjectUtils.emptyMap(), options); + } + + public ApiResponse resourceTypes(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("resources"), ObjectUtils.emptyMap(), options); + } + + public ApiResponse resources(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")); + List uri = new ArrayList(); + uri.add("resources"); + uri.add(resourceType); + if (type != null) + uri.add(type); + return callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); + } + + public ApiResponse resourcesByTag(String tag, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + } + + public ApiResponse resourcesByIds(Iterable publicIds, 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 = ObjectUtils.only(options, "tags", "context", "moderations"); + params.put("public_ids", publicIds); + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); + } + + 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"); + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + } + + 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"); + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), + ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", + "image_metadata", "pages", "phash", "max_results"), options); + } + + 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"); + String type = ObjectUtils.asString(options.get("type"), "upload"); + Map params = new HashMap(); + Util.processWriteParameters(options, params); + params.put("moderation_status", options.get("moderation_status")); + return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), + params, options); + } + + public ApiResponse deleteResources(Iterable publicIds, 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 = ObjectUtils.only(options, "keep_original", "next_cursor"); + params.put("public_ids", publicIds); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); + } + + public ApiResponse deleteResourcesByPrefix(String prefix, 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 = ObjectUtils.only(options, "keep_original", "next_cursor"); + params.put("prefix", prefix); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); + } + + public ApiResponse deleteResourcesByTag(String tag, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "keep_original", "next_cursor"), options); + } + + public ApiResponse deleteAllResources(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 filtered = ObjectUtils.only(options, "keep_original", "next_cursor"); + filtered.put("all", true); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), filtered, options); + } + + public ApiResponse deleteDerivedResources(Iterable derivedResourceIds, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.DELETE, Arrays.asList("derived_resources"), ObjectUtils.asMap("derived_resource_ids", derivedResourceIds), options); + } + + public ApiResponse tags(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); + return callApi(HttpMethod.GET, Arrays.asList("tags", resourceType), ObjectUtils.only(options, "next_cursor", "max_results", "prefix"), options); + } + + public ApiResponse transformations(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("transformations"), ObjectUtils.only(options, "next_cursor", "max_results"), options); + } + + public ApiResponse transformation(String transformation, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("transformations", transformation), ObjectUtils.only(options, "max_results"), options); + } + + public ApiResponse deleteTransformation(String transformation, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.DELETE, Arrays.asList("transformations", transformation), ObjectUtils.emptyMap(), options); + } + + // updates - currently only supported update are: + // "allowed_for_strict": boolean flag + // "unsafe_update": transformation string + public ApiResponse updateTransformation(String transformation, Map updates, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.PUT, Arrays.asList("transformations", transformation), updates, options); + } + + public ApiResponse createTransformation(String name, String definition, Map options) throws Exception { + return callApi(HttpMethod.POST, Arrays.asList("transformations", name), ObjectUtils.asMap("transformation", definition), options); + } + + public ApiResponse uploadPresets(Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_presets"), ObjectUtils.only(options, "next_cursor", "max_results"), options); + } + + public ApiResponse uploadPreset(String name, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_presets", name), ObjectUtils.only(options, "max_results"), options); + } + + public ApiResponse deleteUploadPreset(String name, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.DELETE, Arrays.asList("upload_presets", name), ObjectUtils.emptyMap(), options); + } + + 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")); + return callApi(HttpMethod.PUT, Arrays.asList("upload_presets", name), params, options); + } + + 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")); + return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); + } + + public ApiResponse rootFolders(Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("folders"), ObjectUtils.emptyMap(), options); + } + + public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); + } + + public ApiBase withConnectionManager(ClientConnectionManager connectionManager) { + this.connectionManager = connectionManager; + return this; + } + + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java new file mode 100644 index 00000000..8353ad1e --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java @@ -0,0 +1,12 @@ +package com.cloudinary.api; + +import java.util.Map; + +import org.apache.http.HttpResponse; + +@SuppressWarnings("rawtypes") +public interface ApiResponse extends Map { + HttpResponse getRawHttpResponse(); + Map rateLimits() throws java.text.ParseException; + RateLimit apiRateLimit() throws java.text.ParseException; +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java b/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java new file mode 100644 index 00000000..0c39e1d5 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java @@ -0,0 +1,11 @@ +package com.cloudinary.api; + +import com.cloudinary.api.exceptions.ApiException; + +public class AuthorizationRequired extends ApiException { + private static final long serialVersionUID = 7160740370855761014L; + + public AuthorizationRequired(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java b/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java new file mode 100644 index 00000000..10496ca9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java @@ -0,0 +1,37 @@ +package com.cloudinary.api; + +import java.util.Date; + +public class RateLimit { + private long limit = 0L; + private long remaining = 0L; + private Date reset = null; + + public RateLimit() { + super(); + } + + public long getLimit() { + return limit; + } + + public void setLimit(long limit) { + this.limit = limit; + } + + public long getRemaining() { + return remaining; + } + + public void setRemaining(long remaining) { + this.remaining = remaining; + } + + public Date getReset() { + return reset; + } + + public void setReset(Date reset) { + this.reset = reset; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/Response.java b/cloudinary-core/src/main/java/com/cloudinary/api/Response.java new file mode 100644 index 00000000..a0351ee4 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/Response.java @@ -0,0 +1,67 @@ +package com.cloudinary.api; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +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.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)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); + + 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-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java new file mode 100644 index 00000000..a46939b8 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java @@ -0,0 +1,9 @@ +package com.cloudinary.api.exceptions; + +public class AlreadyExists extends ApiException { + private static final long serialVersionUID = 999568182896607322L; + + public AlreadyExists(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java new file mode 100644 index 00000000..8b33bdb9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java @@ -0,0 +1,9 @@ +package com.cloudinary.api.exceptions; + +public class ApiException extends Exception { + private static final long serialVersionUID = 4416861825144420038L; + + public ApiException(String message) { + super(message); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java new file mode 100644 index 00000000..b19aca10 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java @@ -0,0 +1,10 @@ +package com.cloudinary.api.exceptions; + + +public class BadRequest extends ApiException { + private static final long serialVersionUID = 1410136354253339531L; + + public BadRequest(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java new file mode 100644 index 00000000..92375009 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java @@ -0,0 +1,8 @@ +package com.cloudinary.api.exceptions; + +public class GeneralError extends ApiException { + private static final long serialVersionUID = 4553362706625067182L; + public GeneralError(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java new file mode 100644 index 00000000..b127d9ec --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java @@ -0,0 +1,9 @@ +package com.cloudinary.api.exceptions; + + +public class NotAllowed extends ApiException { + private static final long serialVersionUID = 4371365822491647653L; + public NotAllowed(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java new file mode 100644 index 00000000..418efaf9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java @@ -0,0 +1,9 @@ +package com.cloudinary.api.exceptions; + + +public class NotFound extends ApiException { + private static final long serialVersionUID = -2072640462778940357L; + public NotFound(String message) { + super(message); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java new file mode 100644 index 00000000..eea272e9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java @@ -0,0 +1,9 @@ +package com.cloudinary.api.exceptions; + + +public class RateLimited extends ApiException { + private static final long serialVersionUID = -8298038106172355219L; + public RateLimited(String message) { + super(message); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java b/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java new file mode 100644 index 00000000..b32f8827 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java @@ -0,0 +1,12 @@ +package com.cloudinary.utils; + +public abstract class AbstractURLBuilderWrapper { + + protected String source; + public AbstractURLBuilderWrapper(String source){ + this.source = source; + } + + public abstract void addParam(String key, Object value); + public abstract String url(); +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java new file mode 100644 index 00000000..a22ca79f --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java @@ -0,0 +1,317 @@ +//Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland +//www.source-code.biz, www.inventec.ch/chdh +// +//This module is multi-licensed and may be used under the terms +//of any of the following licenses: +// +//EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal +//LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html +//GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html +//AGPL, GNU Affero General Public License V3 or later, http://www.gnu.org/licenses/agpl.html +//AL, Apache License, V2.0 or later, http://www.apache.org/licenses +//BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php +//MIT, MIT License, http://www.opensource.org/licenses/MIT +// +//Please contact the author if you need another license. +//This module is provided "as is", without warranties of any kind. +package com.cloudinary.utils; + +/** + * A Base64 encoder/decoder. + * + *

+ * This class is used to encode and decode data in Base64 format as described in + * RFC 1521. + * + * @author Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland, + * www.source-code.biz + */ +public class Base64Coder { + + // The line separator string of the operating system. + private static final String systemLineSeparator = System + .getProperty("line.separator"); + + // Mapping table from 6-bit nibbles to Base64 characters. + private static final char[] map1 = new char[64]; + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) + map1[i++] = c; + for (char c = 'a'; c <= 'z'; c++) + map1[i++] = c; + for (char c = '0'; c <= '9'; c++) + map1[i++] = c; + map1[i++] = '+'; + map1[i++] = '/'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static final byte[] map2 = new byte[128]; + static { + for (int i = 0; i < map2.length; i++) + map2[i] = -1; + for (int i = 0; i < 64; i++) + map2[map1[i]] = (byte) i; + } + + /** + * Encodes a string into Base64 format. No blanks or line breaks are + * inserted. + * + * @param s + * A String to be encoded. + * @return A String containing the Base64 encoded data. + */ + public static String encodeString(String s) { + return new String(encode(s.getBytes())); + } + + /** + * Encodes a byte array into Base 64 format and breaks the output into lines + * of 76 characters. This method is compatible with + * sun.misc.BASE64Encoder.encodeBuffer(byte[]). + * + * @param in + * An array containing the data bytes to be encoded. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines(byte[] in) { + return encodeLines(in, 0, in.length, 76, systemLineSeparator); + } + + /** + * Encodes a byte array into Base 64 format and breaks the output into + * lines. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iOff + * Offset of the first byte in in to be processed. + * @param iLen + * Number of bytes to be processed in in, starting + * at iOff. + * @param lineLen + * Line length for the output data. Should be a multiple of 4. + * @param lineSeparator + * The line separator to be used to separate the output lines. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines(byte[] in, int iOff, int iLen, + int lineLen, String lineSeparator) { + int blockLen = (lineLen * 3) / 4; + if (blockLen <= 0) + throw new IllegalArgumentException(); + int lines = (iLen + blockLen - 1) / blockLen; + int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); + StringBuilder buf = new StringBuilder(bufLen); + int ip = 0; + while (ip < iLen) { + int l = Math.min(iLen - ip, blockLen); + buf.append(encode(in, iOff + ip, l)); + buf.append(lineSeparator); + ip += l; + } + return buf.toString(); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in) { + return encode(in, 0, in.length); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iLen + * Number of bytes to process in in. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iLen) { + return encode(in, 0, iLen); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in + * An array containing the data bytes to be encoded. + * @param iOff + * Offset of the first byte in in to be processed. + * @param iLen + * Number of bytes to process in in, starting at + * iOff. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iOff, int iLen) { + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iEnd ? in[ip++] & 0xff : 0; + int i2 = ip < iEnd ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = op < oDataLen ? map1[o2] : '='; + op++; + out[op] = op < oDataLen ? map1[o3] : '='; + op++; + } + return out; + } + + /** + * Decodes a string from Base64 format. No blanks or line breaks are allowed + * within the Base64 encoded input data. + * + * @param s + * A Base64 String to be decoded. + * @return A String containing the decoded data. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static String decodeString(String s) { + return new String(decode(s)); + } + + /** + * Decodes a byte array from Base64 format and ignores line separators, tabs + * and blanks. CR, LF, Tab and Space characters are ignored in the input + * data. This method is compatible with + * sun.misc.BASE64Decoder.decodeBuffer(String). + * + * @param s + * A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decodeLines(String s) { + char[] buf = new char[s.length()]; + int p = 0; + for (int ip = 0; ip < s.length(); ip++) { + char c = s.charAt(ip); + if (c != ' ' && c != '\r' && c != '\n' && c != '\t') + buf[p++] = c; + } + return decode(buf, 0, p); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param s + * A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(String s) { + return decode(s.toCharArray()); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in + * A character array containing the Base64 encoded data. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in) { + return decode(in, 0, in.length); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in + * A character array containing the Base64 encoded data. + * @param iOff + * Offset of the first character in in to be + * processed. + * @param iLen + * Number of characters to process in in, starting + * at iOff. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in, int iOff, int iLen) { + if (iLen % 4 != 0) + throw new IllegalArgumentException( + "Length of Base64 encoded input string is not a multiple of 4."); + while (iLen > 0 && in[iOff + iLen - 1] == '=') + iLen--; + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iEnd ? in[ip++] : 'A'; + int i3 = ip < iEnd ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) + out[op++] = (byte) o1; + if (op < oLen) + out[op++] = (byte) o2; + } + return out; + } + + // Dummy constructor. + private Base64Coder() { + } + + public static String encodeURLSafeString(byte[] digest) { + char[] encode = encode(digest); + for (int i = 0; i < encode.length; i++) { + if (encode[i] == '+') { + encode[i] = '-'; + } else if (encode[i] == '/') { + encode[i] = '_'; + } + } + return new String(encode); + } + +} // end class Base64Coder \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java new file mode 100644 index 00000000..fa40851e --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java @@ -0,0 +1,183 @@ +package com.cloudinary.utils; + + +/** +* HtmlEscape in Java, which is compatible with utf-8 +* @author Ulrich Jensen, http://www.htmlescape.net +* Feel free to get inspired, use or steal this code and use it in your +* own projects. +* License: +* You have the right to use this code in your own project or publish it +* on your own website. +* If you are going to use this code, please include the author lines. +* Use this code at your own risk. The author does not warrent or assume any +* legal liability or responsibility for the accuracy, completeness or usefullness of +* this program code. +*/ + +public class HtmlEscape { + + private static char[] hex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + /** + * Method for html escaping a String, for use in a textarea + * @param original The String to escape + * @return The escaped String + */ + public static String escapeTextArea(String original) + { + return escapeSpecial(escapeTags(original)); + } + + /** + * Normal escape function, for Html escaping Strings + * @param original The original String + * @return The escape String + */ + public static String escape(String original) + { + return escapeSpecial(escapeBr(escapeTags(original))); + } + + public static String escapeTags(String original) + { + if(original==null) return ""; + StringBuffer out=new StringBuffer(""); + char[] chars=original.toCharArray(); + for(int i=0;i + case 34:out.append("""); break; //" + default:found=false;break; + } + if(!found) out.append(chars[i]); + + } + return out.toString(); + + } + + public static String escapeBr(String original) + { + if(original==null) return ""; + StringBuffer out=new StringBuffer(""); + char[] chars=original.toCharArray(); + for(int i=0;i"); break; //newline + case '\r': break; + default:found=false;break; + } + if(!found) out.append(chars[i]); + + } + return out.toString(); + } + + public static String escapeSpecial(String original) + { + if(original==null) return ""; + StringBuffer out=new StringBuffer(""); + char[] chars=original.toCharArray(); + for(int i=0;i127) { + char c=chars[i]; + int a4=c%16; + c=(char) (c/16); + int a3=c%16; + c=(char) (c/16); + int a2=c%16; + c=(char) (c/16); + int a1=c%16; + out.append("&#x"+hex[a1]+hex[a2]+hex[a3]+hex[a4]+";"); + } + else + { + out.append(chars[i]); + } + } + } + return out.toString(); + } + +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java new file mode 100644 index 00000000..06a6f8e9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -0,0 +1,112 @@ +package com.cloudinary.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +public class ObjectUtils { + + public static String asString(Object value) { + if (value == null) { + return null; + } else { + return value.toString(); + } + } + + public static String asString(Object value, String defaultValue) { + if (value == null) { + return defaultValue; + } else { + return value.toString(); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List asArray(Object value) { + if (value == null) { + return Collections.EMPTY_LIST; + } else if (value instanceof int[]) { + List array = new ArrayList(); + for (int i : (int[]) value) { + array.add(new Integer(i)); + } + return array; + } else if (value instanceof Object[]) { + return Arrays.asList((Object[]) value); + } else if (value instanceof List) { + return (List) value; + } else { + List array = new ArrayList(); + array.add(value); + return array; + } + } + + public static Boolean asBoolean(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Boolean) { + return (Boolean) value; + } else { + return "true".equals(value); + } + } + + public static Float asFloat(Object value) { + if (value == null) { + return null; + } else if (value instanceof Float) { + return (Float) value; + } else { + return Float.parseFloat(value.toString()); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static Map asMap(Object... values) { + if (values.length % 2 != 0) + throw new RuntimeException("Usage - (key, value, key, value, ...)"); + Map result = new HashMap(values.length / 2); + for (int i = 0; i < values.length; i += 2) { + result.put(values[i], values[i + 1]); + } + return result; + } + + @SuppressWarnings("rawtypes") + public static Map emptyMap() { + return Collections.EMPTY_MAP; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static String encodeMap(Object arg) { + if (arg != null && arg instanceof Map) { + Map mapArg = (Map) arg; + HashSet out = new HashSet(); + for (Map.Entry entry : mapArg.entrySet()) { + out.add(entry.getKey() + "=" + entry.getValue()); + } + return StringUtils.join(out.toArray(), "|"); + } else if (arg == null) { + return null; + } else { + return arg.toString(); + } + } + + public static Map only(Map hash, String... keys) { + Map result = new HashMap(); + for (String key : keys) { + if (hash.containsKey(key)) { + result.put(key, hash.get(key)); + } + } + return result; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java new file mode 100644 index 00000000..1f88bf48 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java @@ -0,0 +1,17 @@ +package com.cloudinary.utils; + +public class Rectangle { + + public int height; + public int width; + public int y; + public int x; + + public Rectangle(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width= width; + this.height= height; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/StringUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java similarity index 54% rename from cloudinary-core/src/main/java/com/cloudinary/StringUtils.java rename to cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java index a9e3ebe8..b90e4ccf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -1,11 +1,10 @@ -package com.cloudinary; +package com.cloudinary.utils; -import java.util.List; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.Collection; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang.StringEscapeUtils; +import java.util.List; public class StringUtils { public static final String EMPTY = ""; @@ -14,11 +13,10 @@ public static String join(List list, String separator) { if (list == null) { return null; } - - return join( list.toArray(), separator, 0, list.size()); + + return join(list.toArray(), separator, 0, list.size()); } - public static String join(Object[] array, String separator) { if (array == null) { return null; @@ -26,16 +24,15 @@ public static String join(Object[] array, String separator) { return join(array, separator, 0, array.length); } - public static String join(Collection collection,String separator) { + public static String join(Collection collection, String separator) { if (collection == null) { return null; } - - return join( collection.toArray(new String[collection.size()]), separator, 0, collection.size()); + + return join(collection.toArray(new String[collection.size()]), separator, 0, collection.size()); } - public static String join(final Object[] array, String separator, - final int startIndex, final int endIndex) { + public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { if (array == null) { return null; } @@ -61,20 +58,31 @@ public static String join(final Object[] array, String separator, return buf.toString(); } + final protected static char[] hexArray = "0123456789abcdef".toCharArray(); + public static String encodeHexString(byte[] bytes) { - return Hex.encodeHexString(bytes); + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); } public static String escapeHtml(String input) { - return StringEscapeUtils.escapeHtml(input); + return HtmlEscape.escape(input); } public static boolean isNotBlank(String input) { return !isBlank(input); } - public static String encodeBase64URLSafeString(byte[] input) { - return Base64.encodeBase64URLSafeString(input); + public static boolean isEmpty(String input){ + if (input == null || input.length()== 0) { + return true; + } + return false; } public static boolean isBlank(String input) { @@ -90,4 +98,14 @@ public static boolean isBlank(String input) { return true; } + public static String read(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = in.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return new String(baos.toByteArray()); + } + } diff --git a/cloudinary-core/src/main/java/org/json/JSONArray.java b/cloudinary-core/src/main/java/org/json/JSONArray.java new file mode 100644 index 00000000..3f05548d --- /dev/null +++ b/cloudinary-core/src/main/java/org/json/JSONArray.java @@ -0,0 +1,977 @@ +package org.json; + +/* + Copyright (c) 2002 JSON.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + The Software shall be used for Good, not Evil. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there is , + *  (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, and + * if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, or + * null.
  • + *
+ * + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONArray { + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * + * @param x + * A JSONTokener + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() != ']') { + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + /** + * Construct a JSONArray from a source JSON text. + * + * @param source + * A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection + * A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = new ArrayList(); + if (collection != null) { + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + this.myArrayList.add(JSONObject.wrap(iter.next())); + } + } + } + + /** + * Construct a JSONArray from an array + * + * @throws JSONException + * If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + throw new JSONException( + "JSONArray initial value should be a string or collection or array."); + } + } + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException + * If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" + * and "false" are converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not + * convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a boolean."); + } + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public double getDouble(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).doubleValue() + : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).intValue() + : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a + * JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a + * JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); + } + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public long getLong(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).longValue() + : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String) object; + } + throw new JSONException("JSONArray[" + index + "] not a string."); + } + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. Warning: + * This method assumes that the data structure is acyclical. + * + * @param separator + * A string that will be inserted between the elements. + * @return a string. + * @throws JSONException + * If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) ? null : this.myArrayList + .get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return this.getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional int value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return this.getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional JSONArray associated with an index. + * + * @param index + * subscript + * @return A JSONArray value, or null if the index has no value, or if the + * value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = this.opt(index); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if + * the key is not found, or null if the index has no value, or if the value + * is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = this.opt(index); + return o instanceof JSONObject ? (JSONObject) o : null; + } + + /** + * Get the optional long value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return this.getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value is not a + * string and is not null, then it is coverted to a string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is + * returned if the key is not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object + .toString(); + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + this.put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param value + * A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + this.put(new JSONArray(value)); + return this; + } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @throws JSONException + * if the value is not finite. + * @return this. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + this.put(d); + return this; + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + public JSONArray put(int value) { + this.put(new Integer(value)); + return this; + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + public JSONArray put(long value) { + this.put(new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject which + * is produced from a Map. + * + * @param value + * A Map value. + * @return this. + */ + public JSONArray put(Map value) { + this.put(new JSONObject(value)); + return this; + } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param index + * The subscript. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + this.put(index, new JSONArray(value)); + return this; + } + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + this.put(index, new Double(value)); + return this; + } + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + this.put(index, new Integer(value)); + return this; + } + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + this.put(index, new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index + * The subscript. + * @param value + * The Map value. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value)); + return this; + } + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + this.myArrayList.set(index, value); + } else { + while (index != this.length()) { + this.put(JSONObject.NULL); + } + this.put(value); + } + return this; + } + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there + * was no value. + */ + public Object remove(int index) { + return index >= 0 && index < this.length() + ? this.myArrayList.remove(index) + : null; + } + + /** + * Determine if two JSONArrays are similar. + * They must contain similar sequences. + * + * @param other The other JSONArray + * @return true if they are equal + */ + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray)other).length()) { + return false; + } + for (int i = 0; i < len; i += 1) { + Object valueThis = this.get(i); + Object valueOther = ((JSONArray)other).get(i); + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } + + /** + * Produce a JSONObject by combining a JSONArray of names with the values of + * this JSONArray. + * + * @param names + * A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException + * If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || this.length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary + * whitespace is added. If it is not possible to produce a syntactically + * correct JSON text then null will be returned instead. This could occur if + * the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable representation of the + * array. + */ + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method + * assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0).toString(); + } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indention of the top level. + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean commanate = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + JSONObject.writeValue(writer, this.myArrayList.get(0), + indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newindent); + JSONObject.writeValue(writer, this.myArrayList.get(i), + indentFactor, newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } +} diff --git a/cloudinary-core/src/main/java/org/json/JSONException.java b/cloudinary-core/src/main/java/org/json/JSONException.java new file mode 100644 index 00000000..6fef5194 --- /dev/null +++ b/cloudinary-core/src/main/java/org/json/JSONException.java @@ -0,0 +1,43 @@ +package org.json; + +/** + * The JSONException is thrown by the JSON.org classes when things are amiss. + * + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONException extends RuntimeException { + private static final long serialVersionUID = 0; + private Throwable cause; + + /** + * Constructs a JSONException with an explanatory message. + * + * @param message + * Detail about the reason for the exception. + */ + public JSONException(String message) { + super(message); + } + + /** + * Constructs a new JSONException with the specified cause. + * @param cause The cause. + */ + public JSONException(Throwable cause) { + super(cause.getMessage()); + this.cause = cause; + } + + /** + * Returns the cause of this exception or null if the cause is nonexistent + * or unknown. + * + * @return the cause of this exception or null if the cause is nonexistent + * or unknown. + */ + @Override + public Throwable getCause() { + return this.cause; + } +} diff --git a/cloudinary-core/src/main/java/org/json/JSONObject.java b/cloudinary-core/src/main/java/org/json/JSONObject.java new file mode 100644 index 00000000..b7a21592 --- /dev/null +++ b/cloudinary-core/src/main/java/org/json/JSONObject.java @@ -0,0 +1,1689 @@ +package org.json; + +/* + Copyright (c) 2002 JSON.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + The Software shall be used for Good, not Evil. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ResourceBundle; +import java.util.Set; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its external + * form is a string wrapped in curly braces with colons between the names and + * values, and commas between the values and names. The internal form is an + * object having get and opt methods for accessing + * the values by name, and put methods for adding or replacing + * values by name. The values can be any of these types: Boolean, + * JSONArray, JSONObject, Number, + * String, or the JSONObject.NULL object. A + * JSONObject constructor can be used to convert an external form JSON text + * into an internal form whose values can be retrieved with the + * get and opt methods, or to convert values into a + * JSON text using the put and toString methods. A + * get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. The opt methods differ from the get methods in that they + * do not throw. Instead, they return a specified value, such as null. + *

+ * The put methods add or replace values in an object. For + * example, + * + *

+ * myString = new JSONObject()
+ *         .put("JSON", "Hello, World!").toString();
+ * 
+ * + * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON syntax rules. The constructors are more forgiving in the texts they + * will accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing brace.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a + * quote or single quote, and if they do not contain leading or trailing + * spaces, and if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and + * if they are not the reserved words true, false, + * or null.
  • + *
+ * + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONObject { + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * + * @return NULL. + */ + @Override + protected final Object clone() { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @param object + * An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object or + * null. + */ + @Override + public boolean equals(Object object) { + return object == null || object == this; + } + + /** + * Get the "null" string value. + * + * @return The string "null". + */ + public String toString() { + return "null"; + } + } + + /** + * The map where the JSONObject's properties are kept. + */ + private final Map map; + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + this.map = new HashMap(); + } + + /** + * Construct a JSONObject from a subset of another JSONObject. An array of + * strings is used to identify the keys that should be copied. Missing keys + * are ignored. + * + * @param jo + * A JSONObject. + * @param names + * An array of strings. + * @throws JSONException + * @exception JSONException + * If a value is a non-finite number or if a name is + * duplicated. + */ + public JSONObject(JSONObject jo, String[] names) { + this(); + for (int i = 0; i < names.length; i += 1) { + try { + this.putOnce(names[i], jo.opt(names[i])); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a JSONTokener. + * + * @param x + * A JSONTokener object containing the source string. + * @throws JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); + } + +// The key is followed by ':'. + + c = x.nextClean(); + if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + this.putOnce(key, x.nextValue()); + +// Pairs are separated by ','. + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + /** + * Construct a JSONObject from a Map. + * + * @param map + * A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException + */ + public JSONObject(Map map) { + this.map = new HashMap(); + if (map != null) { + Iterator> i = map.entrySet().iterator(); + while (i.hasNext()) { + Entry entry = i.next(); + Object value = entry.getValue(); + if (value != null) { + this.map.put(entry.getKey(), wrap(value)); + } + } + } + } + + /** + * Construct a JSONObject from an Object using bean getters. It reflects on + * all of the public methods of the object. For each of the methods with no + * parameters and a name starting with "get" or + * "is" followed by an uppercase letter, the method is invoked, + * and a key and the value returned from the getter method are put into the + * new JSONObject. + * + * The key is formed by removing the "get" or "is" + * prefix. If the second remaining character is not upper case, then the + * first character is converted to lower case. + * + * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is + * "Larry Fine", then the JSONObject will contain + * "name": "Larry Fine". + * + * @param bean + * An object that has getter methods that should be used to make + * a JSONObject. + */ + public JSONObject(Object bean) { + this(); + this.populateMap(bean); + } + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings from + * the names array, and the values will be the field values associated with + * those keys in the object. If a key is not found or not visible, then it + * will not be copied into the new JSONObject. + * + * @param object + * An object that has fields that should be used to make a + * JSONObject. + * @param names + * An array of strings, the names of the fields to be obtained + * from the object. + */ + @SuppressWarnings("rawtypes") + public JSONObject(Object object, String names[]) { + this(); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + String name = names[i]; + try { + this.putOpt(name, c.getField(name).get(object)); + } catch (Exception ignore) { + } + } + } + + /** + * Construct a JSONObject from a source JSON text string. This is the most + * commonly used JSONObject constructor. + * + * @param source + * A string beginning with { (left + * brace) and ending with } + *  (right brace). + * @exception JSONException + * If there is a syntax error in the source string or a + * duplicated key. + */ + public JSONObject(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONObject from a ResourceBundle. + * + * @param baseName + * The ResourceBundle base name. + * @param locale + * The Locale to load the ResourceBundle for. + * @throws JSONException + * If any JSONExceptions are detected. + */ + public JSONObject(String baseName, Locale locale) throws JSONException { + this(); + ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, + Thread.currentThread().getContextClassLoader()); + +// Iterate through the keys in the bundle. + + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (key != null) { + +// Go through the path, ensuring that there is a nested JSONObject for each +// segment except the last. Add the value using the last segment's name into +// the deepest nested JSONObject. + + String[] path = ((String) key).split("\\."); + int last = path.length - 1; + JSONObject target = this; + for (int i = 0; i < last; i += 1) { + String segment = path[i]; + JSONObject nextTarget = target.optJSONObject(segment); + if (nextTarget == null) { + nextTarget = new JSONObject(); + target.put(segment, nextTarget); + } + target = nextTarget; + } + target.put(path[last], bundle.getString((String) key)); + } + } + } + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a JSONArray + * is stored under the key to hold all of the accumulated values. If there + * is already a JSONArray, then the new value is appended to it. In + * contrast, the put method replaces the previous value. + * + * If only one value is accumulated that is not a JSONArray, then the result + * will be the same as using put. But if multiple values are accumulated, + * then the result will be like append. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the value is an invalid number or if the key is null. + */ + public JSONObject accumulate(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, + value instanceof JSONArray ? new JSONArray().put(value) + : value); + } else if (object instanceof JSONArray) { + ((JSONArray) object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * + * @param key + * A key string. + * @param value + * An object to be accumulated under the key. + * @return this. + * @throws JSONException + * If the key is null or if the current value associated with + * the key is not a JSONArray. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray) object).put(value)); + } else { + throw new JSONException("JSONObject[" + key + + "] is not a JSONArray."); + } + return this; + } + + /** + * Produce a string from a double. The string "null" will be returned if the + * number is not finite. + * + * @param d + * A double. + * @return A String. + */ + public static String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String string = Double.toString(d); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get the value object associated with a key. + * + * @param key + * A key string. + * @return The object associated with the key. + * @throws JSONException + * if the key is not found. + */ + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + quote(key) + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with a key. + * + * @param key + * A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or + * "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a Boolean."); + } + + /** + * Get the double value associated with a key. + * + * @param key + * A key string. + * @return The numeric value. + * @throws JSONException + * if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number ? ((Number) object).doubleValue() + : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a number."); + } + } + + /** + * Get the int value associated with a key. + * + * @param key + * A key string. + * @return The integer value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an integer. + */ + public int getInt(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number ? ((Number) object).intValue() + : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not an int."); + } + } + + /** + * Get the JSONArray value associated with a key. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONArray."); + } + + /** + * Get the JSONObject value associated with a key. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + * @throws JSONException + * if the key is not found or if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONObject."); + } + + /** + * Get the long value associated with a key. + * + * @param key + * A key string. + * @return The long value. + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to a long. + */ + public long getLong(String key) throws JSONException { + Object object = this.get(key); + try { + return object instanceof Number ? ((Number) object).longValue() + : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a long."); + } + } + + /** + * Get an array of field names from a JSONObject. + * + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(JSONObject jo) { + int length = jo.length(); + if (length == 0) { + return null; + } + Iterator iterator = jo.keys(); + String[] names = new String[length]; + int i = 0; + while (iterator.hasNext()) { + names[i] = iterator.next(); + i += 1; + } + return names; + } + + /** + * Get an array of field names from an Object. + * + * @return An array of field names, or null if there are no names. + */ + @SuppressWarnings("rawtypes") + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; i += 1) { + names[i] = fields[i].getName(); + } + return names; + } + + /** + * Get the string associated with a key. + * + * @param key + * A key string. + * @return A string which is the value. + * @throws JSONException + * if there is no string value for the key. + */ + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String) object; + } + throw new JSONException("JSONObject[" + quote(key) + "] not a string."); + } + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key + * A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1. If there is such a property, and if it is + * an Integer, Long, Double, or Float, then add one to it. + * + * @param key + * A key string. + * @return this. + * @throws JSONException + * If there is already a property with this name that is not an + * Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, (Integer) value + 1); + } else if (value instanceof Long) { + this.put(key, (Long) value + 1); + } else if (value instanceof Double) { + this.put(key, (Double) value + 1); + } else if (value instanceof Float) { + this.put(key, (Float) value + 1); + } else { + throw new JSONException("Unable to increment [" + quote(key) + "]."); + } + return this; + } + + /** + * Determine if the value associated with the key is null or if there is no + * value. + * + * @param key + * A key string. + * @return true if there is no value associated with the key or if the value + * is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(this.opt(key)); + } + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.keySet().iterator(); + } + + /** + * Get a set of keys of the JSONObject. + * + * @return A keySet. + */ + public Set keySet() { + return this.map.keySet(); + } + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + JSONArray ja = new JSONArray(); + Iterator keys = this.keys(); + while (keys.hasNext()) { + ja.put(keys.next()); + } + return ja.length() == 0 ? null : ja; + } + + /** + * Produce a string from a Number. + * + * @param number + * A Number + * @return A String. + * @throws JSONException + * If n is a non-finite number. + */ + public static String numberToString(Number number) throws JSONException { + if (number == null) { + throw new JSONException("Null pointer"); + } + testValidity(number); + +// Shave off trailing zeros and decimal point, if possible. + + String string = number.toString(); + if (string.indexOf('.') > 0 && string.indexOf('e') < 0 + && string.indexOf('E') < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + /** + * Get an optional value associated with a key. + * + * @param key + * A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + /** + * Get an optional boolean associated with a key. It returns false if there + * is no such key, or if the value is not Boolean.TRUE or the String "true". + * + * @param key + * A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + /** + * Get an optional boolean associated with a key. It returns the + * defaultValue if there is no such key, or if it is not a Boolean or the + * String "true" or "false" (case insensitive). + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + try { + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional double associated with a key, or NaN if there is no such + * key or if its value is not a number. If the value is a string, an attempt + * will be made to evaluate it as a number. + * + * @param key + * A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + /** + * Get an optional double associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + try { + return this.getDouble(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional int value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return this.optInt(key, 0); + } + + /** + * Get an optional int value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + try { + return this.getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional JSONArray associated with a key. It returns null if there + * is no such key, or if its value is not a JSONArray. + * + * @param key + * A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = this.opt(key); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get an optional JSONObject associated with a key. It returns null if + * there is no such key, or if its value is not a JSONObject. + * + * @param key + * A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject) object : null; + } + + /** + * Get an optional long value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return this.optLong(key, 0); + } + + /** + * Get an optional long value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + try { + return this.getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional string associated with a key. It returns an empty string + * if there is no such key. If the value is not a string and is not null, + * then it is converted to a string. + * + * @param key + * A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return this.optString(key, ""); + } + + /** + * Get an optional string associated with a key. It returns the defaultValue + * if there is no such key. + * + * @param key + * A key string. + * @param defaultValue + * The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + @SuppressWarnings("rawtypes") + private void populateMap(Object bean) { + Class klass = bean.getClass(); + +// If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass ? klass.getMethods() : klass + .getDeclaredMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + if (Modifier.isPublic(method.getModifiers())) { + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + if ("getClass".equals(name) + || "getDeclaringClass".equals(name)) { + key = ""; + } else { + key = name.substring(3); + } + } else if (name.startsWith("is")) { + key = name.substring(2); + } + if (key.length() > 0 + && Character.isUpperCase(key.charAt(0)) + && method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + + key.substring(1); + } + + Object result = method.invoke(bean, (Object[]) null); + if (result != null) { + this.map.put(key, wrap(result)); + } + } + } + } catch (Exception ignore) { + } + } + } + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A boolean which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * + * @param key + * A key string. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Collection value) throws JSONException { + this.put(key, new JSONArray(value)); + return this; + } + + /** + * Put a key/double pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A double which is the value. + * @return this. + * @throws JSONException + * If the key is null or if the number is invalid. + */ + public JSONObject put(String key, double value) throws JSONException { + this.put(key, new Double(value)); + return this; + } + + /** + * Put a key/int pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * An int which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + this.put(key, new Integer(value)); + return this; + } + + /** + * Put a key/long pair in the JSONObject. + * + * @param key + * A key string. + * @param value + * A long which is the value. + * @return this. + * @throws JSONException + * If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + this.put(key, new Long(value)); + return this; + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * + * @param key + * A key string. + * @param value + * A Map value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Map value) throws JSONException { + this.put(key, new JSONObject(value)); + return this; + } + + /** + * Put a key/value pair in the JSONObject. If the value is null, then the + * key will be removed from the JSONObject if it is present. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is non-finite number or if the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new NullPointerException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null, and only if there is not already a member with that + * name. + * + * @param key string + * @param value object + * @return this. + * @throws JSONException + * if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + this.put(key, value); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null. + * + * @param key + * A key string. + * @param value + * An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + this.put(key, value); + } + return this; + } + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within = '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } + } + } + w.write('"'); + return w; + } + + /** + * Remove a name and its value, if present. + * + * @param key + * The name to be removed. + * @return The value that was associated with the name, or null if there was + * no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Determine if two JSONObjects are similar. + * They must contain the same set of names which must be associated with + * similar values. + * + * @param other The other JSONObject + * @return true if they are equal + */ + public boolean similar(Object other) { + try { + if (!(other instanceof JSONObject)) { + return false; + } + Set set = this.keySet(); + if (!set.equals(((JSONObject)other).keySet())) { + return false; + } + Iterator iterator = set.iterator(); + while (iterator.hasNext()) { + String name = iterator.next(); + Object valueThis = this.get(name); + Object valueOther = ((JSONObject)other).get(name); + if (valueThis instanceof JSONObject) { + if (!((JSONObject)valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray)valueThis).similar(valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } catch (Throwable exception) { + return false; + } + } + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. + * + * @param string + * A String. + * @return A simple JSON value. + */ + public static Object stringToValue(String string) { + Double d; + if (string.equals("")) { + return string; + } + if (string.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (string.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (string.equalsIgnoreCase("null")) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. If a number cannot be + * produced, then the value will just be a string. + */ + + char b = string.charAt(0); + if ((b >= '0' && b <= '9') || b == '-') { + try { + if (string.indexOf('.') > -1 || string.indexOf('e') > -1 + || string.indexOf('E') > -1) { + d = Double.valueOf(string); + if (!d.isInfinite() && !d.isNaN()) { + return d; + } + } else { + Long myLong = new Long(string); + if (string.equals(myLong.toString())) { + if (myLong == myLong.intValue()) { + return myLong.intValue(); + } else { + return myLong; + } + } + } + } catch (Exception ignore) { + } + } + return string; + } + + /** + * Throw an exception if the object is a NaN or infinite number. + * + * @param o + * The object to test. + * @throws JSONException + * If o is a non-finite number. + */ + public static void testValidity(Object o) throws JSONException { + if (o != null) { + if (o instanceof Double) { + if (((Double) o).isInfinite() || ((Double) o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } else if (o instanceof Float) { + if (((Float) o).isInfinite() || ((Float) o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } + } + } + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * + * @param names + * A JSONArray containing a list of key strings. This determines + * the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException + * If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is + * added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + */ + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the object contains an invalid number. + */ + public String toString(int indentFactor) throws JSONException { + StringWriter w = new StringWriter(); + synchronized (w.getBuffer()) { + return this.write(w, indentFactor, 0).toString(); + } + } + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. + * If the object does not contain a toJSONString method (which is the most + * common case), then a text will be produced by other means. If the value + * is an array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be + * called. Otherwise, the value's toString method will be called, and the + * result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + @SuppressWarnings("unchecked") + public static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + Object object; + try { + object = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (object instanceof String) { + return (String) object; + } + throw new JSONException("Bad value from toJSONString: " + object); + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean || value instanceof JSONObject + || value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + return new JSONObject((Map)value).toString(); + } + if (value instanceof Collection) { + return new JSONArray((Collection) value).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + return quote(value.toString()); + } + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If it is + * a map, wrap it in a JSONObject. If it is a standard property (Double, + * String, et al) then it is already wrapped. Otherwise, if it comes from + * one of the java packages, turn it into a string. And if it doesn't, try + * to wrap it in a JSONObject. If the wrapping fails, then null is returned. + * + * @param object + * The object to wrap + * @return The wrapped value + */ + @SuppressWarnings("unchecked") + public static Object wrap(Object object) { + try { + if (object == null) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray + || NULL.equals(object) || object instanceof JSONString + || object instanceof Byte || object instanceof Character + || object instanceof Short || object instanceof Integer + || object instanceof Long || object instanceof Boolean + || object instanceof Float || object instanceof Double + || object instanceof String) { + return object; + } + + if (object instanceof Collection) { + return new JSONArray((Collection) object); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + return new JSONObject((Map) object); + } + Package objectPackage = object.getClass().getPackage(); + String objectPackageName = objectPackage != null ? objectPackage + .getName() : ""; + if (objectPackageName.startsWith("java.") + || objectPackageName.startsWith("javax.") + || object.getClass().getClassLoader() == null) { + return object.toString(); + } + return new JSONObject(object); + } catch (Exception exception) { + return null; + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + @SuppressWarnings("unchecked") + static final Writer writeValue(Writer writer, Object value, + int indentFactor, int indent) throws JSONException, IOException { + if (value == null || value.equals(null)) { + writer.write("null"); + } else if (value instanceof JSONObject) { + ((JSONObject) value).write(writer, indentFactor, indent); + } else if (value instanceof JSONArray) { + ((JSONArray) value).write(writer, indentFactor, indent); + } else if (value instanceof Map) { + new JSONObject((Map) value).write(writer, indentFactor, indent); + } else if (value instanceof Collection) { + new JSONArray((Collection) value).write(writer, indentFactor, + indent); + } else if (value.getClass().isArray()) { + new JSONArray(value).write(writer, indentFactor, indent); + } else if (value instanceof Number) { + writer.write(numberToString((Number) value)); + } else if (value instanceof Boolean) { + writer.write(value.toString()); + } else if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + writer.write(o != null ? o.toString() : quote(value.toString())); + } else { + quote(value.toString(), writer); + } + return writer; + } + + static final void indent(Writer writer, int indent) throws IOException { + for (int i = 0; i < indent; i += 1) { + writer.write(' '); + } + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) + throws JSONException { + try { + boolean commanate = false; + final int length = this.length(); + Iterator keys = this.keys(); + writer.write('{'); + + if (length == 1) { + Object key = keys.next(); + writer.write(quote(key.toString())); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + writeValue(writer, this.map.get(key), indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + while (keys.hasNext()) { + Object key = keys.next(); + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, newindent); + writer.write(quote(key.toString())); + writer.write(':'); + if (indentFactor > 0) { + writer.write(' '); + } + writeValue(writer, this.map.get(key), indentFactor, newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + indent(writer, indent); + } + writer.write('}'); + return writer; + } catch (IOException exception) { + throw new JSONException(exception); + } + } +} diff --git a/cloudinary-core/src/main/java/org/json/JSONString.java b/cloudinary-core/src/main/java/org/json/JSONString.java new file mode 100644 index 00000000..1f2d77dd --- /dev/null +++ b/cloudinary-core/src/main/java/org/json/JSONString.java @@ -0,0 +1,18 @@ +package org.json; +/** + * The JSONString interface allows a toJSONString() + * method so that a class can change the behavior of + * JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The + * toJSONString method will be used instead of the default behavior + * of using the Object's toString() method and quoting the result. + */ +public interface JSONString { + /** + * The toJSONString method allows a class to produce its own JSON + * serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); +} diff --git a/cloudinary-core/src/main/java/org/json/JSONTokener.java b/cloudinary-core/src/main/java/org/json/JSONTokener.java new file mode 100644 index 00000000..32548ed9 --- /dev/null +++ b/cloudinary-core/src/main/java/org/json/JSONTokener.java @@ -0,0 +1,446 @@ +package org.json; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONTokener { + + private long character; + private boolean eof; + private long index; + private long line; + private char previous; + private Reader reader; + private boolean usePrevious; + + + /** + * Construct a JSONTokener from a Reader. + * + * @param reader A reader. + */ + public JSONTokener(Reader reader) { + this.reader = reader.markSupported() + ? reader + : new BufferedReader(reader); + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.line = 1; + } + + + /** + * Construct a JSONTokener from an InputStream. + * @param inputStream The source. + */ + public JSONTokener(InputStream inputStream) throws JSONException { + this(new InputStreamReader(inputStream)); + } + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this(new StringReader(s)); + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + */ + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.index -= 1; + this.character -= 1; + this.usePrevious = true; + this.eof = false; + } + + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + public boolean end() { + return this.eof && !this.usePrevious; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + */ + public boolean more() throws JSONException { + this.next(); + if (this.end()) { + return false; + } + this.back(); + return true; + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + */ + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + + if (c <= 0) { // End of stream + this.eof = true; + c = 0; + } + } + this.index += 1; + if (this.previous == '\r') { + this.line += 1; + this.character = c == '\n' ? 0 : 1; + } else if (c == '\n') { + this.line += 1; + this.character = 0; + } else { + this.character += 1; + } + this.previous = (char) c; + return this.previous; + } + + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = this.next(); + if (n != c) { + throw this.syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + + char[] chars = new char[n]; + int pos = 0; + + while (pos < n) { + chars[pos] = this.next(); + if (this.end()) { + throw this.syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } + + + /** + * Get the next char in the string, skipping whitespace. + * @throws JSONException + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = this.next(); + if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); + case '\\': + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char)Integer.parseInt(this.next(4), 16)); + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param delimiter A delimiter character. + * @return A string. + */ + public String nextTo(char delimiter) throws JSONException { + StringBuilder sb = new StringBuilder(); + for (;;) { + char c = this.next(); + if (c == delimiter || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimiter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + */ + public String nextTo(String delimiters) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + for (;;) { + c = this.next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = this.nextClean(); + String string; + + switch (c) { + case '"': + case '\'': + return this.nextString(c); + case '{': + this.back(); + return new JSONObject(this); + case '[': + this.back(); + return new JSONArray(this); + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuilder sb = new StringBuilder(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = this.next(); + } + this.back(); + + string = sb.toString().trim(); + if ("".equals(string)) { + throw this.syntaxError("Missing value"); + } + return JSONObject.stringToValue(string); + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + */ + public char skipTo(char to) throws JSONException { + char c; + try { + long startIndex = this.index; + long startCharacter = this.character; + long startLine = this.line; + this.reader.mark(1000000); + do { + c = this.next(); + if (c == 0) { + this.reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return c; + } + } while (c != to); + } catch (IOException exception) { + throw new JSONException(exception); + } + this.back(); + return c; + } + + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + this.toString()); + } + + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + public String toString() { + return " at " + this.index + " [character " + this.character + " line " + + this.line + "]"; + } +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java index fa4b49db..35d76eff 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java @@ -1,11 +1,11 @@ package com.cloudinary.test; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assume.assumeNotNull; import java.io.IOException; @@ -20,17 +20,20 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.cloudinary.Api; -import com.cloudinary.Api.ApiResponse; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.Transformation; +import com.cloudinary.api.ApiBase; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.BadRequest; +import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.utils.ObjectUtils; @SuppressWarnings({"rawtypes", "unchecked"}) public class ApiTest { private Cloudinary cloudinary; - private Api api; + private ApiBase api; private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); @BeforeClass @@ -40,28 +43,28 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); return; } - Api api = cloudinary.api(); + ApiBase api = cloudinary.api(); try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), Cloudinary.emptyMap()); + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); } catch (Exception e) { } try { - api.deleteTransformation("api_test_transformation", Cloudinary.emptyMap()); + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); } catch (Exception e) { } try { - api.deleteTransformation("api_test_transformation2", Cloudinary.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); } catch (Exception e) { } try { - api.deleteTransformation("api_test_transformation3", Cloudinary.emptyMap()); + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); } catch (Exception e) { } - try{api.deleteUploadPreset("api_test_upload_preset", Cloudinary.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset2", Cloudinary.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset3", Cloudinary.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset4", Cloudinary.emptyMap());}catch (Exception e) {} - Map options = Cloudinary.asMap( + try{api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap());}catch (Exception e) {} + Map options = ObjectUtils.asMap( "public_id", "api_test", "tags", new String[]{"api_test_tag", uniqueTag}, "context", "key=value", @@ -90,14 +93,14 @@ public Map findByAttr(List elements, String attr, Object value) { @Test public void test01ResourceTypes() throws Exception { // should allow listing resource_types - Map result = api.resourceTypes(Cloudinary.emptyMap()); + Map result = api.resourceTypes(ObjectUtils.emptyMap()); assertContains("image", (Collection) result.get("resource_types")); } @Test public void test02Resources() throws Exception { // should allow listing resources - Map result = api.resources(Cloudinary.emptyMap()); + Map result = api.resources(ObjectUtils.emptyMap()); Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); assertNotNull(resource); assertEquals(resource.get("type"), "upload"); @@ -125,7 +128,7 @@ public void test03ResourcesCursor() throws Exception { @Test public void test04ResourcesByType() throws Exception { // should allow listing resources by type - Map result = api.resources(Cloudinary.asMap("type", "upload")); + Map result = api.resources(ObjectUtils.asMap("type", "upload")); Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); assertNotNull(resource); } @@ -133,11 +136,11 @@ public void test04ResourcesByType() throws Exception { @Test public void test05ResourcesByPrefix() throws Exception { // should allow listing resources by prefix - Map result = api.resources(Cloudinary.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); List resources = (List) result.get("resources"); assertNotNull(findByAttr(resources, "public_id", "api_test")); assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", Cloudinary.asMap("custom", Cloudinary.asMap("key", "value")))); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); boolean found = false; for (Map r: resources) { org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); @@ -149,9 +152,9 @@ public void test05ResourcesByPrefix() throws Exception { @Test public void testResourcesListingDirection() throws Exception { // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, Cloudinary.asMap("type", "upload", "direction", "asc")); + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, Cloudinary.asMap("type", "upload", "direction", -1)); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); List resourcesDesc = (List) result.get("resources"); Collections.reverse(resources); assertEquals(resources, resourcesDesc); @@ -163,20 +166,21 @@ public void testResourcesListingStartAt() throws Exception { Thread.sleep(2000L); java.util.Date startAt = new java.util.Date(); Thread.sleep(2000L); - Map response = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.emptyMap()); - List resources = (List) api.resources(Cloudinary.asMap("type", "upload", "start_at", startAt, "direction", "asc")).get("resources"); + Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); assertEquals(response.get("public_id"), resources.get(0).get("public_id")); } @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), Cloudinary.asMap("type", "upload", "tags", true, "context", true)); + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); List resources = (List) result.get("resources"); assertEquals(2, resources.size()); assertNotNull(findByAttr(resources, "public_id", "api_test")); assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", Cloudinary.asMap("custom", Cloudinary.asMap("key", "value")))); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); boolean found = false; for (Map r: resources) { org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); @@ -188,10 +192,10 @@ public void testResourcesByPublicIds() throws Exception { @Test public void test06ResourcesTag() throws Exception { // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", Cloudinary.asMap("tags", true, "context", true)); + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", Cloudinary.asMap("custom", Cloudinary.asMap("key", "value"))); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); assertNotNull(resource); List resources = (List) result.get("resources"); boolean found = false; @@ -205,7 +209,7 @@ public void test06ResourcesTag() throws Exception { @Test public void test07ResourceMetadata() throws Exception { // should allow get resource metadata - Map resource = api.resource("api_test", Cloudinary.emptyMap()); + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); assertNotNull(resource); assertEquals(resource.get("public_id"), "api_test"); assertEquals(resource.get("bytes"), 3381L); @@ -215,56 +219,56 @@ public void test07ResourceMetadata() throws Exception { @Test public void test08DeleteDerived() throws Exception { // should allow deleting derived resource - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap( + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap( "public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")) )); - Map resource = api.resource("api_test3", Cloudinary.emptyMap()); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); assertNotNull(resource); List derived = (List) resource.get("derived"); assertEquals(derived.size(), 1); String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), Cloudinary.emptyMap()); - resource = api.resource("api_test3", Cloudinary.emptyMap()); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); assertNotNull(resource); derived = (List) resource.get("derived"); assertEquals(derived.size(), 0); } - @Test(expected = Api.NotFound.class) + @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), Cloudinary.emptyMap()); - api.resource("api_test3", Cloudinary.emptyMap()); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); } - @Test(expected = Api.NotFound.class) + @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", Cloudinary.emptyMap()); - api.resource("api_test_by_prefix", Cloudinary.emptyMap()); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); } - @Test(expected = Api.NotFound.class) + @Test(expected = NotFound.class) public void test09aDeleteResourcesByTags() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", Cloudinary.emptyMap()); - api.resource("api_test4", Cloudinary.emptyMap()); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); } @Test public void test10Tags() throws Exception { // should allow listing tags - Map result = api.tags(Cloudinary.emptyMap()); + Map result = api.tags(ObjectUtils.emptyMap()); List tags = (List) result.get("tags"); assertContains("api_test_tag", tags); } @@ -272,10 +276,10 @@ public void test10Tags() throws Exception { @Test public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix - Map result = api.tags(Cloudinary.asMap("prefix", "api_test")); + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); List tags = (List) result.get("tags"); assertContains("api_test_tag", tags); - result = api.tags(Cloudinary.asMap("prefix", "api_test_no_such_tag")); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); tags = (List) result.get("tags"); assertEquals(0, tags.size()); } @@ -283,7 +287,7 @@ public void test11TagsPrefix() throws Exception { @Test public void test12Transformations() throws Exception { // should allow listing transformations - Map result = api.transformations(Cloudinary.emptyMap()); + Map result = api.transformations(ObjectUtils.emptyMap()); Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); assertNotNull(transformation); @@ -293,7 +297,7 @@ public void test12Transformations() throws Exception { @Test public void test13TransformationMetadata() throws Exception { // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", Cloudinary.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100) .generate()); @@ -302,12 +306,12 @@ public void test13TransformationMetadata() throws Exception { @Test public void test14TransformationUpdate() throws Exception { // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", Cloudinary.asMap("allowed_for_strict", true), Cloudinary.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", Cloudinary.emptyMap()); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", Cloudinary.asMap("allowed_for_strict", false), Cloudinary.emptyMap()); - transformation = api.transformation("c_scale,w_100", Cloudinary.emptyMap()); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), false); } @@ -315,8 +319,8 @@ public void test14TransformationUpdate() throws Exception { @Test public void test15TransformationCreate() throws Exception { // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), Cloudinary.emptyMap()); - Map transformation = api.transformation("api_test_transformation", Cloudinary.emptyMap()); + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), true); assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102) @@ -327,9 +331,9 @@ public void test15TransformationCreate() throws Exception { @Test public void test15aTransformationUnsafeUpdate() throws Exception { // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), Cloudinary.emptyMap()); - api.updateTransformation("api_test_transformation3", Cloudinary.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), Cloudinary.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", Cloudinary.emptyMap()); + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103) .generate()); @@ -339,43 +343,43 @@ public void test15aTransformationUnsafeUpdate() throws Exception { @Test public void test16aTransformationDelete() throws Exception { // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), Cloudinary.emptyMap()); - api.transformation("api_test_transformation2", Cloudinary.emptyMap()); - api.deleteTransformation("api_test_transformation2", Cloudinary.emptyMap()); + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); } - @Test(expected = Api.NotFound.class) + @Test(expected = NotFound.class) public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", Cloudinary.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); } @Test public void test17aTransformationDeleteImplicit() throws Exception { // should allow deleting implicit transformation - api.transformation("c_scale,w_100", Cloudinary.emptyMap()); - api.deleteTransformation("c_scale,w_100", Cloudinary.emptyMap()); + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); } /** * @throws Exception * @expectedException \Cloudinary\Api\NotFound */ - @Test(expected = Api.NotFound.class) + @Test(expected = NotFound.class) public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", Cloudinary.emptyMap()); + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); } @Test public void test18Usage() throws Exception { // should support usage API call - Map result = api.usage(Cloudinary.emptyMap()); + Map result = api.usage(ObjectUtils.emptyMap()); assertNotNull(result.get("last_updated")); } @Test public void test19Ping() throws Exception { // should support ping API call - Map result = api.ping(Cloudinary.emptyMap()); + Map result = api.ping(ObjectUtils.emptyMap()); assertEquals(result.get("status"), "ok"); } @@ -383,11 +387,11 @@ public void test19Ping() throws Exception { // Add @Test if you really want to test it - This test deletes derived resources! public void testDeleteAllResources() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); assertEquals(1, ((org.json.simple.JSONArray) result.get("derived")).size()); - api.deleteAllResources(Cloudinary.asMap("keep_original", true)); - result = api.resource("api_test5", Cloudinary.emptyMap()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); //assertEquals(0, ((org.json.simple.JSONArray) result.get("derived")).size()); } @@ -395,8 +399,8 @@ public void testDeleteAllResources() throws Exception { @Test public void testManualModeration() throws Exception { // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("moderation","manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), Cloudinary.asMap("moderation_status", "approved")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation","manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); } @@ -405,11 +409,11 @@ public void testOcrUpdate() { // should support requesting ocr info try { Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", Cloudinary.emptyMap()); + "src/test/resources/logo.png", ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), - Cloudinary.asMap("ocr", "illegal")); + ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.BadRequest); + assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } @@ -419,11 +423,11 @@ public void testRawConvertUpdate() { // should support requesting raw conversion try { Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", Cloudinary.emptyMap()); + "src/test/resources/logo.png", ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), - Cloudinary.asMap("raw_convert", "illegal")); + ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.BadRequest); + assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } @@ -433,11 +437,11 @@ public void testCategorizationUpdate() { // should support requesting categorization try { Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", Cloudinary.emptyMap()); + "src/test/resources/logo.png", ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), - Cloudinary.asMap("categorization", "illegal")); + ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.BadRequest); + assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } @@ -447,11 +451,11 @@ public void testDetectionUpdate() { // should support requesting detection try { Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", Cloudinary.emptyMap()); + "src/test/resources/logo.png", ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), - Cloudinary.asMap("detection", "illegal")); + ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.BadRequest); + assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } @@ -461,11 +465,11 @@ public void testSimilaritySearchUpdate() { // should support requesting similarity search try { Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", Cloudinary.emptyMap()); + "src/test/resources/logo.png", ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), - Cloudinary.asMap("similarity_search", "illegal")); + ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.BadRequest); + assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } @@ -474,9 +478,9 @@ public void testSimilaritySearchUpdate() { public void testUpdateCustomCoordinates() throws IOException, Exception { //should update custom coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), Cloudinary.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), Cloudinary.asMap("coordinates", true)); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); long[] expected = new long[]{121L,31L,110L,151L}; Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++){ @@ -487,8 +491,8 @@ public void testUpdateCustomCoordinates() throws IOException, Exception { @Test public void testApiLimits() throws Exception { // should support reporting the current API limits found in the response header - ApiResponse result1 = api.transformations(Cloudinary.emptyMap()); - ApiResponse result2 = api.transformations(Cloudinary.emptyMap()); + ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); + ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); assertNotNull(result1.apiRateLimit()); assertNotNull(result2.apiRateLimit()); assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); @@ -501,37 +505,37 @@ public void testApiLimits() throws Exception { @Test public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets - api.createUploadPreset(Cloudinary.asMap("name", + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(Cloudinary.asMap("name", + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(Cloudinary.asMap("name", + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); org.json.simple.JSONArray presets = (org.json.simple.JSONArray) api - .uploadPresets(Cloudinary.emptyMap()).get("presets"); + .uploadPresets(ObjectUtils.emptyMap()).get("presets"); assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); - api.deleteUploadPreset("api_test_upload_preset", Cloudinary.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset2", Cloudinary.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset3", Cloudinary.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); } @Test public void testGetUploadPreset() throws Exception { // should allow getting a single upload_preset String[] tags = { "a", "b", "c" }; - Map context = Cloudinary.asMap("a", "b", "c", "d"); + Map context = ObjectUtils.asMap("a", "b", "c", "d"); Transformation transformation = new Transformation(); transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(Cloudinary.asMap("unsigned", true, + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", context)); String name = result.get("name").toString(); - Map preset = api.uploadPreset(name, Cloudinary.emptyMap()); + Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); assertEquals(preset.get("name"), name); assertEquals(Boolean.TRUE, preset.get("unsigned")); Map settings = (Map) preset.get("settings"); @@ -550,13 +554,13 @@ public void testGetUploadPreset() throws Exception { @Test public void testDeleteUploadPreset() throws Exception { // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(Cloudinary.asMap("name", + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); - api.uploadPreset("api_test_upload_preset4", Cloudinary.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset4", Cloudinary.emptyMap()); + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); boolean error = false; try { - api.uploadPreset("api_test_upload_preset4", Cloudinary.emptyMap()); + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); } catch (Exception e) { error = true; } @@ -567,19 +571,19 @@ public void testDeleteUploadPreset() throws Exception { public void testUpdateUploadPreset() throws Exception { // should allow updating upload_presets String name = api - .createUploadPreset(Cloudinary.asMap("folder", "folder")) + .createUploadPreset(ObjectUtils.asMap("folder", "folder")) .get("name").toString(); - Map preset = api.uploadPreset(name, Cloudinary.emptyMap()); + Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); Map settings = (Map) preset.get("settings"); - settings.putAll(Cloudinary.asMap("colors", true, "unsigned", true, + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); - preset = api.uploadPreset(name, Cloudinary.emptyMap()); + preset = api.uploadPreset(name, ObjectUtils.emptyMap()); assertEquals(name, preset.get("name")); assertEquals(Boolean.TRUE, preset.get("unsigned")); assertEquals(settings, preset.get("settings")); - api.deleteUploadPreset(name, Cloudinary.emptyMap()); + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); } @Test @@ -587,23 +591,23 @@ public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value Map result1 = cloudinary.uploader().upload( "src/test/resources/logo.png", - Cloudinary.asMap("moderation", "manual")); + ObjectUtils.asMap("moderation", "manual")); Map result2 = cloudinary.uploader().upload( "src/test/resources/logo.png", - Cloudinary.asMap("moderation", "manual")); + ObjectUtils.asMap("moderation", "manual")); Map result3 = cloudinary.uploader().upload( "src/test/resources/logo.png", - Cloudinary.asMap("moderation", "manual")); + ObjectUtils.asMap("moderation", "manual")); api.update((String) result1.get("public_id"), - Cloudinary.asMap("moderation_status", "approved")); + ObjectUtils.asMap("moderation_status", "approved")); api.update((String) result2.get("public_id"), - Cloudinary.asMap("moderation_status", "rejected")); + ObjectUtils.asMap("moderation_status", "rejected")); Map approved = api.resourcesByModeration("manual", "approved", - Cloudinary.asMap("max_results", 1000)); + ObjectUtils.asMap("max_results", 1000)); Map rejected = api.resourcesByModeration("manual", "rejected", - Cloudinary.asMap("max_results", 1000)); + ObjectUtils.asMap("max_results", 1000)); Map pending = api.resourcesByModeration("manual", "pending", - Cloudinary.asMap("max_results", 1000)); + ObjectUtils.asMap("max_results", 1000)); assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); assertNull(findByAttr((List) approved.get("resources"), @@ -630,12 +634,12 @@ public void testListByModerationUpdate() throws Exception { // @Test public void testFolderApi() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("public_id", "test_folder2/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); cloudinary.uploader().upload("src/test/resources/logo.png", - Cloudinary.asMap("public_id", "test_folder1/test_subfolder1/item")); + ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); cloudinary.uploader().upload("src/test/resources/logo.png", - Cloudinary.asMap("public_id", "test_folder1/test_subfolder2/item")); + ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); Map result = api.rootFolders(null); assertEquals("test_folder1", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("name")); assertEquals("test_folder2", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("name")); @@ -647,9 +651,9 @@ public void testFolderApi() throws Exception { try { api.subFolders("test_folder", null); } catch (Exception e) { - assertTrue(e instanceof com.cloudinary.Api.NotFound); + assertTrue(e instanceof NotFound); } - api.deleteResourcesByPrefix("test_folder", Cloudinary.emptyMap()); + api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); } private void assertContains(Object object, Collection list) { 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 29439b03..c22b3a08 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -15,6 +15,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; public class CloudinaryTest { @@ -167,6 +168,7 @@ public void testType() { assertEquals("http://res.cloudinary.com/test123/image/facebook/test", result); } + @SuppressWarnings("deprecation") @Test public void testResourceType() { // should use resource_type from options @@ -342,28 +344,29 @@ public void testOpacity() { assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); } + @SuppressWarnings("unchecked") @Test public void testImageTag() { Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", Cloudinary.asMap("alt", "my image")); + String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); assertEquals( "my image", result); transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(transformation).imageTag("test", Cloudinary.asMap("alt", "my image")); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); assertEquals( "my image", result); - result = cloudinary.url().transformation(transformation).imageTag("test", Cloudinary.asMap("alt", "my image", "class", "extra")); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); assertEquals( "my image", result); transformation = new Transformation().width("auto").crop("crop"); - result = cloudinary.url().transformation(transformation).imageTag("test", Cloudinary.asMap("alt", "my image", "responsive_placeholder", "blank")); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); assertEquals( "my image", result); - result = cloudinary.url().transformation(transformation).imageTag("test", Cloudinary.asMap("alt", "my image", "responsive_placeholder", "other.gif")); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); assertEquals( "my image", result); @@ -392,9 +395,10 @@ public void testShorten() { assertEquals("http://res.cloudinary.com/test123/iu/test", result); } + @SuppressWarnings("unchecked") @Test public void testPrivateDownload() throws Exception { - String url = cloudinary.privateDownload("img", "jpg", Cloudinary.emptyMap()); + String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); URI uri = new URI(url); Map parameters = getUrlParameters(uri); assertEquals("img", parameters.get("public_id")); @@ -403,9 +407,10 @@ public void testPrivateDownload() throws Exception { assertEquals("/v1_1/test123/image/download", uri.getPath()); } + @SuppressWarnings("unchecked") @Test public void testZipDownload() throws Exception { - String url = cloudinary.zipDownload("ttag", Cloudinary.emptyMap()); + String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); URI uri = new URI(url); Map parameters = getUrlParameters(uri); assertEquals("ttag", parameters.get("tag")); @@ -425,7 +430,7 @@ public void testSpriteCss() { @Test public void testEscapePublicId() { // should escape public_ids - Map tests = Cloudinary.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); for (Map.Entry entry : tests.entrySet()) { String result = cloudinary.url().generate(entry.getKey()); assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); @@ -456,7 +461,7 @@ public void testResponsiveWidth() { String result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(Cloudinary.asMap("width", "auto", "crop", "pad")); + Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); @@ -465,8 +470,8 @@ public void testResponsiveWidth() { } public void testUtils() { - assertEquals(Cloudinary.asBoolean(true, null), true); - assertEquals(Cloudinary.asBoolean(false, null), false); + assertEquals(ObjectUtils.asBoolean(true, null), true); + assertEquals(ObjectUtils.asBoolean(false, null), false); } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java index 17d92c8e..73618ccc 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java @@ -11,8 +11,6 @@ import java.util.List; import java.util.Map; -import java.awt.Rectangle; - import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -20,6 +18,8 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; @SuppressWarnings({"rawtypes", "unchecked"}) public class UploaderTest { @@ -42,71 +42,71 @@ public void setUp() { @Test public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("colors", true)); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); assertEquals(result.get("width"), 241L); assertEquals(result.get("height"), 51L); assertNotNull(result.get("colors")); assertNotNull(result.get("predominant")); Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", Cloudinary.asString(result.get("version"))); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); assertEquals(result.get("signature"), expected_signature); } @Test public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", Cloudinary.emptyMap()); + Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); assertEquals(result.get("width"), 241L); assertEquals(result.get("height"), 51L); Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", Cloudinary.asString(result.get("version"))); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); assertEquals(result.get("signature"), expected_signature); } @Test public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", Cloudinary.emptyMap()); + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); assertEquals(result.get("width"), 16L); assertEquals(result.get("height"), 16L); Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", Cloudinary.asString(result.get("version"))); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); assertEquals(result.get("signature"), expected_signature); } @Test public void testRename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.emptyMap()); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", Cloudinary.emptyMap()); - assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", Cloudinary.emptyMap())); + cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); - Map result2 = cloudinary.uploader().upload("src/test/resources/favicon.ico", Cloudinary.emptyMap()); + Map result2 = cloudinary.uploader().upload("src/test/resources/favicon.ico", ObjectUtils.emptyMap()); boolean error_found=false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", Cloudinary.emptyMap()); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); } catch(Exception e) { error_found=true; } assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", Cloudinary.asMap("overwrite", Boolean.TRUE)); - assertEquals(cloudinary.api().resource(result.get("public_id")+"2", Cloudinary.emptyMap()).get("format"), "ico"); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); + assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); } @Test public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("use_filename", true)); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("use_filename", true, "unique_filename", false)); + result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); assertEquals((String) result.get("public_id"), "logo"); } @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("cloudinary", Cloudinary.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); + Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.getStringConfig("cloud_name"); @@ -115,83 +115,83 @@ public void testExplicit() throws IOException { @Test public void testEager() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } @Test public void testHeaders() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("headers", Cloudinary.asMap("Link", "1"))); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", new String[]{"Link: 1"})); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } @Test public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", Cloudinary.emptyMap()); + Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); assertTrue(((Long) result.get("width")) > 1); assertTrue(((Long) result.get("height")) > 1); } @Test public void testImageUploadTag() { - String tag = cloudinary.uploader().imageUploadTag("test-field", Cloudinary.asMap("callback", "http://localhost/cloudinary_cors.html"), Cloudinary.asMap("htmlattr", "htmlvalue")); + String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); assertTrue(tag.contains("type='file'")); assertTrue(tag.contains("data-cloudinary-field='test-field'")); assertTrue(tag.contains("class='cloudinary-fileupload'")); assertTrue(tag.contains("htmlattr='htmlvalue'")); - tag = cloudinary.uploader().imageUploadTag("test-field", Cloudinary.asMap("callback", "http://localhost/cloudinary_cors.html"), Cloudinary.asMap("class", "myclass")); + tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } @Test public void testSprite() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); assertEquals(2, ((Map) result.get("image_infos")).size()); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", Cloudinary.asMap("transformation", "w_100")); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); assertTrue(((String) result.get("css_url")).contains("w_100")); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", Cloudinary.asMap("transformation", new Transformation().width(100), "format", "jpg")); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); } @Test public void testMulti() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", Cloudinary.emptyMap()); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); assertTrue(((String) result.get("url")).endsWith(".gif")); - result = cloudinary.uploader().multi("multi_test_tag", Cloudinary.asMap("transformation", "w_100")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); assertTrue(((String) result.get("url")).contains("w_100")); - result = cloudinary.uploader().multi("multi_test_tag", Cloudinary.asMap("transformation", new Transformation().width(111), "format", "pdf")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); assertTrue(((String) result.get("url")).contains("w_111")); assertTrue(((String) result.get("url")).endsWith(".pdf")); } @Test public void testTags() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.emptyMap()); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); String public_id = (String)result.get("public_id"); - Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.emptyMap()); + Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); String public_id2 = (String)result2.get("public_id"); - cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, Cloudinary.emptyMap()); - cloudinary.uploader().addTag("tag2", new String[]{public_id}, Cloudinary.emptyMap()); - List tags = (List) cloudinary.api().resource(public_id, Cloudinary.emptyMap()).get("tags"); - assertEquals(tags, Cloudinary.asArray(new String[]{"tag1", "tag2"})); - tags = (List) cloudinary.api().resource(public_id2, Cloudinary.emptyMap()).get("tags"); - assertEquals(tags, Cloudinary.asArray(new String[]{"tag1"})); - cloudinary.uploader().removeTag("tag1", new String[]{public_id}, Cloudinary.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, Cloudinary.emptyMap()).get("tags"); - assertEquals(tags, Cloudinary.asArray(new String[]{"tag2"})); - cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, Cloudinary.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, Cloudinary.emptyMap()).get("tags"); - assertEquals(tags, Cloudinary.asArray(new String[]{"tag3"})); + cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); + cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); + List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); + tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); + cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); + cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); } @Test public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("allowed_formats", formats)); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); assertEquals(result.get("format"), "png"); } @@ -201,7 +201,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { boolean errorFound = false; String[] formats = {"jpg"}; try{ - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("allowed_formats", formats)); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); } catch(Exception e) { errorFound=true; } @@ -212,7 +212,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("allowed_formats", formats, "format", "jpg")); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); assertEquals("jpg", result.get("format")); } @@ -224,7 +224,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120,30,109,150); coordinates.addRect(rect1); coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("face_coordinates", coordinates, "faces", true)); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); org.json.simple.JSONArray resultFaces = (org.json.simple.JSONArray) result.get("faces"); assertEquals(2, resultFaces.size()); @@ -245,8 +245,8 @@ public void testFaceCoordinates() throws Exception { Coordinates differentCoordinates = new Coordinates(); Rectangle rect3 = new Rectangle(122,32,111,152); differentCoordinates.addRect(rect3); - cloudinary.uploader().explicit((String) result.get("public_id"), Cloudinary.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); - Map info = cloudinary.api().resource((String) result.get("public_id"), Cloudinary.asMap("faces", true)); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); resultFaces = (org.json.simple.JSONArray) info.get("faces"); assertEquals(1, resultFaces.size()); @@ -263,8 +263,8 @@ public void testFaceCoordinates() throws Exception { public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), Cloudinary.asMap("coordinates", true)); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); long[] expected = new long[]{121L,31L,110L,151L}; Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++){ @@ -272,8 +272,8 @@ public void testCustomCoordinates() throws Exception { } coordinates = new Coordinates(new int[]{122,32,110,152}); - cloudinary.uploader().explicit((String) uploadResult.get("public_id"), Cloudinary.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); - result = cloudinary.api().resource(uploadResult.get("public_id").toString(), Cloudinary.asMap("coordinates", true)); + cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); + result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); expected = new long[]{122L,32L,110L,152L}; actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++){ @@ -284,20 +284,20 @@ public void testCustomCoordinates() throws Exception { @Test public void testContext() throws Exception { //should allow sending context - Map context = Cloudinary.asMap("caption", "some caption", "alt", "alternative"); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("context", context)); - Map info = cloudinary.api().resource((String) result.get("public_id"), Cloudinary.asMap("context", true)); - assertEquals(Cloudinary.asMap("custom", context), info.get("context")); - Map differentContext = Cloudinary.asMap("caption", "different caption", "alt2", "alternative alternative"); - cloudinary.uploader().explicit((String) result.get("public_id"), Cloudinary.asMap("type", "upload", "context", differentContext)); - info = cloudinary.api().resource((String) result.get("public_id"), Cloudinary.asMap("context", true)); - assertEquals(Cloudinary.asMap("custom", differentContext), info.get("context")); + Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("context", context)); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); + Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); + info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); } @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("moderation", "manual")); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); } @@ -307,7 +307,7 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { //should support requesting raw conversion try { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("raw_convert", "illegal")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("raw_convert", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -317,7 +317,7 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { //should support requesting categorization try { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("categorization", "illegal")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("categorization", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -327,7 +327,7 @@ public void testCategorizationRequest() { public void testDetectionRequest() { //should support requesting detection try { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("detection", "illegal")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("detection", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -337,7 +337,7 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { //should support requesting auto tagging try { - cloudinary.uploader().upload("src/test/resources/logo.png", Cloudinary.asMap("auto_tagging", 0.5f)); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("auto_tagging", 0.5f)); } catch(Exception e) { assertTrue(e.getMessage().matches("^Must use(.*)")); } @@ -346,7 +346,7 @@ public void testAutoTaggingRequest() { @Test public void testUploadLargeRawFiles() throws Exception { // support uploading large raw files - Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", Cloudinary.emptyMap()); + Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); assertEquals(new java.io.File("src/test/resources/docx.docx").length(), response.get("bytes")); assertEquals(Boolean.TRUE, response.get("done")); } @@ -354,10 +354,10 @@ public void testUploadLargeRawFiles() throws Exception { @Test public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets - Map preset = cloudinary.api().createUploadPreset(Cloudinary.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload("src/test/resources/logo.png", preset.get("name").toString(), Cloudinary.emptyMap()); + Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); + Map result = cloudinary.uploader().unsignedUpload("src/test/resources/logo.png", preset.get("name").toString(), ObjectUtils.emptyMap()); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); - cloudinary.api().deleteUploadPreset(preset.get("name").toString(), Cloudinary.emptyMap()); + cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } } From a3401c6c1c2df45a07741602de5ab618315bb8b2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 20 Aug 2014 17:28:40 +0300 Subject: [PATCH 003/592] maven build , project dependency core -> http42 -> taglib --- .../java/com/cloudinary/test/ApiTest.java | 994 +++++++++--------- .../com/cloudinary/test/CloudinaryTest.java | 11 +- .../com/cloudinary/test/UploaderTest.java | 94 +- .../com/cloudinary/test/stubs/ApiStub.java | 21 + .../cloudinary/test/stubs/CloudinaryStub.java | 41 + .../test/stubs/URLBuilderWrapperStub.java | 23 + .../cloudinary/test/stubs/UploaderStub.java | 22 + cloudinary-http42/pom.xml | 38 + .../src/main/java/com/cloudinary/Api.java | 0 .../main/java/com/cloudinary/Cloudinary.java | 0 .../com/cloudinary/URLBuilderWrapper.java | 0 .../main/java/com/cloudinary/Uploader.java | 0 .../java/com/cloudinary/test/ApiTest.java | 663 ++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 490 +++++++++ .../com/cloudinary/test/UploaderTest.java | 363 +++++++ .../src/test/resources/docx.docx | Bin 0 -> 20453 bytes .../src/test/resources/favicon.ico | Bin 0 -> 1150 bytes cloudinary-http42/src/test/resources/logo.png | Bin 0 -> 3381 bytes cloudinary-taglib/pom.xml | 2 +- .../taglib/CloudinaryUploadTag.java | 12 +- pom.xml | 58 +- 21 files changed, 2242 insertions(+), 590 deletions(-) create mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java create mode 100644 cloudinary-http42/pom.xml rename {cloudinary-core => cloudinary-http42}/src/main/java/com/cloudinary/Api.java (100%) rename {cloudinary-core => cloudinary-http42}/src/main/java/com/cloudinary/Cloudinary.java (100%) rename {cloudinary-core => cloudinary-http42}/src/main/java/com/cloudinary/URLBuilderWrapper.java (100%) rename {cloudinary-core => cloudinary-http42}/src/main/java/com/cloudinary/Uploader.java (100%) create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java create mode 100644 cloudinary-http42/src/test/resources/docx.docx create mode 100644 cloudinary-http42/src/test/resources/favicon.ico create mode 100644 cloudinary-http42/src/test/resources/logo.png diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java index 35d76eff..614d93ba 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java @@ -1,6 +1,5 @@ package com.cloudinary.test; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -16,481 +15,493 @@ import java.util.List; import java.util.Map; +import org.json.JSONArray; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; -import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.Transformation; import com.cloudinary.api.ApiBase; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.test.stubs.CloudinaryStub; import com.cloudinary.utils.ObjectUtils; -@SuppressWarnings({"rawtypes", "unchecked"}) +@SuppressWarnings({ "rawtypes", "unchecked" }) public class ApiTest { - private Cloudinary cloudinary; - private ApiBase api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); - - @BeforeClass - public static void setUpClass() throws IOException { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.getStringConfig("api_secret") == null) { - System.err.println("Please setup environment for Upload test to run"); - return; - } - ApiBase api = cloudinary.api(); - try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try{api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap());}catch (Exception e) {} - Map options = ObjectUtils.asMap( - "public_id", "api_test", - "tags", new String[]{"api_test_tag", uniqueTag}, - "context", "key=value", - "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - options.put("public_id", "api_test1"); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - } - - @Before - public void setUp() { - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.getStringConfig("api_secret")); - this.api = cloudinary.api(); - } - - public Map findByAttr(List elements, String attr, Object value) { - for (Map element : elements) { - if (value.equals(element.get(attr))) { - return element; - } - } - return null; - } - - @Test - public void test01ResourceTypes() throws Exception { - // should allow listing resource_types - Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); - } - - @Test - public void test02Resources() throws Exception { - // should allow listing resources - Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } - - @Test - public void test03ResourcesCursor() throws Exception { - // should allow listing resources with cursor - Map options = new HashMap(); - options.put("max_results", 1); - Map result = api.resources(options); - List resources = (List) result.get("resources"); - assertNotNull(resources); - assertEquals(1, resources.size()); - assertNotNull(result.get("next_cursor")); - - options.put("next_cursor", result.get("next_cursor")); - Map result2 = api.resources(options); - List resources2 = (List) result2.get("resources"); - assertNotNull(resources2); - assertEquals(resources2.size(), 1); - assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void test04ResourcesByType() throws Exception { - // should allow listing resources by type - Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - } - - @Test - public void test05ResourcesByPrefix() throws Exception { - // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void testResourcesListingDirection() throws Exception { - // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); - List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); - List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); - } - - @Test - public void testResourcesListingStartAt() throws Exception { - // should allow listing resources by start date - make sure your clock is set correctly!!! - Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); + private CloudinaryStub cloudinary; + private ApiBase api; + private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + + @BeforeClass + public static void setUpClass() throws IOException { + CloudinaryStub cloudinary = new CloudinaryStub(); + if (cloudinary.getStringConfig("api_secret") == null) { + System.err.println("Please setup environment for Upload test to run"); + return; + } + ApiBase api = cloudinary.api(); + try { + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", + Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + options.put("public_id", "api_test1"); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + } + + @Before + public void setUp() { + this.cloudinary = new CloudinaryStub(); + assumeNotNull(cloudinary.getStringConfig("api_secret")); + this.api = cloudinary.api(); + } + + public Map findByAttr(List elements, String attr, Object value) { + for (Map element : elements) { + if (value.equals(element.get(attr))) { + return element; + } + } + return null; + } + + @Ignore + public void test01ResourceTypes() throws Exception { + // should allow listing resource_types + Map result = api.resourceTypes(ObjectUtils.emptyMap()); + assertContains("image", (Collection) result.get("resource_types")); + } + + @Ignore + public void test02Resources() throws Exception { + // should allow listing resources + Map result = api.resources(ObjectUtils.emptyMap()); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Ignore + public void test03ResourcesCursor() throws Exception { + // should allow listing resources with cursor + Map options = new HashMap(); + options.put("max_results", 1); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + assertEquals(1, resources.size()); + assertNotNull(result.get("next_cursor")); + + options.put("next_cursor", result.get("next_cursor")); + Map result2 = api.resources(options); + List resources2 = (List) result2.get("resources"); + assertNotNull(resources2); + assertEquals(resources2.size(), 1); + assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); + } + + @Ignore + public void test04ResourcesByType() throws Exception { + // should allow listing resources by type + Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + } + + @Ignore + public void test05ResourcesByPrefix() throws Exception { + // should allow listing resources by prefix + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + JSONArray tags = (JSONArray) r.get("tags"); + + found = found || contains(tags, "api_test_tag"); + } + assertTrue(found); + } + + @Ignore + public void testResourcesListingDirection() throws Exception { + // should allow listing resources in both directions + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + List resources = (List) result.get("resources"); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + List resourcesDesc = (List) result.get("resources"); + Collections.reverse(resources); + assertEquals(resources, resourcesDesc); + } + + @Ignore + public void testResourcesListingStartAt() throws Exception { + // should allow listing resources by start date - make sure your clock + // is set correctly!!! + Thread.sleep(2000L); + java.util.Date startAt = new java.util.Date(); Thread.sleep(2000L); - Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); - List resources = (List) listResources.get("resources"); - assertEquals(response.get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void testResourcesByPublicIds() throws Exception { - // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test06ResourcesTag() throws Exception { - // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); - assertNotNull(resource); - List resources = (List) result.get("resources"); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test07ResourceMetadata() throws Exception { - // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); - assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); - assertEquals(resource.get("bytes"), 3381L); - assertEquals(((List) resource.get("derived")).size(), 1); - } - - @Test - public void test08DeleteDerived() throws Exception { - // should allow deleting derived resource - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap( - "public_id", "api_test3", - "eager", Collections.singletonList(new Transformation().width(101).crop("scale")) - )); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - List derived = (List) resource.get("derived"); - assertEquals(derived.size(), 1); - String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - derived = (List) resource.get("derived"); - assertEquals(derived.size(), 0); - } - - @Test(expected = NotFound.class) - public void test09DeleteResources() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByPrefix() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByTags() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); - } - - @Test - public void test10Tags() throws Exception { - // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - } - - @Test - public void test11TagsPrefix() throws Exception { - // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); - tags = (List) result.get("tags"); - assertEquals(0, tags.size()); - } - - @Test - public void test12Transformations() throws Exception { - // should allow listing transformations - Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); - - assertNotNull(transformation); - assertTrue((Boolean) transformation.get("used")); - } - - @Test - public void test13TransformationMetadata() throws Exception { - // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100) - .generate()); - } - - @Test - public void test14TransformationUpdate() throws Exception { - // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), false); - } - - @Test - public void test15TransformationCreate() throws Exception { - // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102) - .generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test15aTransformationUnsafeUpdate() throws Exception { - // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103) - .generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test16aTransformationDelete() throws Exception { - // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test - public void test17aTransformationDeleteImplicit() throws Exception { - // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ - @Test(expected = NotFound.class) - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - @Test - public void test18Usage() throws Exception { - // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); - assertNotNull(result.get("last_updated")); - } - - @Test - public void test19Ping() throws Exception { - // should support ping API call - Map result = api.ping(ObjectUtils.emptyMap()); - assertEquals(result.get("status"), "ok"); - } - - // This test must be last because it deletes (potentially) all dependent transformations which some tests rely on. - // Add @Test if you really want to test it - This test deletes derived resources! - public void testDeleteAllResources() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((org.json.simple.JSONArray) result.get("derived")).size()); - api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); - //assertEquals(0, ((org.json.simple.JSONArray) result.get("derived")).size()); - } - - - @Test - public void testManualModeration() throws Exception { - // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation","manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); - } - + Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); + assertEquals(response.get("public_id"), resources.get(0).get("public_id")); + } + + @Ignore + public void testResourcesByPublicIds() throws Exception { + // should allow listing resources by public ids + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + JSONArray tags = (JSONArray) r.get("tags"); + found = found || contains(tags, "api_test_tag"); + } + assertTrue(found); + } + + @Ignore + public void test06ResourcesTag() throws Exception { + // should allow listing resources by tag + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); + assertNotNull(resource); + List resources = (List) result.get("resources"); + boolean found = false; + for (Map r : resources) { + JSONArray tags = (JSONArray) r.get("tags"); + found = found || contains(tags, "api_test_tag"); + } + assertTrue(found); + } + + @Ignore + public void test07ResourceMetadata() throws Exception { + // should allow get resource metadata + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + assertNotNull(resource); + assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("bytes"), 3381L); + assertEquals(((List) resource.get("derived")).size(), 1); + } + + @Ignore + public void test08DeleteDerived() throws Exception { + // should allow deleting derived resource + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = (List) resource.get("derived"); + assertEquals(derived.size(), 1); + String derived_resource_id = (String) derived.get(0).get("id"); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = (List) resource.get("derived"); + assertEquals(derived.size(), 0); + } + +// @Test(expected = NotFound.class) + @Ignore + public void test09DeleteResources() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); + } + +// @Test(expected = NotFound.class) + @Ignore + public void test09aDeleteResourcesByPrefix() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + } + +// @Test(expected = NotFound.class) + @Ignore + public void test09aDeleteResourcesByTags() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); + } + + @Ignore + public void test10Tags() throws Exception { + // should allow listing tags + Map result = api.tags(ObjectUtils.emptyMap()); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + } + + @Ignore + public void test11TagsPrefix() throws Exception { + // should allow listing tag by prefix + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); + tags = (List) result.get("tags"); + assertEquals(0, tags.size()); + } + + @Ignore + public void test12Transformations() throws Exception { + // should allow listing transformations + Map result = api.transformations(ObjectUtils.emptyMap()); + Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + + assertNotNull(transformation); + assertTrue((Boolean) transformation.get("used")); + } + + @Ignore + public void test13TransformationMetadata() throws Exception { + // should allow getting transformation metadata + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); + } + + @Ignore + public void test14TransformationUpdate() throws Exception { + // should allow updating transformation allowed_for_strict + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), false); + } + + @Ignore + public void test15TransformationCreate() throws Exception { + // should allow creating named transformation + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); + assertEquals(transformation.get("used"), false); + } + +// @Test + @Ignore + public void test15aTransformationUnsafeUpdate() throws Exception { + // should allow unsafe update of named transformation + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), + ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); + assertEquals(transformation.get("used"), false); + } + @Test + public void test16aTransformationDelete() throws Exception { + // should allow deleting named transformation + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + +// @Test(expected = NotFound.class) + @Ignore + public void test16bTransformationDelete() throws Exception { + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test + public void test17aTransformationDeleteImplicit() throws Exception { + // should allow deleting implicit transformation + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + /** + * @throws Exception + * @expectedException \Cloudinary\Api\NotFound + */ +// @Test(expected = NotFound.class) + @Ignore + public void test17bTransformationDeleteImplicit() throws Exception { + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + +// @Test + @Ignore + public void test18Usage() throws Exception { + // should support usage API call + Map result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + @Ignore + public void test19Ping() throws Exception { + // should support ping API call + Map result = api.ping(ObjectUtils.emptyMap()); + assertEquals(result.get("status"), "ok"); + } + + // This test must be last because it deletes (potentially) all dependent + // transformations which some tests rely on. + // Add @Test if you really want to test it - This test deletes derived + // resources! + public void testDeleteAllResources() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + assertEquals(1, ((JSONArray) result.get("derived")).length()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); + // assertEquals(0, ((JSONArray) result.get("derived")).size()); + } + +// @Test + @Ignore + public void testManualModeration() throws Exception { + // should support setting manual moderation status + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); + } + +// @Test + @Ignore public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("ocr", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - - @Test + + @Ignore public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("raw_convert", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - - @Test + + @Ignore public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("categorization", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - - @Test + + @Ignore public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("detection", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - - @Test + + @Ignore public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("similarity_search", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - - @Test + + @Ignore public void testUpdateCustomCoordinates() throws IOException, Exception { - //should update custom coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - long[] expected = new long[]{121L,31L,110L,151L}; - Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } + // should update custom coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + long[] expected = new long[] { 121L, 31L, 110L, 151L }; + JSONArray actual = ((JSONArray) ((JSONArray) ((Map) result.get("coordinates")).get("custom")).get(0)); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual.get(i)); + } } - - @Test + + @Ignore public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response header + // should support reporting the current API limits found in the response + // header ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); assertNotNull(result1.apiRateLimit()); @@ -501,61 +512,52 @@ public void testApiLimits() throws Exception { assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); } - - @Test + + @Ignore public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset3", "folder", "folder3")); - org.json.simple.JSONArray presets = (org.json.simple.JSONArray) api - .uploadPresets(ObjectUtils.emptyMap()).get("presets"); - assertEquals(((Map) presets.get(0)).get("name"), - "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), - "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), - "api_test_upload_preset"); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); + JSONArray presets = (JSONArray) api.uploadPresets(ObjectUtils.emptyMap()).get("presets"); + assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); + assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); + assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); } - @Test + @Ignore public void testGetUploadPreset() throws Exception { // should allow getting a single upload_preset String[] tags = { "a", "b", "c" }; Map context = ObjectUtils.asMap("a", "b", "c", "d"); Transformation transformation = new Transformation(); transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, - "folder", "folder", "transformation", transformation, "tags", - tags, "context", context)); + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", + context)); 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"); - Map outTransformation = (Map) ((org.json.simple.JSONArray) settings - .get("transformation")).get(0); + Map outTransformation = (Map) ((JSONArray) settings.get("transformation")).get(0); assertEquals(outTransformation.get("width"), 100L); assertEquals(outTransformation.get("crop"), "scale"); - Object[] outTags = ((org.json.simple.JSONArray) settings.get("tags")) - .toArray(); - assertArrayEquals(tags, outTags); + JSONArray outTags = ((JSONArray) settings.get("tags")); + for (int i = 0; i < tags.length; i++){ + assertEquals(tags[i], outTags.get(i)); + } Map outContext = (Map) settings.get("context"); assertEquals(context, outContext); } - @Test + @Ignore public void testDeleteUploadPreset() throws Exception { // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset4", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); boolean error = false; @@ -567,16 +569,13 @@ public void testDeleteUploadPreset() throws Exception { assertTrue(error); } - @Test + @Ignore public void testUpdateUploadPreset() throws Exception { // should allow updating upload_presets - String name = api - .createUploadPreset(ObjectUtils.asMap("folder", "folder")) - .get("name").toString(); + 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)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); preset = api.uploadPreset(name, ObjectUtils.emptyMap()); @@ -585,49 +584,29 @@ public void testUpdateUploadPreset() throws Exception { assertEquals(settings, preset.get("settings")); api.deleteUploadPreset(name, ObjectUtils.emptyMap()); } - - @Test + + @Ignore public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - api.update((String) result1.get("public_id"), - ObjectUtils.asMap("moderation_status", "approved")); - api.update((String) result2.get("public_id"), - ObjectUtils.asMap("moderation_status", "rejected")); - Map approved = api.resourcesByModeration("manual", "approved", - ObjectUtils.asMap("max_results", 1000)); - Map rejected = api.resourcesByModeration("manual", "rejected", - ObjectUtils.asMap("max_results", 1000)); - Map pending = api.resourcesByModeration("manual", "pending", - ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result2.get("public_id"))); - } - + Map result1 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); + Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); + Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); + Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); + assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); + } + // For this test to work, "Auto-create folders" should be enabled in the // Upload Settings. // Uncomment @Test if you really want to test it. @@ -636,18 +615,14 @@ public void testFolderApi() throws Exception { // should allow deleting all resources cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("name")); + assertEquals("test_folder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("name")); result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", - ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", - ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("path")); + assertEquals("test_folder1/test_subfolder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("path")); try { api.subFolders("test_folder", null); } catch (Exception e) { @@ -655,8 +630,17 @@ public void testFolderApi() throws Exception { } api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); } - + private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } + assertTrue(list.contains(object)); + } + + private boolean contains(JSONArray source, String findMe) { + for (int i = 0; i < source.length(); i++) { + if (findMe.equals(source.get(i))) + return true; + } + return 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 c22b3a08..6b2d8a47 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -11,19 +11,20 @@ import java.util.Map; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; -import com.cloudinary.Cloudinary; import com.cloudinary.Transformation; +import com.cloudinary.test.stubs.CloudinaryStub; import com.cloudinary.utils.ObjectUtils; public class CloudinaryTest { - private Cloudinary cloudinary; + private CloudinaryStub cloudinary; @Before public void setUp() { - this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); + this.cloudinary = new CloudinaryStub("cloudinary://a:b@test123"); } @Test @@ -396,7 +397,7 @@ public void testShorten() { } @SuppressWarnings("unchecked") - @Test + @Ignore public void testPrivateDownload() throws Exception { String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); URI uri = new URI(url); @@ -408,7 +409,7 @@ public void testPrivateDownload() throws Exception { } @SuppressWarnings("unchecked") - @Test + @Ignore public void testZipDownload() throws Exception { String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); URI uri = new URI(url); diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java index 73618ccc..5f143c1d 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java @@ -11,24 +11,26 @@ import java.util.List; import java.util.Map; +import org.json.JSONArray; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; -import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.Transformation; +import com.cloudinary.test.stubs.CloudinaryStub; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; @SuppressWarnings({"rawtypes", "unchecked"}) public class UploaderTest { - private Cloudinary cloudinary; + private CloudinaryStub cloudinary; @BeforeClass public static void setUpClass() { - Cloudinary cloudinary = new Cloudinary(); + CloudinaryStub cloudinary = new CloudinaryStub(); if (cloudinary.getStringConfig("api_secret") == null) { System.err.println("Please setup environment for Upload test to run"); } @@ -36,11 +38,11 @@ public static void setUpClass() { @Before public void setUp() { - this.cloudinary = new Cloudinary(); + this.cloudinary = new CloudinaryStub(); assumeNotNull(cloudinary.getStringConfig("api_secret")); } - @Test + @Ignore public void testUpload() throws IOException { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); assertEquals(result.get("width"), 241L); @@ -54,7 +56,7 @@ public void testUpload() throws IOException { assertEquals(result.get("signature"), expected_signature); } - @Test + @Ignore public void testUploadUrl() throws IOException { Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); assertEquals(result.get("width"), 241L); @@ -66,7 +68,7 @@ public void testUploadUrl() throws IOException { assertEquals(result.get("signature"), expected_signature); } - @Test + @Ignore public void testUploadDataUri() throws IOException { Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); assertEquals(result.get("width"), 16L); @@ -78,7 +80,7 @@ public void testUploadDataUri() throws IOException { assertEquals(result.get("signature"), expected_signature); } - @Test + @Ignore public void testRename() throws Exception { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); @@ -97,14 +99,14 @@ public void testRename() throws Exception { assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); } - @Test + @Ignore public void testUniqueFilename() throws Exception { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); assertEquals((String) result.get("public_id"), "logo"); } - @Test + @Ignore public void testExplicit() throws IOException { Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); @@ -124,7 +126,7 @@ public void testHeaders() throws IOException { cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } - @Test + @Ignore public void testText() throws IOException { Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); assertTrue(((Long) result.get("width")) > 1); @@ -142,7 +144,7 @@ public void testImageUploadTag() { assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } - @Test + @Ignore public void testSprite() throws IOException { cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); @@ -154,7 +156,7 @@ public void testSprite() throws IOException { assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); } - @Test + @Ignore public void testMulti() throws IOException { cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); @@ -167,7 +169,7 @@ public void testMulti() throws IOException { assertTrue(((String) result.get("url")).endsWith(".pdf")); } - @Test + @Ignore public void testTags() throws Exception { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); String public_id = (String)result.get("public_id"); @@ -187,7 +189,7 @@ public void testTags() throws Exception { assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); } - @Test + @Ignore public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; @@ -195,7 +197,7 @@ public void testAllowedFormats() throws Exception { assertEquals(result.get("format"), "png"); } - @Test + @Ignore public void testAllowedFormatsWithIllegalFormat() throws Exception { //should prevent non whitelisted formats from being uploaded if allowed_formats is specified boolean errorFound = false; @@ -208,7 +210,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { assertTrue(errorFound); } - @Test + @Ignore public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; @@ -216,7 +218,7 @@ public void testAllowedFormatsWithFormat() throws Exception { assertEquals("jpg", result.get("format")); } - @Test + @Ignore public void testFaceCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates(); @@ -225,22 +227,22 @@ public void testFaceCoordinates() throws Exception { coordinates.addRect(rect1); coordinates.addRect(rect2); Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); - org.json.simple.JSONArray resultFaces = (org.json.simple.JSONArray) result.get("faces"); - assertEquals(2, resultFaces.size()); + JSONArray resultFaces = (JSONArray) result.get("faces"); + assertEquals(2, resultFaces.length()); - Object[] resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); + JSONArray resultCoordinates = ((JSONArray) resultFaces.get(0)); - assertEquals((long)rect1.x, resultCoordinates[0]); - assertEquals((long)rect1.y, resultCoordinates[1]); - assertEquals((long)rect1.width, resultCoordinates[2]); - assertEquals((long)rect1.height, resultCoordinates[3]); + assertEquals((long)rect1.x, resultCoordinates.get(0)); + assertEquals((long)rect1.y, resultCoordinates.get(1)); + assertEquals((long)rect1.width, resultCoordinates.get(2)); + assertEquals((long)rect1.height, resultCoordinates.get(3)); - resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(1)).toArray(); + resultCoordinates = ((JSONArray) resultFaces.get(1)); - assertEquals((long)rect2.x, resultCoordinates[0]); - assertEquals((long)rect2.y, resultCoordinates[1]); - assertEquals((long)rect2.width, resultCoordinates[2]); - assertEquals((long)rect2.height, resultCoordinates[3]); + assertEquals((long)rect2.x, resultCoordinates.get(0)); + assertEquals((long)rect2.y, resultCoordinates.get(1)); + assertEquals((long)rect2.width, resultCoordinates.get(2)); + assertEquals((long)rect2.height, resultCoordinates.get(3)); Coordinates differentCoordinates = new Coordinates(); Rectangle rect3 = new Rectangle(122,32,111,152); @@ -248,40 +250,40 @@ public void testFaceCoordinates() throws Exception { cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); - resultFaces = (org.json.simple.JSONArray) info.get("faces"); - assertEquals(1, resultFaces.size()); - resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); + resultFaces = (JSONArray) info.get("faces"); + assertEquals(1, resultFaces.length()); + resultCoordinates = ((JSONArray) resultFaces.get(0)); - assertEquals((long)rect3.x, resultCoordinates[0]); - assertEquals((long)rect3.y, resultCoordinates[1]); - assertEquals((long)rect3.width, resultCoordinates[2]); - assertEquals((long)rect3.height, resultCoordinates[3]); + assertEquals((long)rect3.x, resultCoordinates.get(0)); + assertEquals((long)rect3.y, resultCoordinates.get(1)); + assertEquals((long)rect3.width, resultCoordinates.get(2)); + assertEquals((long)rect3.height, resultCoordinates.get(3)); } - @Test + @Ignore public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); long[] expected = new long[]{121L,31L,110L,151L}; - Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + JSONArray actual = ((JSONArray)((JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)); for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); + assertEquals(expected[i], actual.get(i)); } coordinates = new Coordinates(new int[]{122,32,110,152}); cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); expected = new long[]{122L,32L,110L,152L}; - actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + actual = ((JSONArray)((JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)); for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); + assertEquals(expected[i], actual.get(i)); } } - @Test + @Ignore public void testContext() throws Exception { //should allow sending context Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); @@ -294,7 +296,7 @@ public void testContext() throws Exception { assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); } - @Test + @Ignore public void testModerationRequest() throws Exception { //should support requesting manual moderation Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); @@ -343,7 +345,7 @@ public void testAutoTaggingRequest() { } } - @Test + @Ignore public void testUploadLargeRawFiles() throws Exception { // support uploading large raw files Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); @@ -351,7 +353,7 @@ public void testUploadLargeRawFiles() throws Exception { assertEquals(Boolean.TRUE, response.get("done")); } - @Test + @Ignore public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java new file mode 100644 index 00000000..6fcc9481 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java @@ -0,0 +1,21 @@ +package com.cloudinary.test.stubs; + +import java.util.Map; + +import com.cloudinary.CloudinaryBase; +import com.cloudinary.api.ApiBase; +import com.cloudinary.api.ApiResponse; + +public class ApiStub extends ApiBase { + public ApiStub(CloudinaryBase cloudinary) { + super(cloudinary); + // TODO Auto-generated constructor stub + } + + @Override + protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java new file mode 100644 index 00000000..f16fef9e --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java @@ -0,0 +1,41 @@ +package com.cloudinary.test.stubs; + +import java.net.URISyntaxException; +import java.util.Map; + +import com.cloudinary.CloudinaryBase; +import com.cloudinary.UploaderBase; +import com.cloudinary.api.ApiBase; +import com.cloudinary.utils.AbstractURLBuilderWrapper; + +public class CloudinaryStub extends CloudinaryBase { + public CloudinaryStub() { + super(); + } + + public CloudinaryStub(String string) { + super(string); + } + + @SuppressWarnings("rawtypes") + public CloudinaryStub(Map config) { + super(config); + } + + @Override + public AbstractURLBuilderWrapper urlBuilder(String source) throws URISyntaxException { + return new URLBuilderWrapperStub(source); + } + + @Override + public UploaderBase uploader() { + return new UploaderStub(this).withConnectionManager(connectionManager); + } + + @Override + public ApiBase api() { + return new ApiStub(this).withConnectionManager(connectionManager); + } + + +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java new file mode 100644 index 00000000..4e3428c9 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java @@ -0,0 +1,23 @@ +package com.cloudinary.test.stubs; + +import java.net.URISyntaxException; + +import com.cloudinary.utils.AbstractURLBuilderWrapper; + +public class URLBuilderWrapperStub extends AbstractURLBuilderWrapper { + + + public URLBuilderWrapperStub(String source) throws URISyntaxException { + super(source); + } + + @Override + public void addParam(String key, Object value) { + } + + @Override + public String url() { + return ""; + } + +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java new file mode 100644 index 00000000..99059838 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java @@ -0,0 +1,22 @@ +package com.cloudinary.test.stubs; + +import java.io.IOException; +import java.util.Map; + +import com.cloudinary.CloudinaryBase; +import com.cloudinary.UploaderBase; + +public class UploaderStub extends UploaderBase { + + public UploaderStub(CloudinaryBase cloudinary) { + super(cloudinary); + // TODO Auto-generated constructor stub + } + + @Override + public Map callApi(String action, Map params, Map options, Object file) throws IOException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml new file mode 100644 index 00000000..ab7dffa6 --- /dev/null +++ b/cloudinary-http42/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + + com.cloudinary + cloudinary-parent + 1.0.15-SNAPSHOT + + + cloudinary-http42 + jar + + Cloudinary Apache HTTP 4.2 Library + + + com.cloudinary + cloudinary + ${project.version} + + + org.apache.commons + commons-lang3 + 3.1 + + + org.apache.httpcomponents + httpmime + 4.2.1 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + + + diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-http42/src/main/java/com/cloudinary/Api.java similarity index 100% rename from cloudinary-core/src/main/java/com/cloudinary/Api.java rename to cloudinary-http42/src/main/java/com/cloudinary/Api.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java similarity index 100% rename from cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java rename to cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java b/cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java similarity index 100% rename from cloudinary-core/src/main/java/com/cloudinary/URLBuilderWrapper.java rename to cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-http42/src/main/java/com/cloudinary/Uploader.java similarity index 100% rename from cloudinary-core/src/main/java/com/cloudinary/Uploader.java rename to cloudinary-http42/src/main/java/com/cloudinary/Uploader.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java new file mode 100644 index 00000000..ce1391a7 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -0,0 +1,663 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.api.ApiBase; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.BadRequest; +import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.utils.ObjectUtils; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class ApiTest { + + private Cloudinary cloudinary; + private ApiBase api; + private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.getStringConfig("api_secret") == null) { + System.err.println("Please setup environment for Upload test to run"); + return; + } + ApiBase api = cloudinary.api(); + try { + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try{api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap());}catch (Exception e) {} + try{api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap());}catch (Exception e) {} + Map options = ObjectUtils.asMap( + "public_id", "api_test", + "tags", new String[]{"api_test_tag", uniqueTag}, + "context", "key=value", + "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + options.put("public_id", "api_test1"); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + } + + @Before + public void setUp() { + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.getStringConfig("api_secret")); + this.api = cloudinary.api(); + } + + public Map findByAttr(List elements, String attr, Object value) { + for (Map element : elements) { + if (value.equals(element.get(attr))) { + return element; + } + } + return null; + } + + @Test + public void test01ResourceTypes() throws Exception { + // should allow listing resource_types + Map result = api.resourceTypes(ObjectUtils.emptyMap()); + assertContains("image", (Collection) result.get("resource_types")); + } + + @Test + public void test02Resources() throws Exception { + // should allow listing resources + Map result = api.resources(ObjectUtils.emptyMap()); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Test + public void test03ResourcesCursor() throws Exception { + // should allow listing resources with cursor + Map options = new HashMap(); + options.put("max_results", 1); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + assertEquals(1, resources.size()); + assertNotNull(result.get("next_cursor")); + + options.put("next_cursor", result.get("next_cursor")); + Map result2 = api.resources(options); + List resources2 = (List) result2.get("resources"); + assertNotNull(resources2); + assertEquals(resources2.size(), 1); + assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void test04ResourcesByType() throws Exception { + // should allow listing resources by type + Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + } + + @Test + public void test05ResourcesByPrefix() throws Exception { + // should allow listing resources by prefix + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r: resources) { + org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void testResourcesListingDirection() throws Exception { + // should allow listing resources in both directions + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + List resources = (List) result.get("resources"); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + List resourcesDesc = (List) result.get("resources"); + Collections.reverse(resources); + assertEquals(resources, resourcesDesc); + } + + @Ignore + public void testResourcesListingStartAt() throws Exception { + // should allow listing resources by start date - make sure your clock is set correctly!!! + Thread.sleep(2000L); + java.util.Date startAt = new java.util.Date(); + Thread.sleep(2000L); + Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); + assertEquals(response.get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void testResourcesByPublicIds() throws Exception { + // should allow listing resources by public ids + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r: resources) { + org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test06ResourcesTag() throws Exception { + // should allow listing resources by tag + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); + assertNotNull(resource); + List resources = (List) result.get("resources"); + boolean found = false; + for (Map r: resources) { + org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test07ResourceMetadata() throws Exception { + // should allow get resource metadata + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + assertNotNull(resource); + assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("bytes"), 3381L); + assertEquals(((List) resource.get("derived")).size(), 1); + } + + @Test + public void test08DeleteDerived() throws Exception { + // should allow deleting derived resource + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap( + "public_id", "api_test3", + "eager", Collections.singletonList(new Transformation().width(101).crop("scale")) + )); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = (List) resource.get("derived"); + assertEquals(derived.size(), 1); + String derived_resource_id = (String) derived.get(0).get("id"); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = (List) resource.get("derived"); + assertEquals(derived.size(), 0); + } + + @Test(expected = NotFound.class) + public void test09DeleteResources() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByPrefix() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByTags() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); + } + + @Test + public void test10Tags() throws Exception { + // should allow listing tags + Map result = api.tags(ObjectUtils.emptyMap()); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + } + + @Test + public void test11TagsPrefix() throws Exception { + // should allow listing tag by prefix + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); + tags = (List) result.get("tags"); + assertEquals(0, tags.size()); + } + + @Test + public void test12Transformations() throws Exception { + // should allow listing transformations + Map result = api.transformations(ObjectUtils.emptyMap()); + Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + + assertNotNull(transformation); + assertTrue((Boolean) transformation.get("used")); + } + + @Test + public void test13TransformationMetadata() throws Exception { + // should allow getting transformation metadata + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100) + .generate()); + } + + @Test + public void test14TransformationUpdate() throws Exception { + // should allow updating transformation allowed_for_strict + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), false); + } + + @Test + public void test15TransformationCreate() throws Exception { + // should allow creating named transformation + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102) + .generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test15aTransformationUnsafeUpdate() throws Exception { + // should allow unsafe update of named transformation + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103) + .generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test16aTransformationDelete() throws Exception { + // should allow deleting named transformation + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test16bTransformationDelete() throws Exception { + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test + public void test17aTransformationDeleteImplicit() throws Exception { + // should allow deleting implicit transformation + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + /** + * @throws Exception + * @expectedException \Cloudinary\Api\NotFound + */ + @Test(expected = NotFound.class) + public void test17bTransformationDeleteImplicit() throws Exception { + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + @Test + public void test18Usage() throws Exception { + // should support usage API call + Map result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + @Test + public void test19Ping() throws Exception { + // should support ping API call + Map result = api.ping(ObjectUtils.emptyMap()); + assertEquals(result.get("status"), "ok"); + } + + // This test must be last because it deletes (potentially) all dependent transformations which some tests rely on. + // Add @Test if you really want to test it - This test deletes derived resources! + public void testDeleteAllResources() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + assertEquals(1, ((org.json.simple.JSONArray) result.get("derived")).size()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); + //assertEquals(0, ((org.json.simple.JSONArray) result.get("derived")).size()); + } + + + @Test + public void testManualModeration() throws Exception { + // should support setting manual moderation status + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation","manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); + } + + @Test + public void testOcrUpdate() { + // should support requesting ocr info + try { + Map uploadResult = cloudinary.uploader().upload( + "src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), + ObjectUtils.asMap("ocr", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testRawConvertUpdate() { + // should support requesting raw conversion + try { + Map uploadResult = cloudinary.uploader().upload( + "src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), + ObjectUtils.asMap("raw_convert", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testCategorizationUpdate() { + // should support requesting categorization + try { + Map uploadResult = cloudinary.uploader().upload( + "src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), + ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testDetectionUpdate() { + // should support requesting detection + try { + Map uploadResult = cloudinary.uploader().upload( + "src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), + ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testSimilaritySearchUpdate() { + // should support requesting similarity search + try { + Map uploadResult = cloudinary.uploader().upload( + "src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), + ObjectUtils.asMap("similarity_search", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testUpdateCustomCoordinates() throws IOException, Exception { + //should update custom coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + long[] expected = new long[]{121L,31L,110L,151L}; + Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++){ + assertEquals(expected[i], actual[i]); + } + } + + @Test + public void testApiLimits() throws Exception { + // should support reporting the current API limits found in the response header + ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); + ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); + assertNotNull(result1.apiRateLimit()); + assertNotNull(result2.apiRateLimit()); + assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); + assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); + assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); + assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); + assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); + } + + @Test + public void testListUploadPresets() throws Exception { + // should allow creating and listing upload_presets + api.createUploadPreset(ObjectUtils.asMap("name", + "api_test_upload_preset", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", + "api_test_upload_preset2", "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", + "api_test_upload_preset3", "folder", "folder3")); + org.json.simple.JSONArray presets = (org.json.simple.JSONArray) api + .uploadPresets(ObjectUtils.emptyMap()).get("presets"); + assertEquals(((Map) presets.get(0)).get("name"), + "api_test_upload_preset3"); + assertEquals(((Map) presets.get(1)).get("name"), + "api_test_upload_preset2"); + assertEquals(((Map) presets.get(2)).get("name"), + "api_test_upload_preset"); + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } + + @Test + public void testGetUploadPreset() throws Exception { + // should allow getting a single upload_preset + String[] tags = { "a", "b", "c" }; + Map context = ObjectUtils.asMap("a", "b", "c", "d"); + Transformation transformation = new Transformation(); + transformation.width(100).crop("scale"); + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, + "folder", "folder", "transformation", transformation, "tags", + tags, "context", context)); + 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"); + Map outTransformation = (Map) ((org.json.simple.JSONArray) settings + .get("transformation")).get(0); + assertEquals(outTransformation.get("width"), 100L); + assertEquals(outTransformation.get("crop"), "scale"); + Object[] outTags = ((org.json.simple.JSONArray) settings.get("tags")) + .toArray(); + assertArrayEquals(tags, outTags); + Map outContext = (Map) settings.get("context"); + assertEquals(context, outContext); + } + + @Test + public void testDeleteUploadPreset() throws Exception { + // should allow deleting upload_presets", :upload_preset => true do + api.createUploadPreset(ObjectUtils.asMap("name", + "api_test_upload_preset4", "folder", "folder")); + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + boolean error = false; + try { + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + error = true; + } + assertTrue(error); + } + + @Test + public void testUpdateUploadPreset() throws Exception { + // should allow updating upload_presets + 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)); + 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, preset.get("settings")); + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); + } + + @Test + public void testListByModerationUpdate() throws Exception { + // "should support listing by moderation kind and value + Map result1 = cloudinary.uploader().upload( + "src/test/resources/logo.png", + ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload( + "src/test/resources/logo.png", + ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload( + "src/test/resources/logo.png", + ObjectUtils.asMap("moderation", "manual")); + api.update((String) result1.get("public_id"), + ObjectUtils.asMap("moderation_status", "approved")); + api.update((String) result2.get("public_id"), + ObjectUtils.asMap("moderation_status", "rejected")); + Map approved = api.resourcesByModeration("manual", "approved", + ObjectUtils.asMap("max_results", 1000)); + Map rejected = api.resourcesByModeration("manual", "rejected", + ObjectUtils.asMap("max_results", 1000)); + Map pending = api.resourcesByModeration("manual", "pending", + ObjectUtils.asMap("max_results", 1000)); + assertNotNull(findByAttr((List) approved.get("resources"), + "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), + "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), + "public_id", (String) result2.get("public_id"))); + assertNotNull(findByAttr((List) rejected.get("resources"), + "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), + "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), + "public_id", (String) result3.get("public_id"))); + assertNotNull(findByAttr((List) pending.get("resources"), + "public_id", (String) result3.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), + "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), + "public_id", (String) result2.get("public_id"))); + } + + // For this test to work, "Auto-create folders" should be enabled in the + // Upload Settings. + // Uncomment @Test if you really want to test it. + // @Test + public void testFolderApi() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + Map result = api.rootFolders(null); + assertEquals("test_folder1", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("name")); + result = api.subFolders("test_folder1", null); + assertEquals("test_folder1/test_subfolder1", + ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", + ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("path")); + try { + api.subFolders("test_folder", null); + } catch (Exception e) { + assertTrue(e instanceof NotFound); + } + api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); + } + + private void assertContains(Object object, Collection list) { + assertTrue(list.contains(object)); + } +} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java new file mode 100644 index 00000000..c22b3a08 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -0,0 +1,490 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; + +public class CloudinaryTest { + + private Cloudinary cloudinary; + + @Before + public void setUp() { + this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); + } + + @Test + public void testCloudName() { + // should use cloud_name from config + String result = cloudinary.url().generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testSecureDistribution() { + // should use default secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistributionOverwrite() { + // should allow overwriting secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); + assertEquals("https://something.else.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistibution() { + // should take secure distribution from config if secure=TRUE + cloudinary.setConfig("secure_distribution", "config.secure.distribution.com"); + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); + } + + @Test + public void testSecureAkamai() { + // should default to akamai if secure is given with private_cdn and no + // secure_distribution + cloudinary.setConfig("secure", true); + cloudinary.setConfig("private_cdn", true); + String result = cloudinary.url().generate("test"); + assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); + } + + @Test + public void testSecureNonAkamai() { + // should not add cloud_name if private_cdn and secure non akamai + // secure_distribution + cloudinary.setConfig("secure", true); + cloudinary.setConfig("private_cdn", true); + cloudinary.setConfig("secure_distribution", "something.cloudfront.net"); + String result = cloudinary.url().generate("test"); + assertEquals("https://something.cloudfront.net/image/upload/test", result); + } + + @Test + public void testHttpPrivateCdn() { + // should not add cloud_name if private_cdn and not secure + cloudinary.setConfig("private_cdn", true); + String result = cloudinary.url().generate("test"); + assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); + } + + @Test + public void testFormat() { + // should use format from options + String result = cloudinary.url().format("jpg").generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); + } + + @Test + public void testCrop() { + Transformation transformation = new Transformation().width(100).height(101); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); + assertEquals("101", transformation.getHtmlHeight().toString()); + assertEquals("100", transformation.getHtmlWidth().toString()); + transformation = new Transformation().width(100).height(101).crop("crop"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); + } + + @Test + public void testVariousOptions() { + // should use x, y, radius, prefix, gravity and quality from options + Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); + } + + @Test + public void testTransformationSimple() { + // should support named transformation + Transformation transformation = new Transformation().named("blip"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); + } + + @Test + public void testTransformationArray() { + // should support array of named transformations + Transformation transformation = new Transformation().named("blip", "blop"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); + } + + @Test + public void testBaseTransformations() { + // should support base transformation + Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); + } + + @Test + public void testBaseTransformationArray() { + // should support array of base transformations + Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop") + .width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); + } + + @Test + public void testNoEmptyTransformation() { + // should not include empty transformations + Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); + } + + @Test + 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); + } + + @SuppressWarnings("deprecation") + @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); + } + + @Test + public void testIgnoreHttp() { + // should ignore http links only if type is not given or is asset + String result = cloudinary.url().generate("http://test"); + assertEquals("http://test", result); + 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); + } + + @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); + } + + @Test + public void testCname() { + // should support external cname + String result = cloudinary.url().cname("hello.com").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"); + assertEquals("http://a2.hello.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testBackground() { + // should support background + Transformation transformation = new Transformation().background("red"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); + transformation = new Transformation().background("#112233"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); + } + + @Test + public void testDefaultImage() { + // should support default_image + Transformation transformation = new Transformation().defaultImage("default"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); + } + + @Test + public void testAngle() { + // should support angle + Transformation transformation = new Transformation().angle(12); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); + transformation = new Transformation().angle("exif", "12"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); + } + + @Test + public void testOverlay() { + // should support overlay + Transformation transformation = new Transformation().overlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); + // should not pass width/height to html if overlay + transformation = new Transformation().overlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); + } + + @Test + public void testUnderlay() { + Transformation transformation = new Transformation().underlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); + // should not pass width/height to html if underlay + transformation = new Transformation().underlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); + } + + @Test + public void testFetchFormat() { + // should support format for fetch urls + String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/logo.png"); + assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/logo.png", result); + } + + @Test + public void testEffect() { + // should support effect + Transformation transformation = new Transformation().effect("sepia"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); + } + + @Test + public void testEffectWithParam() { + // should support effect with param + Transformation transformation = new Transformation().effect("sepia", 10); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); + } + + @Test + public void testDensity() { + // should support density + Transformation transformation = new Transformation().density(150); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); + } + + @Test + public void testPage() { + // should support page + Transformation transformation = new Transformation().page(5); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); + } + + @Test + public void testBorder() { + // should support border + Transformation transformation = new Transformation().border(5, "black"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); + transformation = new Transformation().border(5, "#ffaabbdd"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); + transformation = new Transformation().border("1px_solid_blue"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); + } + + @Test + public void testFlags() { + // should support flags + Transformation transformation = new Transformation().flags("abc"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); + transformation = new Transformation().flags("abc", "def"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); + } + + @Test + public void testOpacity() { + // should support opacity + Transformation transformation = new Transformation().opacity(50); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testImageTag() { + Transformation transformation = new Transformation().width(100).height(101).crop("crop"); + String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals( + "my image", + result); + transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); + assertEquals( + "my image", + result); + transformation = new Transformation().width("auto").crop("crop"); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); + assertEquals( + "my image", + result); + } + + @Test + public void testFolders() { + // should add version if public_id contains / + String result = cloudinary.url().generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); + result = cloudinary.url().version(123).generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); + } + + @Test + public void testFoldersWithVersion() { + // should not add version if public_id contains version already + String result = cloudinary.url().generate("v1234/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); + } + + @Test + public void testShorten() { + // should allow to shorted image/upload urls + String result = cloudinary.url().shorten(true).generate("test"); + assertEquals("http://res.cloudinary.com/test123/iu/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testPrivateDownload() throws Exception { + String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("img", parameters.get("public_id")); + assertEquals("jpg", parameters.get("format")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download", uri.getPath()); + } + + @SuppressWarnings("unchecked") + @Test + public void testZipDownload() throws Exception { + String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("ttag", parameters.get("tag")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); + } + + @Test + public void testSpriteCss() { + String result = cloudinary.url().generateSpriteCss("test"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + result = cloudinary.url().generateSpriteCss("test.css"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testEscapePublicId() { + // should escape public_ids + Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + for (Map.Entry entry : tests.entrySet()) { + String result = cloudinary.url().generate(entry.getKey()); + assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); + } + } + + @Test + public void testSignedUrl() { + // should correctly sign a url + String expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"; + String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) + .generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"; + actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; + actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + } + + @Test + public void testResponsiveWidth() { + // should support responsive width + Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + String result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); + trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(null); + } + + public void testUtils() { + assertEquals(ObjectUtils.asBoolean(true, null), true); + assertEquals(ObjectUtils.asBoolean(false, null), false); + } + + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { + Map params = new HashMap(); + for (String param : uri.getQuery().split("&")) { + String pair[] = param.split("="); + String key = URLDecoder.decode(pair[0], "UTF-8"); + String value = ""; + if (pair.length > 1) { + value = URLDecoder.decode(pair[1], "UTF-8"); + } + params.put(new String(key), new String(value)); + } + return params; + } +} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java new file mode 100644 index 00000000..73618ccc --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -0,0 +1,363 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class UploaderTest { + + private Cloudinary cloudinary; + + @BeforeClass + public static void setUpClass() { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.getStringConfig("api_secret") == null) { + System.err.println("Please setup environment for Upload test to run"); + } + } + + @Before + public void setUp() { + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.getStringConfig("api_secret")); + } + + @Test + public void testUpload() throws IOException { + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); + assertEquals(result.get("width"), 241L); + assertEquals(result.get("height"), 51L); + assertNotNull(result.get("colors")); + assertNotNull(result.get("predominant")); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testUploadUrl() throws IOException { + Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); + assertEquals(result.get("width"), 241L); + assertEquals(result.get("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testUploadDataUri() throws IOException { + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); + assertEquals(result.get("width"), 16L); + assertEquals(result.get("height"), 16L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testRename() throws Exception { + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + + cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); + + Map result2 = cloudinary.uploader().upload("src/test/resources/favicon.ico", ObjectUtils.emptyMap()); + boolean error_found=false; + try { + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + } catch(Exception e) { + error_found=true; + } + assertTrue(error_found); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); + assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); + } + + @Test + public void testUniqueFilename() throws Exception { + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); + assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); + result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); + assertEquals((String) result.get("public_id"), "logo"); + } + @Test + public void testExplicit() throws IOException { + Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); + String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); + String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); + String cloudName = cloudinary.getStringConfig("cloud_name"); + assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); + } + + @Test + public void testEager() throws IOException { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + } + + @Test + public void testHeaders() throws IOException { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", new String[]{"Link: 1"})); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + } + + @Test + public void testText() throws IOException { + Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); + assertTrue(((Long) result.get("width")) > 1); + assertTrue(((Long) result.get("height")) > 1); + } + + @Test + public void testImageUploadTag() { + String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); + assertTrue(tag.contains("type='file'")); + assertTrue(tag.contains("data-cloudinary-field='test-field'")); + assertTrue(tag.contains("class='cloudinary-fileupload'")); + assertTrue(tag.contains("htmlattr='htmlvalue'")); + tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); + assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); + } + + @Test + public void testSprite() throws IOException { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); + assertEquals(2, ((Map) result.get("image_infos")).size()); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); + assertTrue(((String) result.get("css_url")).contains("w_100")); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); + assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); + } + + @Test + public void testMulti() throws IOException { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); + assertTrue(((String) result.get("url")).endsWith(".gif")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); + assertTrue(((String) result.get("url")).contains("w_100")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); + assertTrue(((String) result.get("url")).contains("w_111")); + assertTrue(((String) result.get("url")).endsWith(".pdf")); + } + + @Test + public void testTags() throws Exception { + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + String public_id = (String)result.get("public_id"); + Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + String public_id2 = (String)result2.get("public_id"); + cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); + cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); + List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); + tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); + cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); + cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); + } + + @Test + public void testAllowedFormats() throws Exception { + //should allow whitelisted formats if allowed_formats + String[] formats = {"png"}; + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); + assertEquals(result.get("format"), "png"); + } + + @Test + public void testAllowedFormatsWithIllegalFormat() throws Exception { + //should prevent non whitelisted formats from being uploaded if allowed_formats is specified + boolean errorFound = false; + String[] formats = {"jpg"}; + try{ + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); + } catch(Exception e) { + errorFound=true; + } + assertTrue(errorFound); + } + + @Test + public void testAllowedFormatsWithFormat() throws Exception { + //should allow non whitelisted formats if type is specified and convert to that type + String[] formats = {"jpg"}; + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); + assertEquals("jpg", result.get("format")); + } + + @Test + public void testFaceCoordinates() throws Exception { + //should allow sending face coordinates + Coordinates coordinates = new Coordinates(); + Rectangle rect1 = new Rectangle(121,31,110,151); + Rectangle rect2 = new Rectangle(120,30,109,150); + coordinates.addRect(rect1); + coordinates.addRect(rect2); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); + org.json.simple.JSONArray resultFaces = (org.json.simple.JSONArray) result.get("faces"); + assertEquals(2, resultFaces.size()); + + Object[] resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); + + assertEquals((long)rect1.x, resultCoordinates[0]); + assertEquals((long)rect1.y, resultCoordinates[1]); + assertEquals((long)rect1.width, resultCoordinates[2]); + assertEquals((long)rect1.height, resultCoordinates[3]); + + resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(1)).toArray(); + + assertEquals((long)rect2.x, resultCoordinates[0]); + assertEquals((long)rect2.y, resultCoordinates[1]); + assertEquals((long)rect2.width, resultCoordinates[2]); + assertEquals((long)rect2.height, resultCoordinates[3]); + + Coordinates differentCoordinates = new Coordinates(); + Rectangle rect3 = new Rectangle(122,32,111,152); + differentCoordinates.addRect(rect3); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); + + resultFaces = (org.json.simple.JSONArray) info.get("faces"); + assertEquals(1, resultFaces.size()); + resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); + + assertEquals((long)rect3.x, resultCoordinates[0]); + assertEquals((long)rect3.y, resultCoordinates[1]); + assertEquals((long)rect3.width, resultCoordinates[2]); + assertEquals((long)rect3.height, resultCoordinates[3]); + + } + + @Test + public void testCustomCoordinates() throws Exception { + //should allow sending face coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + long[] expected = new long[]{121L,31L,110L,151L}; + Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++){ + assertEquals(expected[i], actual[i]); + } + + coordinates = new Coordinates(new int[]{122,32,110,152}); + cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); + result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + expected = new long[]{122L,32L,110L,152L}; + actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++){ + assertEquals(expected[i], actual[i]); + } + } + + @Test + public void testContext() throws Exception { + //should allow sending context + Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("context", context)); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); + Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); + info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); + } + + @Test + public void testModerationRequest() throws Exception { + //should support requesting manual moderation + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); + assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); + } + + + @Test + public void testRawConvertRequest() { + //should support requesting raw conversion + try { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("raw_convert", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testCategorizationRequest() { + //should support requesting categorization + try { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("categorization", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testDetectionRequest() { + //should support requesting detection + try { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("detection", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testAutoTaggingRequest() { + //should support requesting auto tagging + try { + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("auto_tagging", 0.5f)); + } catch(Exception e) { + assertTrue(e.getMessage().matches("^Must use(.*)")); + } + } + + @Test + public void testUploadLargeRawFiles() throws Exception { + // support uploading large raw files + Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); + assertEquals(new java.io.File("src/test/resources/docx.docx").length(), response.get("bytes")); + assertEquals(Boolean.TRUE, response.get("done")); + } + + @Test + public void testUnsignedUpload() throws Exception { + // should support unsigned uploading using presets + Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); + Map result = cloudinary.uploader().unsignedUpload("src/test/resources/logo.png", preset.get("name").toString(), ObjectUtils.emptyMap()); + assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); + cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); + } + +} diff --git a/cloudinary-http42/src/test/resources/docx.docx b/cloudinary-http42/src/test/resources/docx.docx new file mode 100644 index 0000000000000000000000000000000000000000..d179509837c5687f4f30a6c9d9b75b0665c1f1b3 GIT binary patch literal 20453 zcmeFZ1Cyk|wy53Hwr$&XPuuQk+qP}nwr$(CZB4s-+W30*x%+de)s>M=l@{|G$u`1Z_vXBUW2^C0@nBmk%i=wQ=iNhkFfsKdXP$> ziU@rMy5bDm>82V(o$rUp8tev-9nE@aEo8s{ExLm86gvy56ER^<3VFkc{(GhAx$9lq z8xz_TsN7evLQQdknXM=Y!zq@)%SfjfqofJ68_eVLKct7I9! z#<)RN0ZewctWaB!`gkW-J|j9jL-dn7p7>M_S!X1yONB-$GHWS3RcYJfK1Z_6p|sUL zHJ~cWwGUkY*YQ*|n{9RJ<8YbSBDeKdm^b+d`W*o=k@PIqh()P(GDj~)`mlNGxkpo~ zKd8`E+wXF-L5kl`?=$w#Q5yXF;|l>&^J?IQR$#8~{_;;^Z+yw?8O1-Q$Y^^gU?;lo zmrQR9S+7BbHm?n&K0n~bH>VF|I3jKUDs)T=TnV#5%mnc9PO&}s#7zi#Gng1Ik{ZBi z$bru8LLHvQi5MVFKHiM8hJ28D`*#);syNxbz%ZkI`8lEZv3c=BPTpMfW8KE$YtA;1 z3GGvh*r+{nl)aqBo_qp@P>VY`9d~4-u>-HKm>%RUda-Yk1smx;zkT-W3kX2&|8RNS zILwy&Z#$Ox{ub)n<#p_hEFI`*|Gxf@tN%aj-~Y1o$oLHjAb#lJYmgniQ7^U)NW%HG zZLvHT!rwrFX%|0SU?&ULySobT+vPU2j~`AZWS27nG8VZ?xIt_5)RuBrGjv6FEL^Qx zBEaO-Mm5s?O<+W)Yt5`}mg%_T67#qt87lJk8-n?Xc?pe~Y6~=jM!S{TW3j~{6l3Bh zvHnC>w0Dm@r~sv>6|bwon3-JvY#LEz)metsV#D-?G`?P)2tyeK)3aHl)u*%+AGVJ& zr}#sSX-Pjrk`z_-jMFU9L3Paz{%=6LjDbn|8$xk z-|_Lg_y5~ZWn!Pq0R6Yoh;<4kaY=LO0ySEr{ai;0W;ZEYP-YfSa_!yDwp?0|wpt@e zk#JV}((&fXp7t@q@d~I*QpONJ2o%a~ou>L_cxdqAe*cp&Lh7h;8IrO7hvad??}{_5 zMd&Tr=1NSgHp+41LLtj+@}^GUG0RFUxho-4B8>J~Bi)xE70*AVTAaQfJY0;W-4ud_v-@MdyUvpmYDR;xjdPk(qL-(qFnd(Uy; zwfI1pIKp5bu?2`$0wS}8YLGr-+zzf63- z4eZ}-wO@L3|KS_oXxIS&5Wai-!&d**N-xwWtT)(^x8Uf%ewyO-T#|}h=G$c!N5}w; zG(gGCB3jiYi@}JiIjMX^z4*P3P}rFcl0e z%#5mZE-)hKx4e>T##Hl}rjwPSAx1HVePkyhvXCtNWBmSRQ&W&Pa5+mRMF8J-NDp}t zcSMN-ogr=3F*s7cr>J2&mQt?g&@+}IX`Wy_7_@NFxK4Agf!``!2<(M4$*QUz#@<66 z1s3R6O@hX&T;DfTer8Yy8-Nsxt1FDQSYQrSuMwaGji;8UJ204=Fp1w~h>n%`E~XM1 zkA~uYkc1`<964N!kGtBN=4e*yaF93OE6m1X=8%GfwIg{V%-^9hpVH=;gRKdU%}?nJ zD7jDRzc(1+Nf#Y3eN9{OhPYAHktxoYFKo!VITDyZy@-gXm>lM&YI@fPB808Da}5j^ zFq!F#OLoblVN%ab4_8~sLO(juK?;W`=YJIy0i;i`gWIjcKzq{<6p`-?cNX3_JWGwb zxptoOBRqK4GIMRzylHx)hI64T$-5n265CxOMpNeX!525&v2`> zN>uj`s0WdSOp<8j@v{?B;~)}hW{p!wtGp_CswfV8VG!d*fM3q`t%GIFA51G) z%VG@r^2KM$2L?eVLO1eUy*#+opw)%(M0tpX#{Kx1ur>UJRxg-Kl6#VmREJLXkGyCy z))^;4c%6I*6G+ac_qw(elaI$kZkYE<%QU-M#!o!5OZG%ay25NwzCOVOY<`vMc?BMS z96$!!))%H-Ik%DkYbtTgcBMf~^d8hqx`V=Wn~psJp#%truOJReU zf8v2vb(53}Eh$rY|3LXD=N=vs5@aX_LYuXeo9qZbk`=N zG{q9EzxE%fv#~>@kbl}GnMW}xMwig;JV`-Z1vYPYLZSe!Q&H0=U!IU*iWu4n`0p|NGgft(WbZCk6{5v)%dJWnN($>mvSny ziJIh+8vbH&Z=_|p;&%SAlq#FrFu*oQ8K5GKwJj)HT|+|DUS9E(M0RD7MkdGbD+&? zceM(>E^Jex{>FdSE|==Gg!BmU(4j2$Qh$aB8drb+ zEfOCAwS9qz8t9a+Rv!W_i^kuD1)%l$^U@}a_g>hb=XW9O36_Zs+Eo@$n`*@^_JwLf z)m2b<0ZGtwwX_C2J&bl1@sg~;&8T(9jkK`sE#tvW4kUen&XizxT`0o7m;=GW#8JAt z_B&Y+8H+1d_{Xfl&9N1B0hWI#o~x&%SivlbOGa;X$&Rpp7!iQxm?KO)bC>PB=1J^TmCARKkApJpSz)57IZ9wOvhw|y-E zxo@0Ze6j3Dy0>aip#hfv87N6dmlzd+FhU(qcG|%$hD_vuKOWV728u+O8`9<$HY#uChzdS1xJrK`Ah252UXqRaa!Aqd_UJX(E;5Mq zqxTmn@?h!}kJQ%UP2$9oU&8OHNVVGqn{0dtfenmJ8TXZo=`2MXfOS@b2nV&y1Knqk zF(j0^0NJ=^mLA(x2d>kas@j!5a61P)`dyU+n27QwRr3)|(Cri=IQO+!n!52MJnt&Q zFQJhgRqdRaua={x1tVh7^Uw#4$*5%A zz1Q%Du0NkvAi>wiTrDDHm@$2^L^DIYfgw6PdBWqtqpM2C4Xrn4E;XErR8eKvqN=_b zs$kH6E3WlAJ35d1qf>lnCaQ)>vZId42BSg^CxdJ$#4syHQHr6u7AjUPRx(0~W8qBA zj*|U}jSyw6Uc8j%wp`=}=@^<~qJ9Mw`O4?Yk;nB2XWI4~Rl1 z4`cBNmZMD6Ek_wdD`k?s3`J2|WrA$)VlG$&pz3igse8;$775wZQF8hyzIv(BC2lJ5 z)LZoY&Ohr3ilgICg)Bp&F94(+{)ybrApZ%G3V%u*PJgGn>Tm!6@c%?4V;dVsBYTFw z9DkvrCA&e7?3L5_1^<#ost00)K;bWd04Ioo$JgtWIZRYXvtdTwl^XfoJU{n%jzA)|Qq%YA)W~k^S}c zC^uZ8fSY~FNl6^BU=khUrJEFkrWO}Dk~~k?98?IoTTD@;R0ksIxq~0QHBQN2u8Lq0 z{T^hw)Q{@bR6qf?P9YD{^5JJ`n~tuN#yHN;l+{{{i8M7p=h~hAQ1qm5QJB1}=vW@a zrNH!<$G~%=Is$92JE7S6+|Aa|?tBhgcufUmcR%)->BL{wRlI`TVb9g+b(I_;D@ z2WEEpRzM16U!GQ&A+d8oVeOT^^A3eSvV-)llxZReP;+*!ut6aV8l)h`odIrYX3G&! zQ#a{vUd2KbT%7S;u$P%T&Z^C1ExM=|9n%`J(l#Ko7F!k2lHr~HLMINi$k|7|rH~=n zU2&$syaZj8fIi~12FSn(cpiJt=uL`>rfu7Y5Yl#O8K2{hR^tS+NpG!;6PbXAfF}&; z;CiJ3-5MM+o3KRG5G|IMq+tEO3a<^A9f86iVqq^+Lp1@&G%@uEJacz+-w9~~_3tMa ze<)4;lm=$QT&^R9UgS}L7PL^7cOLxn>H@e_jfTVLwWEIp%97iSr-3cnlnn$)8(uUo z6aZ753s-J(?Qw4F9vNlCFO*OYn4;e4$hLxoqx=a0%T9C0H&nh}GAk!Q?Z zzeh6J0klT>)Tukgp8Dh|c4c8V)R!z{q(wpX*%lF@+d@HuVu?b_twCu~*&rlVyF9%G zUPs@yt$p}%$C30EE}9~VP`gvcMmGeX+?!ms<>J2lwL20^GsC)Q))dlNNnI80y3fIU zmX4LTS7$^<&hkbz?^Km{<#u6~wt@BA4sUtKnvUreN|4WR!Onk74@~`vkov!q)AVo8 zK>SzRxBfPNhriZen7Ad=Pmk<-E$tan^fWt-UH}zP3Vcw-2PfAN%~E~BY&CLtc?pVj z3NGKeCcEN&!o|atHQDWmQB~*!NkDR&8Ykx8E7iwHa%~eaM-!HL5_^ltUu;N&lO|0^ zN9q($?+9*Xcf}wmxbsS0kq6uqltNq{noxDdq zi@MfsI1+V0u(n{VL>tueN5**RuUF--8R!iLW0q9&`0gP+wz^gnAUBglKrvc3XI_!VWgeG2jm{&5C(8AxBK36lcpiZj3>X&p1PS$ zH#FU`vt&%UP^*i*u3*apFai6hO4obJ2%c|Z^`O0|Z}nAAinA5l zWqqkf9&+o9SImL6^IO@3DNU6kI7L`Vq!1#oF{Cnun}?Q6wfG@-NMs5)BMH{o=;iyn zr7{^J<6nit|DOuaF#m_bmHvN)gG8utF`))I(ms3a)*eSYvhx(@#ld?XnX)`thdT?i zwBRMB0k`jIcU367Nb5FK;092XQ4?q7j^URwh&TJU%i8FP%46SjG->9RHUjUs7b++7 z1Nl=Wt_9udsDsy?l2pZj)GZtNRI}b5jqbqT+1S_CcEXme{t#^iN{O?Ia%}4gA?@j? z{3IT}P^H%r!5qxpV?*qIyGSyv*6n@2Hh-jJQyQeWew))b0_70N67lel@moSZ#!IZL zXuf+CbnpR;lz1xI6^R#%I5Yz>eeu9RUb;0}>=a|%Vfm}@;Xr9Jn3l}~5;CKAS<^lL(ZxT%Ed1ZT6@Kuo@c$j&OpWvm|5tdcOx*b1x&Pjo0lUC~ z-Xd#`U`CJR6rQWJpaTWAdIE^798`A#BpI35h-o$;%ZkVk*;0it@_rXj?`(Z9_|=GX zTExO3SppFexYrzOoY9~Y!W-4b>C#%=5VL=BvcDG_^w71W4lYVm}}6v2t0C**U?Nmy>K-G@};EP7-M19`z4 z#fS}6Al_fr0*+w()kX1ovKNNi2A0#5%xnC!hVYB6hFQ4-a~`|o5or6)*^(1fG*YHS z!{o?jwZkd%I1A6@Q3})^;vFmTt7c0*Djklj;cSKP$+*-A=H^2P?EPJGk@*huK4wdrY| zA@zpaGTUb6pIW|ir@V=~z!A(dSIxtOl_(VD3u=p=ahCiZ{lZQnZ$+&$YVpJ;_ty;$ z%Nfed_CyB?mbnUgo(-G~O@}DWBZ8|`{G&LfnOl(L!1b+dFH;^=C2E&dt8IHxEPV@5 zyKRNZnt8zKWa^eG!#STPO8HmyLDgp%iV`qEGA_As1{p^UiI@mq44PR%Lsmt7LQnl0 zkY-*Ye0@r}m?U`%OJ(LcjrnLk-{$b|zYO$Sf)u6SzZr#C001cej0KLSMpj02|9Jf` z7*dn9* z)ha|!!w+?}=#6=e(-LR4rdCuTen?e2o^>_6ikBGi<>2iL7q6^hTO_S7e(B5Q`FN=NBGJwA$@;cFTxeOjfg~vm6YT{ zxB*q|V+wIy>q+5Al+JKl3ixO6(_u@qHf0U1a0Y4a#9j2!ZOoW5*;%=S2Uh!i9?+Vw zc=uit{akS*lb}9OFsG99R1}-U)kQ6;KS}a_yL7Jp^b{PoR&sACx{x8h*b63 z=EiCp@3nSIe%o40*Kk`U`l=_aDk=>SFCblW+?C&d+PmEOX8>xi1B#WC8K*>is zl7BwhWjnVlSF&h&hJB7_PFBg3ELa-VtLB3m;$b;FH?5l+IIQ(y#SXEZ+(b?;ms@299{r;0`M8)F!7Jd)_P)QHLtAoH8~%D7AnSR5 zcVmH{HzA#lpv!rGycmn9;r;mN5JkC>_7gRFjvW@t&o~6Xyhlf7Qnn)@BeLs88313n z3!frRWbaFt1o_Ag$9|ZiFivy9=HMJ`+Qs>CQ~>GfWj}-93Lh4hkCN$P2xpd<(ts}(3_c(I((V_?&XINSsml$J3~Kuu zEJ<{SYz=6O*=Z;<6g&?T0!nwMOe<1P00owTz61=iQ;G5j^Giqn6uFZ1#NIw6TjDsH z4qtWbn-$GkMy@+OS*wzhe_ z#xRJQUP8Z^Lv10YJ+>Bv1k&PQNhI0=2l6qh3#)EtpQ$_+Ezbo*tFRF(v~XAVPS}tv?Ri$^?(F zgVacFiRz>F8zs~P!n`RatQo#cW>jf1utA*L{?WVSEvZ!mQy^GEO^$a0`;$b|B{e)(XV+RrV?(NKh(ogdE-JNe&2DlY+~i2P#58*h)f!TPAi#|(46Oav zxOE~|4<+dlomi4R`qzxo+#AFuZzb?L5U(Z-RHS2ZBM+_8U>;-Cnc zhv`+Bg(x-&2tL3b`6xY&dz?>UoJODWnhBp>^TPO7e=6zJM}f_Q%)>_K+SLcJrZQ7l ztXDeCD^IZfhPrZu>L!byHa!+)U>ZE!sJuf3@MeyUv&B849Y3;?hS z^)JxtVC3j%W^MA1ET>Uik3EXi%XP>9G> zWJ83@u;}j0ua}V%(xpLk_yr(ec+R6E!gWl=qJlWumOvseLMlSvw&ZXkAkiiayTa^q zb5#j#0zs(p(zTdjJMI{D^tMoQl6h49QM0nbWDA2|3T@g8@r^mTdEQ6bHEt9W=}3B; zQAEP%tN20N^AcdppE`5BLskH5UWbFx4frd>x1#Y?9Z^cp!Urmo4UkFkBy*UEgJ~`) zM3maP@n=}MDRMbKOGQ7g;(`GG5Ig&19i?~8U5_=-?7OIw7kZ}W0aT)NO4YClP%6=r zmy@Q6oXE-Z6&(hUR)ta$h|5LWVMkE|Wyby9#257Kk`EQcpvjL%Anx>K^$@v=!%wSd ziewZ-9=(EJo6!DUfB4{Qcr`(%$r zWT6Y?EXMr3uxt)_1`!LzLj!LOy-HX^YPn z5W&{{2o;;=2V0FC#9$J#h&U=hZD*1(2$LYc_*-O65wifMeT}7GAQHJ;XK3(Po}8q! zawMl&jU@1u@A?43GyzL5Hu-CqvDK|lTTV0pF}{FYWewSO)Zk#UM%8Hst9uJfm4AiF zDJmW_n44!@pjkCJDeZ4sVGLlV627sZ-ksb-ib=T?-<{fBBB0RPKh9tcUwS%mk@$*& z>z^ePnns3^%4|v$Wz`_TpC4NjxkBxZ{pN$~$4j5@zU5G6p$AKb-bt07)YL5pgAFfB zj>{8jqz$qOywmL2(hU{7)5O`@)E;xi_ja9}mkVje2hVr)F%GCB2i{Q0V`tChEB;4n z@mhvnrq?6*Tp1)4MmaLg=~KoA6MkYXw?}1Kxy?i$IW+4(o#N_bV4$sZBKO)2IjwF>>31mL9JSEgW)W*ib?VIhuARpLquXzo%luYQs5yV~ z>m!**hwd3z3+FkfjbB1VZswNz>p>C@k*WzrS~m|zIRTMD(=oV^vmPX~HA|cC8oFKi zO8mwUYvEJw{qk3OTbLvDV?8synZB`Kh-QUqR1?hr!|VjkaD++_WT4E}{rCQo3C3u7ugirg?t%Ub?o&T7-`E677-9Z2VTyX;cVEz-y9UR>(jT}_W z98HCdjg7u{6aQ}0ENX1nZ?GeJ?Ua9jC2!;W-d_zfPo&rgBdZ>F<*rkFgO-mSViC7> ztJ{BhHDmq>ub%)D{|a%_gM z5-~fO{lIS8Ks7)tLOPMf96KD8()yf)r!CP{V&8p}H6t_jC30NQ^M=Dj!&VqbY?m{# zPC-Dt@&f;3!?h`j9it<04mS?o7)CI;mdm6oK2W&Kk7JU#+0GA+F6?^8Uf#%5>_$k916F%&8U?_&$) zAtM5xZ5#8)Ja=#1vftEg(IQpAI=V>+hY&3-5ooyVl5!Q9^ z4;BSD6BGu?yZ{$S5oko&xF4+>S%aGDB2+((U*@)6z=31r16~*AiJ9EX|L4eLlqs!Tw>^=B{yqxXm%Bdet0Xx~Ot?=#Yk)0t z>=jZR_?d0{CklgvIx2RT3(oABZTX|%E;7F%iHX)w{7tIK*YhN7af54RPj8|n zPlA~~?r58^?j9yO5^Wkv@rH~-QO6tcjOc4#2{eJEiev=ZbVq&$!wQj$RHenUd3 zqvWxu66G}`FbQRni&~yAgP>CQKBExS5PVX;>vyMrYY0i9ctYQO!es{-GfI*BQSw3i zNhyPDhfK)i?RreF0l;{LSDGTnJD~Z)B#yL{P30%QhiZkM6JQW3BHtD&g8%PLTZm*I z4B@-a{zC2K-=Ts^{c6AGuX(#BuEZ_x{X@R2a{@lGRB78N2z3Zro+5k#o(B2vJpV4{ zzc*neLT~u*K2*Fg6PEc3;3}b*;QyFsPL|M+3Ns!4_c}5B5tIIkFF%#wTPzs)LHeN* z^8bg({wcoytLPNF)g#<=dww)vaNhF$xd=#y^+_v2`;%4U1`DH*nRx^oQa+>qR(F%; zJ?nN61Rvw)=3tg#7CeJB@H5{C8-hFEd)s51XP{-~;mFmeqxS^XeJl?P<~UvIwikH0FyvplQ7xpl3q>s*>tZk9R_pAJAnsJs5M0==~cV+Z|Eq$3inA@Zf~F0BcT$HG984n6Pp(b=cn%l*7WMp6#vP z!$D9Vm|e;yipjcU2K?_tWE!vwPg-!8g-g0-`5Uys&M^N7fH}8`b;Sp;_mG29eAd&7D#3w|mTX5+%1q3l-ub zr^^3;9q3$ssOp>XeoCsRTBwWmaNALX1H#U4=XNFy3h#*grS33AV4W2Pl$Qlr^X6)40%S1W9oSa7pSmD!yHI_-Cj>ixR%yuiDZ^9B)ZQlf z+mF)~RD(dSXX=rHf1;pBxVA@#^G|8Qxy5e;Wstz_tWDu&m&DOH_XaA1&FL`L1p zFb${sBD)AI_T9wgfdr$se54G)4nZWF582@(3*r1i(#3=3*SjjAm3 zBbSHkX9T(lKqN~rG-$oPU=Ny7tfxac?Sl$q#DPqzg%Aw-)gzVj5uR-!qcU}c>1Cp< z8Wi~Y2ex=TnzKewkT7PpxhR7aNP@$Op z@{<2NAm?5tFM?Z7FKt+G=8;qBSUlsxx=;x&oPJ%D;{--tTN|}H`GU1Lay*h7H$(QS zedgTYdJ~h%D*EZqDypv)0wNeQ*e-aADg)+$=mFXSC^mfKyObssEHbw=@{}+Eg#(Y? zF@>acduH<=Nm0{Dl->H96hAls0KU($|EtdLzew?a;~^`&*R|A_-@ud5%4%4##EA3$ zFjLuYk?DstLV^eu@zPEqyPoa{5O97?L_)*1g>F^EFrHrRX^#*x4_Tq}Kuc zDi!R8c&@5N)Kv1`u?@iRZpxJh(((mXZ@E@(xK{SK_4xor3qt!v{F!E{bBO>&cB%LN zHRCI}lRv9X@pFHAG7s9A?R%K9cestZ;!-2gZ#y&p*$Frz#P!y#$dwouc%Qts|D*WP zA7hc76x5OhB8)!b@Wm_Bx7uR)+iTAZz+z)sRW1=t7`t&>Q^!!aLseDI&o7k`q8`lm z1C`hz$Ec>8_K|EZCD{&ml>gJ*ZZBXLfTffVMw(Tfj-xz}l^uGsB~~6-M6Y6o+tY9^ zKM>0Sj}U>*yQ^)&AX~8TqMAk((*&^W|UFHr`z1nKHHtKNN*D`l|nija#xT0a@2d743Pa;^(0nIrG zaXO<}PNhz#HxZn;P`Go@%tbxEFlp{29`YVL2%ThYo_wT=*r%*Qqik5;&P@31Pu+bC zEC~q;%BNZAKBEvn;pU>Is9a<`ag!D2JlV*OPn~_DHjEg6tM$2C2i)64ZGoLZ#LEp&)p(Chx@BDf>`0sPkNxtW{9V-3XIZ}OSqof9PJ$0I;#=@t_KGE~it*{B z`P1-fgoTrh&wR5~%Rg9ZqjEMkm6TGr4kO<$|9hU9-;4RT_RUf;-xyjqRAQVat^}zE zB@d(bcb@wVNu|%>#~6RtDJYk(@K=1u3*Ta46=arU{zkcf71=-E|KCLyu3`akKgp&` zDkyqYlC33o@JZLOnBQHCGLfef7}v1*DfdUsk`0x2;fXV*>+pSU!G>0vN&3dE08gw_ zN{Mw7k+5_QlTltImN z$1oKmM3amA79m;uZ30B92=3%5%JKuzFH|qyyO2fA&o0WF`?#ZZaHOO@$jh8E ziR)sY!%~x1lg%@{rr?BE6UIz!P4)MLUuBWsWLEI;m&_Kw$!vH*rbLh5ji3YrR^dJa z7PDX>`z%+yBtR+3y*OaqRDcLCU~ed@HNu1FpJYY~HAdUhOkH@l-J>t$xEg^mN3S;1 z3bi5^&>w%4f9jZszr<6zgAk!9N~i#H_dpI8qNlyll^L9~bwwpCSZsvzwBOx?>&`CZ z>~;7gl?tidZ%M|QiHp>fgX%$lc%VuSNz$*X+YzUm=rf^8?3%4)A z(b*gbw-+_`vT4nmcmlm@{RbJJo^bLo6ZQ^>J&LtnmasMp$_Hsp&6-#~yju6wKeSH1 z6yH2zJX=yhIQNyeJ*_wQL1Ed89&8QQ@=gbjXCG$=HNM7LxqlHunhWq)m(yjZ?cNbA z!SO;T^&x)s{owV>f9@qwe>sHJ9oTy@)cQBhCO9T&s>D-hwjwSl zm-G%5(1n}{gfC)Xux3QC66;^I5J{8l?HXctUi6zOKUtIDv|ypPjYgh0wf5M1&;`(D zCN~bL5`4IL0JPY3o`5v;YU{cjkO%T zs?2h11&*M6SX9}DE~c2lLp4ZZQ!nCWXVuv!u}Yr6t6j%hqB@)1WUw)G+*Dk);WwY;l zb^mik=o?uVmHYk{qIAKn@}YOPL|-mi0SXVz>QCrt01aBj1dvjN(Q6^sepQd~PDchHiGVdmx zu0mdJ;xnQ{;K5BftrVU>Z$_Ukorc|GDSJozf9Ch|dZ206eit2e27MCKT*0tsnr)GJ zK!%i8Z~6Uj0j@J#y1M^#?c2h>dXIuG8Cdx7zFyH!3p(=gSswWHAM>m_Kp-QqZ)a}! zE|mF~f=O#fB|UvhqrdLlnW$y6PLCXXO?JsA)JBz>zX@8n`a5N-moS-NanHW=#5)=q#Sw5RcCu)_^>S#p9>pl=C^Hh=B z4eb-(;I1S$PQZkv~V-%pk zyF)pj*PXYapR>dwkx3mRi4ot2T~jnk1-@Y8WLI4~RjXu#l1rsb#22Z^2wl4DP${4j^G{l%z#NdJk4oh)?{y1$Pbwn6L|W*YA|^Tmz8 z?42H3=+-tS&p`qhGw*EjL4e?5RUb^IKb9zjOc$%#f;eVm^OR=c(vDdz@%P&hj*u&|avu?k*G4+-I~r(KUT zaX%np3Pe|$1^Dh%ZVQe&ynLGKX*PFW_Uh>f z5aw+7Pc_)XePd+S09tAIap}WzlE_ZW96s>dj zIZ@0siUyaR=G{7eD#>i8lzE2180WPCDPp5%*B#Ws+cDmx)p`AQR5ns_2lKdz(H)np2};ypwDJI&+ld~hIFK6L^PcYy?PVZ4IQtJ zUXmcp3dNYdXAGCJTCcS*hTeH#v$jfh%i!@DkyLMYLuninup>z50UN}Qs zL{b~j;(Bceaj_IF#v4c}G5*|=W5irGj%?WIC0U#4lRlfK!Ypwrwp6k%*WQ1LhBeYB z?jKw`M)WbG<70yQJahMAja+X;#Qc3vvX|1>a7~W1^KvT)P8gRO1gW?2ruR{|NOeC6 zLXuei2P(*T(cV5iIRpWZ8X*=n>^yQ_pJtmrNq5&?wFXwZE_>6Yt+=_FZtL1?YK00m77h!VvMy)!JO-#{$4uXX#%m|x2Zn>;^>#Su zkJ)50G`Sj)v{csepyo)jACV4C#FbGP%5Y!ygmX9xCwhCQT9A-2^2VUi7rccMnr|<6BD!H}Z5!_wl+d z+&y88vV-$J>SUa;QkYapnL31Q1s%_35kk3J5a{Hk=64=T~wicTps zxwBs)8gTmqTUIK-)4axAv#myk)0jsXnwcJ0LWomcMYxfyRjMD9P|giw!Bs}6(u{m+ zt34EvnP{auU7>nbh$-9CF2nhADJ&8CWT|6RTn)!ZyQ@K_yd^(hIn5Zba6>Z+@S}8E2;EGS4Chv4m~NjIo-eK z4<0JjSdRq@`(U8gn|4!he}51*ru4*Pp^|jPv{g$Wi662y=AwoHfmNI5<*}xjp9elJ zOt1K6;FGgLhmQd986;@(b1iZQ84FO6OyrrX z=zpRKPceYctoYt^CG&{l;};NsCpCGbcrc0rY|%36Bu|f(58t^^dWLC>l`i2bx4vp zR+^2KNWMLD)35b0q(nQvN@%BGOWU8BrfG|)>{0%z1uzyx^M_lm@E5MMO$MZv(teD= zm;KMtWogjA4gR~P_itT4@;^6!U5xY<|E0nya(2obkO3a-#v|{#_ON;k6+uliNFgxW z3J*UcODjN}R7#|P*Q=m8Xg;FFbMig)g>HrHx|~R^9$j5tHmMXJ{lW@ept};2j6P+tiUn!9)6E z2s^`Rf$@f#$qThkVtRy4Fs{5UdZiy5GZ>to%}0DM3WDJJTB>*BduVdptC%T%QGJYy z%Zvq|mB zl1T_L?HsiQ@J<$B_~sN!HqlGQh%CzwnQf7i z-@;O7E0>UgEO1yrbEk>~*a1@CNsJ#$rMO;ZPD9jL!o;tyG9`kgM8z$>RHz|HuVHEZ z7>hsj7qd-N6cvY$d?*bC6Q33l|FAxn@nr&J5tEyaXOhRp88zg_x7s#pMM|M%Tft>(Z#rq>s*m~4DZz>U7b(vzxSLd z|MB%>~ zCl5Z{@2YNGRb2CDdgy&%C_@^Mj7++~?2P>|E^uTZ5m=7q3h+iX0QG1eB)trunHd<6 z4*EgYhfCs+QPtZ+5KaB!m)?wh_2*%kI=-SbDY9h2h1NOzh+L88cqU%QA z>xR(%2)Nw=t{c7)3SB??&MAccE5JeptRHzF6}ooxeK-j1Mqg(4fzbU9 zxLpHmIr>%&bR*Cg!6J+}sRT6wc}Xm~cJ#Gq2XSE@UIiOw< zx@Od|G=x?L1~+R42K13NblvEkcZ7and#KfEz!UD^U3ql<=)E|EelJI;ew02Px_^#Qn`Tm~o@Ar9r&mMRl5eq-7 zRXDcG%|;Qsh@1p1I+Z>}ggM*civ4pAhGEpz!ng=r^W)NYCX|Vt2;~xrJM!b4W3HS< z3Au}3idV0HE3DqgY<&$Rv#(#y_*=}ZjT(L$81a((0|URDZwfwghT?o*j+2eRP#!KO zXUW{g0ZijEnKtZHPfe%txBa9`)C8$;>hwW_?A zOb6mvM=QCr(EI=PJl;G-?z4FfA6Y>Z57d~91K+WVF|vX|sQ=$jPN1%F*UYNC2Tn6T zKrWA7?7G@Xr2~6O8jWufRbLjih*@N_6U=q4ltEzq0kV6#Dpq|qb^fRfI)%|AH-opw$ y;T9U8=l|7uzkj^`y#FFWi~b^*)h%*-m~Th7NGK?hUlJ)j5&3yUWZPBAsN7$TB`?AN literal 0 HcmV?d00001 diff --git a/cloudinary-http42/src/test/resources/logo.png b/cloudinary-http42/src/test/resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..10d58c4b80281d2bacb3e8c5439111a242c43972 GIT binary patch literal 3381 zcmV-54a)L~P)nAibqOU||W@$8x`b=@e-#2UnyM{SU-6uG->Y0%@+w zauXm$0^DYv_T%n&G`~2cWSu<^AS{dIa5OXDd*6HWX1K-fL4JPpw+{c)Rf-u%v1Axc;&viopW}xA>yxd;sz92zXY>7G0$olqI_z1GdN$AEP0mSzYU9069pI`v5Tq@CpAjw1D=Uy^lI)hH_&5XCKNSGL+X10k0qd#LWSifH&8iCZJ7x z0Efu-vBpA^72gTLt{?%#&4Bn;0ki_Nch*4pV}MzfgqJ@ET^b~SxE{dBTAnGW^Ffoz z8nE^k3W~iGl0t$!Hfef_=~w~aZqvX59q+Qc!7!$=&_RL(5Ib7Oo+(%8M=S=+I-y&GJSJ%@u*EIl zJABMJuy)q@Blxq4#^09DXYc2rWg$O03)4_R9-2H^L79_#0Pqngd<4H-72rC?^<=d# zLzY*NM{kL-GN~ZiEF^f)|J!QE*kf+<$F+gX2i+KhGIDI6Q+eQ`oVkMW>^!6a41mfx_6h%u z1Dq!Xb}hAK#?S8t*gAl?;dQ`7Ri_P6=(&%j2czgSdqx+ns_J?S$Ov`wncgDhG2nwU z6Uuw1xbA>oGkZWF9xM7I=f80W=ZCPuvh0d&6+|&l>X-iB+$6$i!K#X<3 zeM=CCKt$PCiA-U?|K@r0_s@?gLOsKZA_5IfIURut`wD6737+fSi2(C1euws1u-vY4 z(Ez|Ajn#R~GDG~}&Ue8lHh>uEibI3;BY$yV$;3Jn2O|Lv7Lb5*SP>ke+%!3_3k8!{ zl!l8H?lXO~zJLNFUzQlkHrR_!3H9Lz2H}D@!7kdc3~C*OmRS}t+2f>6z=vDmaT!43 zpKqSMd_5H{AeDh&lMm>UrhhmAnb(JDm`9(Vb4eXNHYq*Pcf+7eI8Zat??^)@y(|qc zw+!Tog?GeVf^43xsxB^m|!vDE_ieN!d5T_jj(>)T{i>i}v4w71jk2TmjqztrVrpft+r# z27|AC&D6Vv7z1*c9=Xt*?m7TLwfo67b)F`$HR0#R8aANY@kH#$k*ZFb-=PemY3fbo zP89>qYO3od9KB7$9u-aQ96|4Z@s=-3?yH6DTq@wk{ z(nO%oMLL@AI*tH5QCMKzShkgQ=2_qJ{>aftsJ@F@niIL>+^+4d4jtwgb>*NP3WG9Y z`px!MA*U4^o*4pKm0Sp)Of1kf6J0Fns!E2S#f^(Ypl_4ht5~w2eM`MlL%bq(m?C5b zrLUU*?>UaSSxYlett*klK$cDW^J|9|0LKcjcVL+-$-iFzvBQr!Kb#vCS!G2<5bHp> zj(N;ugD+0W5M5+8>0E`Q71c}Uf>2i4HvHLb&7f^kNP+FbzwR0onvHQJ@J+iocHncf z8e=6Fl)?xM?9!mD%g-H6eJ@+8=$kDXj6hbE{j5&-J+TJBZ9{;l&(HTO0Jx+!F{?iB znrf)dMhQBzqX~SaGG%A#skB}V%b}}Rq_Gqfj2cNw&NS6l8^BOxnZR@=4s2>Fft>)R zic?(Gs2jOZqEaXo!ptG=qH0@;$j@3;>7XHyW%ZSCuL$6X*;gZ-RS0y6^uEtQU1gc# z4f|8&F1PydU4N_fuSR7@BhJx-WytEUuP{Qg?*$)YB>GClxJm-PeoI7+ZdyY0ZM*ZtVRdaPA=5Hr~(#4nW;zn z?=|Qv?`@Ex>1pa&`l=L&DferHXQiG^Zpc+Jk|uVY+?#@y>Os5VuKeC-L^fj_ zX9ntDxFqM}8WjMKWvdH?(boi7a)aV%qwbMlspg&vCKl5@sZ9L8u`YnFKhPelepQgm z9QvJc_XMfi>*)GB ztHc`4HG_$Af>ZSAK69J)xzZ@ZMcsLMRq3EgN%o-#%3xr`HeX1QT?lQ}VfD7Fs?vtT zDnM@`TS*+yJxk8;y$S+Zbr#Ss1-g(werL6e%Ns0UjSpD;NNXam1hPDLZ1C4(3^pBr zQz=iovFs_7n;P0Z-cSI~43vM)J_aF8b&nfr!_4C9EX`uHG!FG9zt8B9ecUzDM>2pe zHu!C$ij8hc?Q;R}z_H$<_&hU%(h=*QS6~4yfIo1oS5eVVuXC-j4D?}A;U3kqzx4RM zPIOt4rTOHRKnul(Z0svSISn51qstsu3n^rAKR;%$!m$MCsl4rw3y_6eW!J%#mVpxu zO_v8is$c2qOTG?L?BB5PIZhUNRTzkF*8TF zwLbL{h_TIVS8(1-AP*lxOa0;7-;@9*`b?YQb;HZlf^2}8rmu)0PMtw4fSG*GU0ABc zo_@p_pcK^hWWE`9-DSZiopUu{Zs`Vi*6^QWh;Sp2i^ zd@8!w#uho$Z#?cVKK=TK#ruzuwdJ!wJ9+y2)gX+SARFix)0o~Dtnwm7*|tR0>m}pg z9{;xY>nH!X@VGStkP8mvG4qQqCmhHF09h4=cM(dv3_fs>2O(R&H)7AM6AuvOptI>0 zFW!>oaeWo2C={hT|MBy$vLAo(Rn_+=mqJwfvYc)T66B{U{}*5YYcwb29g!=G00000 LNkvXXu0mjfH5q86 literal 0 HcmV?d00001 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index eb5f52db..b3d8cca8 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -15,7 +15,7 @@ com.cloudinary - cloudinary + cloudinary-http42 ${project.version} diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java index 6237b8b5..f4defc5f 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java @@ -1,14 +1,20 @@ package com.cloudinary.taglib; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.SimpleTagSupport; -import com.cloudinary.*; +import com.cloudinary.Cloudinary; +import com.cloudinary.Singleton; +import com.cloudinary.Transformation; +import com.cloudinary.Uploader; public class CloudinaryUploadTag extends SimpleTagSupport { @@ -60,7 +66,7 @@ public void doTag() throws JspException, IOException { if (cloudinary == null) { throw new JspException("Cloudinary config could not be located"); } - Uploader uploader = cloudinary.uploader(); + Uploader uploader = (Uploader)cloudinary.uploader(); Map htmlOptions = new HashMap(); htmlOptions.put("type", "file"); diff --git a/pom.xml b/pom.xml index 36c12ce3..164a8661 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ cloudinary-core cloudinary-taglib + cloudinary-http42 @@ -94,46 +95,43 @@ - - commons-lang - commons-lang - 2.6 - - - commons-codec - commons-codec - 1.6 - - - commons-collections - commons-collections - 3.2.1 - - - commons-logging - commons-logging - 1.1.1 - + + + + + + + + + + + + + + + + + + + + + org.apache.httpcomponents httpclient 4.2.1 - - org.apache.httpcomponents - httpmime - 4.2.1 - org.apache.httpcomponents httpcore 4.2.1 - - com.googlecode.json-simple - json-simple - 1.1.1 - + + + + + + junit junit From d407d12f64bc677b79c754beaa435fc9866c7a82 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 9 Sep 2014 12:25:59 +0300 Subject: [PATCH 004/592] android jar --- cloudinary-android/AndroidManifest.xml | 10 + cloudinary-android/pom.xml | 68 ++++ .../main/java/com/cloudinary/Cloudinary.java | 320 +++++++++++++++++ .../main/java/com/cloudinary/Coordinates.java | 37 ++ .../com/cloudinary/EagerTransformation.java | 26 ++ .../java/com/cloudinary/MultipartUtility.java | 121 +++++++ .../java/com/cloudinary/SmartUrlEncoder.java | 14 + .../java/com/cloudinary/Transformation.java | 206 +++++++++++ .../main/java/com/cloudinary/Uploader.java | 336 ++++++++++++++++++ .../src/main/java/com/cloudinary/Url.java | 262 ++++++++++++++ .../src/main/java/com/cloudinary/Util.java | 52 +++ pom.xml | 1 + 12 files changed, 1453 insertions(+) create mode 100644 cloudinary-android/AndroidManifest.xml create mode 100644 cloudinary-android/pom.xml create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Coordinates.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Transformation.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Uploader.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Url.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/Util.java diff --git a/cloudinary-android/AndroidManifest.xml b/cloudinary-android/AndroidManifest.xml new file mode 100644 index 00000000..df33f43e --- /dev/null +++ b/cloudinary-android/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml new file mode 100644 index 00000000..3eafd04c --- /dev/null +++ b/cloudinary-android/pom.xml @@ -0,0 +1,68 @@ + + 4.0.0 + + + + + UTF-8 + UTF-8 + + + + com.cloudinary + cloudinary-parent + 1.0.15-SNAPSHOT + + + cloudinary-android + jar + Cloudinary Android Library + + + + com.google.android + android + 4.1.1.4 + provided + + + com.google.android + android-test + 2.3.1 + provided + + + com.cloudinary + cloudinary + ${project.version} + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.9.0-rc.1 + + + 19 + + true + true + + true + + + maven-compiler-plugin + 3.1 + + 1.6 + 1.6 + + + + + + + diff --git a/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java new file mode 100644 index 00000000..d57fe2cf --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java @@ -0,0 +1,320 @@ +package com.cloudinary; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.text.TextUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class Cloudinary { + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; + public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; + 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.0.2"; + public final static String USER_AGENT = "cld-android-" + VERSION; + + public final Configuration config; + + public static class Configuration { + public String cloudName; + public String apiKey; + public String apiSecret; + public String secureDistribution; + public String cname; + public String uploadPrefix; + public boolean secure = false; + public boolean privateCdn = false; + public boolean cdnSubdomain = false; + public boolean shorten = false; + + public Configuration() { + } + + public Configuration(String cloudName) { + this.cloudName = cloudName; + } + + public Configuration(Configuration other) { + this.cloudName = other.cloudName; + this.apiKey = other.apiKey; + this.apiSecret = other.apiSecret; + this.secureDistribution = other.secureDistribution; + this.cname = other.cname; + this.uploadPrefix = other.uploadPrefix; + this.secure = other.secure; + this.privateCdn = other.privateCdn; + this.cdnSubdomain = other.cdnSubdomain; + this.shorten = other.shorten; + } + + public Configuration(Map config) { + update(config); + } + + public void update(Map config) { + this.cloudName = (String) config.get("cloud_name"); + this.apiKey = (String) config.get("api_key"); + this.apiSecret = (String) config.get("api_secret"); + this.secureDistribution = (String) config.get("secure_distribution"); + this.cname = (String) config.get("cname"); + this.secure = asBoolean(config.get("secure"), false); + this.privateCdn = asBoolean(config.get("private_cdn"), false); + this.cdnSubdomain = asBoolean(config.get("cdn_subdomain"), false); + this.shorten = asBoolean(config.get("shorten"), false); + this.uploadPrefix = (String) config.get("upload_prefix"); + } + } + + public Cloudinary(Map config) { + this.config = new Configuration(config); + } + + public Cloudinary(Configuration config) { + this.config = new Configuration(config); + } + + public Cloudinary(String cloudinaryUrl) { + this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + } + + public Cloudinary(Context context) { + this.config = new Configuration(); + try { + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo info = packageManager.getApplicationInfo( context.getPackageName(), PackageManager.GET_META_DATA); + if (info == null|| info.metaData == null) return; + String cloudinaryUrl = (String) info.metaData.get("CLOUDINARY_URL"); + if (cloudinaryUrl == null) return; + this.config.update(parseConfigUrl(cloudinaryUrl)); + } catch (NameNotFoundException e) { + // No metadata found + } + + } + + public Url url() { + return new Url(this); + } + + public Uploader uploader() { + return new Uploader(this); + } + + public String cloudinaryApiUrl(String action, Map options) { + String cloudinary = asString(options.get("upload_prefix"), asString(this.config.uploadPrefix, "https://api.cloudinary.com")); + String cloud_name = asString(options.get("cloud_name"), asString(this.config.cloudName)); + if (cloud_name == null) + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + String resource_type = asString(options.get("resource_type"), "image"); + return TextUtils.join("/", new String[] { cloudinary, "v1_1", cloud_name, resource_type, action }); + } + + private final static SecureRandom RND = new SecureRandom(); + + public String randomPublicId() { + byte[] bytes = new byte[8]; + RND.nextBytes(bytes); + return encodeHexString(bytes); + } + + public String signedPreloadedImage(Map result) { + try { + return signedPreloadedImage(new JSONObject(result)); + } catch (JSONException e) { + throw new IllegalArgumentException("Bad result map"); + } + } + + public String signedPreloadedImage(JSONObject result) throws JSONException { + return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") + + (result.has("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); + } + + public String apiSignRequest(Map paramsToSign, String apiSecret) { + Collection params = new ArrayList(); + for (Map.Entry param : new TreeMap(paramsToSign).entrySet()) { + if (param.getValue() instanceof Collection) { + params.add(param.getKey() + "=" + TextUtils.join(",", (Collection) param.getValue())); + } else if (param.getValue() instanceof String) { + String value = (String) param.getValue(); + if (!TextUtils.isEmpty(value)) { + params.add(param.getKey() + "=" + value); + } + } + } + String to_sign = TextUtils.join("&", params); + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest((to_sign + apiSecret).getBytes()); + return encodeHexString(digest); + } + + private String encodeHexString(byte[] bytes) { + StringBuffer hex = new StringBuffer(); + for (byte aByte : bytes) { + hex.append(Integer.toHexString(0x100 | (((int) aByte) & 0xFF)).substring(1)); + } + + return hex.toString(); + } + + public String privateDownload(String publicId, String format, Map options) throws URISyntaxException { + String apiKey = Cloudinary.asString(options.get("api_key"), this.config.apiKey); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = Cloudinary.asString(options.get("api_secret"), this.config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("format", format); + params.put("attachment", options.get("attachment")); + params.put("type", options.get("type")); + for (Iterator iterator = params.values().iterator(); iterator.hasNext();) { + Object value = iterator.next(); + if (value == null || "".equals(value)) { + iterator.remove(); + } + } + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + Uri.Builder builder = Uri.parse(cloudinaryApiUrl("download", options)).buildUpon(); + for (Map.Entry param : params.entrySet()) { + builder.appendQueryParameter(param.getKey(), param.getValue().toString()); + } + return builder.toString(); + } + + protected Map parseConfigUrl(String cloudinaryUrl) { + Map params = new HashMap(); + URI cloudinaryUri = URI.create(cloudinaryUrl); + params.put("cloud_name", cloudinaryUri.getHost()); + if (cloudinaryUri.getUserInfo() != null) { + String[] creds = cloudinaryUri.getUserInfo().split(":"); + params.put("api_key", creds[0]); + params.put("api_secret", creds[1]); + } + params.put("private_cdn", !TextUtils.isEmpty(cloudinaryUri.getPath())); + params.put("secure_distribution", cloudinaryUri.getPath()); + if (cloudinaryUri.getQuery() != null) { + for (String param : cloudinaryUri.getQuery().split("&")) { + String[] keyValue = param.split("="); + try { + params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + } + return params; + } + + public static String asString(Object value) { + if (value == null) { + return null; + } else { + return value.toString(); + } + } + + public static String asString(Object value, String defaultValue) { + if (value == null) { + return defaultValue; + } else { + return value.toString(); + } + } + + public static List asArray(Object value) { + if (value == null) { + return Collections.EMPTY_LIST; + } else if (value instanceof Object[]) { + return Arrays.asList((Object[]) value); + } else if (value instanceof List) { + return (List) value; + } else { + List array = new ArrayList(); + array.add(value); + return array; + } + } + + public static Boolean asBoolean(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Boolean) { + return (Boolean) value; + } else { + return "true".equals(value); + } + } + + public static Map asMap(Object... values) { + if (values.length % 2 != 0) + throw new RuntimeException("Usage - (key, value, key, value, ...)"); + Map result = new HashMap(values.length / 2); + for (int i = 0; i < values.length; i += 2) { + result.put(values[i], values[i + 1]); + } + return result; + } + + public static Map emptyMap() { + return Collections.EMPTY_MAP; + } + + public static String encodeMap(Object arg) { + if (arg != null && arg instanceof Map) { + Map mapArg = (Map) arg; + HashSet out = new HashSet(); + for (Map.Entry entry : mapArg.entrySet()) { + out.add(entry.getKey() + "=" + entry.getValue()); + } + return TextUtils.join("|", out.toArray()); + } else if (arg == null) { + return null; + } else { + return arg.toString(); + } + } + + public static Float asFloat(Object value) { + if (value == null) { + return null; + } else if (value instanceof Float) { + return (Float) value; + } else { + return Float.parseFloat(value.toString()); + } + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java new file mode 100644 index 00000000..3efe95d5 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java @@ -0,0 +1,37 @@ +package com.cloudinary; + +import java.util.ArrayList; +import java.util.Collection; + +import android.graphics.Rect; +import android.text.TextUtils; +public class Coordinates { + + Collection coordinates = new ArrayList(); + + public Coordinates() { + } + + public Coordinates(Collection coordinates) { + this.coordinates = coordinates; + } + + public void addRect(Rect rect) { + this.coordinates.add(rect); + } + + + public Collection underlaying() { + return this.coordinates; + } + + @Override + public String toString() { + ArrayList rects = new ArrayList(); + for (Rect rect : this.coordinates) { + rects.add(rect.left + "," + rect.top + "," + rect.width() + "," + rect.height()); + } + return TextUtils.join("|", rects); + } + +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java new file mode 100644 index 00000000..0d6e5e0c --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java @@ -0,0 +1,26 @@ +package com.cloudinary; + +import java.util.List; +import java.util.Map; + +public class EagerTransformation extends Transformation { + protected String format; + + @SuppressWarnings("rawtypes") + public EagerTransformation(List transformations) { + super(transformations); + } + + public EagerTransformation() { + super(); + } + + public EagerTransformation format(String format) { + this.format = format; + return this; + } + + public String getFormat() { + return format; + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java new file mode 100644 index 00000000..4fcfebd7 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java @@ -0,0 +1,121 @@ +package com.cloudinary; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * This utility class provides an abstraction layer for sending multipart HTTP + * POST requests to a web server. + * + * @author www.codejava.net + * @author Cloudinary + */ +public class MultipartUtility { + private final String boundary; + private static final String LINE_FEED = "\r\n"; + private HttpURLConnection httpConn; + private String charset; + private OutputStream outputStream; + private PrintWriter writer; + + /** + * This constructor initializes a new HTTP POST request with content type is + * set to multipart/form-data + * + * @param requestURL + * @param charset + * @throws IOException + */ + public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { + this.charset = charset; + this.boundary = boundary; + + URL url = new URL(requestURL); + httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setDoOutput(true); // indicates POST method + httpConn.setDoInput(true); + httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + httpConn.setRequestProperty("User-Agent", Cloudinary.USER_AGENT); + outputStream = httpConn.getOutputStream(); + writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); + } + + /** + * Adds a form field to the request + * + * @param name + * field name + * @param value + * field value + */ + public void addFormField(String name, String value) { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); + writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); + writer.append(LINE_FEED); + writer.append(value).append(LINE_FEED); + writer.flush(); + } + + /** + * Adds a upload file section to the request + * + * @param fieldName + * name attribute in + * @param uploadFile + * a File to be uploaded + * @throws IOException + */ + public void addFilePart(String fieldName, File uploadFile) throws IOException { + String fileName = uploadFile.getName(); + FileInputStream inputStream = new FileInputStream(uploadFile); + addFilePart(fieldName, inputStream, fileName); + } + + public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); + writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED); + writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); + writer.append(LINE_FEED); + writer.flush(); + + byte[] buffer = new byte[4096]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + inputStream.close(); + + writer.append(LINE_FEED); + writer.flush(); + } + + public void addFilePart(String fieldName, InputStream inputStream) throws IOException { + addFilePart(fieldName, inputStream, "file"); + } + + /** + * Completes the request and receives response from the server. + * + * @return a list of Strings as response in case the server returned status + * OK, otherwise an exception is thrown. + * @throws IOException + */ + public HttpURLConnection execute() throws IOException { + writer.append("--" + boundary + "--").append(LINE_FEED); + writer.close(); + + return httpConn; + } + +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java b/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java new file mode 100644 index 00000000..8c72f9fd --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java @@ -0,0 +1,14 @@ +package com.cloudinary; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +public class SmartUrlEncoder { + public static String encode(String input) { + try { + return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Transformation.java b/cloudinary-android/src/main/java/com/cloudinary/Transformation.java new file mode 100644 index 00000000..5fc4bb0f --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Transformation.java @@ -0,0 +1,206 @@ +package com.cloudinary; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import android.text.TextUtils; + +@SuppressWarnings({"rawtypes","unchecked"}) +public class Transformation { + protected Map transformation; + protected List transformations; + protected String htmlWidth; + protected String htmlHeight; + + public Transformation(Transformation transformation) { + this(dup(transformation.transformations)); + } + + // Warning: options will destructively updated! + public Transformation(List transformations) { + this.transformations = transformations; + if (transformations.isEmpty()) { + chain(); + } else { + this.transformation = transformations.get(transformations.size() - 1); + } + } + + public Transformation() { + this.transformations = new ArrayList(); + chain(); + } + + public Transformation width(Object value) { return param("width", value); } + public Transformation height(Object value) { return param("height", value); } + public Transformation named(String...value) { return param("transformation", value); } + public Transformation crop(String value) { return param("crop", value); } + public Transformation background(String value) { return param("background", value); } + public Transformation color(String value) { return param("color", value); } + public Transformation effect(String value) { return param("effect", value); } + public Transformation effect(String effect, Object param) { return param("effect", effect + ":" + param); } + public Transformation angle(int value) { return param("angle", value); } + public Transformation angle(String...value) { return param("angle", value); } + public Transformation border(String value) { return param("border", value); } + public Transformation border(int width, String color) { return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); } + public Transformation x(Object value) { return param("x", value); } + public Transformation y(Object value) { return param("y", value); } + public Transformation radius(Object value) { return param("radius", value); } + public Transformation quality(Object value) { return param("quality", value); } + public Transformation defaultImage(String value) { return param("default_image", value); } + public Transformation gravity(String value) { return param("gravity", value); } + public Transformation colorSpace(String value) { return param("color_space", value); } + public Transformation prefix(String value) { return param("prefix", value); } + public Transformation overlay(String value) { return param("overlay", value); } + public Transformation underlay(String value) { return param("underlay", value); } + public Transformation fetchFormat(String value) { return param("fetch_format", value); } + public Transformation density(Object value) { return param("density", value); } + public Transformation page(Object value) { return param("page", value); } + public Transformation delay(Object value) { return param("delay", value); } + public Transformation rawTransformation(String value) { return param("raw_transformation", value); } + public Transformation flags(String...value) { return param("flags", value); } + + // Warning: options will destructively updated! + public Transformation params(Map transformation) { + this.transformation = transformation; + transformations.add(transformation); + return this; + } + + public Transformation chain() { + return params(new HashMap()); + } + + public Transformation param(String key, Object value) { + transformation.put(key, value); + return this; + } + + public String generate() { + return generate(transformations); + } + + public String generate(Iterable optionsList) { + List components = new ArrayList(); + for (Map options : optionsList) { + components.add(generate(options)); + } + return TextUtils.join("/", components); + } + + public String generate(Map options) { + String size = (String) options.get("size"); + if (size != null) { + String[] size_components = size.split("x"); + options.put("width", size_components[0]); + options.put("height", size_components[1]); + } + String width = this.htmlWidth = Cloudinary.asString(options.get("width")); + String height = this.htmlHeight = Cloudinary.asString(options.get("height")); + boolean has_layer = !TextUtils.isEmpty((String) options.get("overlay")) || + !TextUtils.isEmpty((String) options.get("underlay")); + + String crop = (String) options.get("crop"); + String angle = TextUtils.join(".", Cloudinary.asArray(options.get("angle"))); + + boolean no_html_sizes = has_layer || !TextUtils.isEmpty(angle) || "fit".equals(crop) || "limit".equals(crop); + if (width != null && (Float.parseFloat(width) < 1 || no_html_sizes)) { + this.htmlWidth = null; + } + if (height != null && (Float.parseFloat(height) < 1 || no_html_sizes)) { + this.htmlHeight = null; + } + + String background = (String) options.get("background"); + if (background != null) { + background = background.replaceFirst("^#", "rgb:"); + } + + String color = (String) options.get("color"); + if (color != null) { + color = color.replaceFirst("^#", "rgb:"); + } + + List transformations = Cloudinary.asArray(options.get("transformation")); + boolean allNamed = true; + for (Object baseTransformation : transformations) { + if (baseTransformation instanceof Map) { + allNamed = false; + break; + } + } + String namedTransformation = null; + if (allNamed) { + namedTransformation = TextUtils.join(".", transformations); + transformations = new ArrayList(); + } else { + List ts = transformations; + transformations = new ArrayList(); + for (Object baseTransformation : ts) { + String transformationString; + if (baseTransformation instanceof Map) { + transformationString = generate((Map) baseTransformation); + } else { + Map map = new HashMap(); + map.put("transformation", baseTransformation); + transformationString = generate(map); + } + transformations.add(transformationString); + } + } + + String flags = TextUtils.join(".", Cloudinary.asArray(options.get("flags"))); + + SortedMap params = new TreeMap(); + params.put("w", width); + params.put("h", height); + params.put("t", namedTransformation); + params.put("c", crop); + params.put("b", background); + params.put("co", color); + params.put("a", angle); + params.put("fl", flags); + String[] simple_params = new String[]{ + "x", "x", "y", "y", "r", "radius", "d", "default_image", "g", "gravity", "cs", "color_space", + "p", "prefix", "l", "overlay", "u", "underlay", "f", "fetch_format", "dn", "density", + "pg", "page", "dl", "delay", "e", "effect", "bo", "border", "q", "quality" + }; + for (int i = 0; i < simple_params.length; i+=2) { + params.put(simple_params[i], Cloudinary.asString(options.get(simple_params[i+1]))); + } + List components = new ArrayList(); + for (Map.Entry param : params.entrySet()) { + if (!TextUtils.isEmpty(param.getValue())) { + components.add(param.getKey() + "_" + param.getValue()); + } + } + String raw_transformation = (String) options.get("raw_transformation"); + if (raw_transformation != null) { + components.add(raw_transformation); + } + if (!components.isEmpty()) { + transformations.add(TextUtils.join(",", components)); + } + return TextUtils.join("/", transformations); + } + + public String getHtmlWidth() { + return htmlWidth; + } + + public String getHtmlHeight() { + return htmlHeight; + } + + private static List dup(List transformations) { + List result = new ArrayList(); + for (Map params : transformations) { + result.add(new HashMap(params)); + } + return result; + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Uploader.java b/cloudinary-android/src/main/java/com/cloudinary/Uploader.java new file mode 100644 index 00000000..47401165 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Uploader.java @@ -0,0 +1,336 @@ +package com.cloudinary; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.text.TextUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class Uploader { + private final Cloudinary cloudinary; + + public Uploader(Cloudinary cloudinary) { + this.cloudinary = cloudinary; + } + 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"}; + + public Map buildUploadParams(Map options) { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("public_id", (String) options.get("public_id")); + params.put("callback", (String) options.get("callback")); + params.put("format", (String) options.get("format")); + params.put("type", (String) options.get("type")); + for (String attr : BOOLEAN_UPLOAD_OPTIONS) { + Boolean value = Cloudinary.asBoolean(options.get(attr), null); + if (value != null) + params.put(attr, value.toString()); + } + params.put("notification_url", (String) options.get("notification_url")); + params.put("eager_notification_url", (String) options.get("eager_notification_url")); + params.put("proxy", (String) options.get("proxy")); + params.put("folder", (String) options.get("folder")); + params.put("allowed_formats", TextUtils.join(",", Cloudinary.asArray(options.get("allowed_formats")))); + params.put("moderation", options.get("moderation")); + params.put("upload_preset", (String) options.get("upload_preset")); + if (options.get("signature") == null) { + params.put("eager", buildEager((List) options.get("eager"))); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + Util.processWriteParameters(options, params); + } else { + params.put("eager", (String) options.get("eager")); + params.put("transformation", (String) options.get("transformation")); + params.put("headers", (String) options.get("headers")); + params.put("tags", (String) options.get("tags")); + params.put("face_coordinates", (String) options.get("face_coordinates")); + params.put("context", (String) options.get("context")); + params.put("ocr", (String) options.get("ocr")); + params.put("raw_convert", (String) options.get("raw_convert")); + params.put("categorization", (String) options.get("categorization")); + params.put("detection", (String) options.get("detection")); + params.put("similarity_search", (String) options.get("similarity_search")); + params.put("auto_tagging", (String) options.get("auto_tagging")); + } + + return params; + } + + public JSONObject upload(Object file, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = buildUploadParams(options); + return callApi("upload", params, options, file); + } + + public JSONObject unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + options = new HashMap(options); + options.put("upload_preset", uploadPreset); + options.put("unsigned", true); + return upload(file, options); + } + + public JSONObject destroy(String publicId, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("public_id", publicId); + params.put("invalidate", Cloudinary.asBoolean(options.get("invalidate"), false).toString()); + return callApi("destroy", params, options, null); + } + + public JSONObject rename(String fromPublicId, String toPublicId, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("overwrite", Cloudinary.asBoolean(options.get("overwrite"), false).toString()); + params.put("from_public_id", fromPublicId); + params.put("to_public_id", toPublicId); + return callApi("rename", params, options, null); + } + + public JSONObject explicit(String publicId, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("callback", (String) options.get("callback")); + params.put("type", (String) options.get("type")); + params.put("eager", buildEager((List) options.get("eager"))); + params.put("headers", Util.buildCustomHeaders(options.get("headers"))); + params.put("tags", TextUtils.join(",", Cloudinary.asArray(options.get("tags")))); + return callApi("explicit", params, options, null); + } + + public JSONObject generate_sprite(String tag, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + Object transParam = options.get("transformation"); + Transformation transformation = null; + if (transParam instanceof Transformation) { + transformation = new Transformation((Transformation) transParam); + } else if (transParam instanceof String) { + transformation = new Transformation().rawTransformation((String) transParam); + } else { + transformation = new Transformation(); + } + String format = (String) options.get("format"); + if (format != null) { + transformation.fetchFormat(format); + } + params.put("transformation", transformation.generate()); + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); + return callApi("sprite", params, options, null); + } + + public JSONObject multi(String tag, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); + return callApi("multi", params, options, null); + } + + public JSONObject explode(String public_id, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("public_id", public_id); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + return callApi("explode", params, options, null); + } + + // options may include 'exclusive' (boolean) which causes clearing this tag + // from all other resources + public JSONObject addTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + boolean exclusive = Cloudinary.asBoolean(options.get("exclusive"), false); + String command = exclusive ? "set_exclusive" : "add"; + return callTagsApi(tag, command, publicIds, options); + } + + public JSONObject removeTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + return callTagsApi(tag, "remove", publicIds, options); + } + + public JSONObject replaceTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + return callTagsApi(tag, "replace", publicIds, options); + } + + public JSONObject callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("tag", tag); + params.put("command", command); + params.put("type", (String) options.get("type")); + params.put("public_ids", Arrays.asList(publicIds)); + return callApi("tags", params, options, null); + } + + private final static String[] TEXT_PARAMS = { "public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", + "font_style", "background", "opacity", "text_decoration" }; + + public JSONObject text(String text, Map options) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + Map params = new HashMap(); + params.put("text", text); + for (String param : TEXT_PARAMS) { + params.put(param, Cloudinary.asString(options.get(param))); + } + return callApi("text", params, options, null); + } + + public JSONObject callApi(String action, Map params, Map options, Object file) throws IOException { + if (options == null) options = Cloudinary.emptyMap(); + boolean returnError = Cloudinary.asBoolean(options.get("return_error"), false); + String apiKey = Cloudinary.asString(options.get("api_key"), this.cloudinary.config.apiKey); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + + if (Boolean.TRUE.equals(options.get("unsigned"))) { + // Nothing to do + } else if (options.containsKey("signature") && options.containsKey("timestamp")) { + params.put("timestamp", options.get("timestamp")); + params.put("signature", options.get("signature")); + params.put("api_key", apiKey); + } else { + String apiSecret = Cloudinary.asString(options.get("api_secret"), this.cloudinary.config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + } + + String apiUrl = cloudinary.cloudinaryApiUrl(action, options); + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", cloudinary.randomPublicId()); + + // Remove blank parameters + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof String || param.getValue() instanceof Integer) { + String value = Cloudinary.asString(param.getValue()); + if (!TextUtils.isEmpty(value)) { + multipart.addFormField(param.getKey(), value); + } + } else if (param.getValue() instanceof Collection) { + for (Object value : (Collection) param.getValue()) { + multipart.addFormField(param.getKey()+"[]", Cloudinary.asString(value)); + } + } + } + + if (file instanceof String && !((String) file).matches("https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) file); + } + if (file instanceof File) { + multipart.addFilePart("file", (File) file); + } else if (file instanceof String) { + multipart.addFormField("file", (String) file); + } else if (file instanceof InputStream) { + multipart.addFilePart("file", (InputStream) file); + } + HttpURLConnection connection = multipart.execute(); + int code; + try { + code = connection.getResponseCode(); + } catch (IOException e) { + if (e.getMessage().equals("No authentication challenges found")) { + // Android trying to be clever... + code = 401; + } else { + throw e; + } + } + InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); + String responseData = readFully(responseStream); + connection.disconnect(); + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); + } + + JSONObject result; + try { + result = new JSONObject(responseData); + if (result.has("error")) { + JSONObject error = result.getJSONObject("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException(error.getString("message")); + } + } + return result; + } catch (JSONException e) { + throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); + } + } + + protected static String readFully(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = in.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return new String(baos.toByteArray()); + } + + protected String buildEager(List transformations) { + if (transformations == null) { + return null; + } + List eager = new ArrayList(); + for (Transformation transformation : transformations) { + List single_eager = new ArrayList(); + String transformationString = transformation.generate(); + if (!TextUtils.isEmpty(transformationString)) { + single_eager.add(transformationString); + } + if (transformation instanceof EagerTransformation) { + EagerTransformation eagerTransformation = (EagerTransformation) transformation; + if (!TextUtils.isEmpty(eagerTransformation.getFormat())) { + single_eager.add(eagerTransformation.getFormat()); + } + } + eager.add(TextUtils.join("/", single_eager)); + } + return TextUtils.join("|", eager); + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Url.java b/cloudinary-android/src/main/java/com/cloudinary/Url.java new file mode 100644 index 00000000..063d15c3 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Url.java @@ -0,0 +1,262 @@ +package com.cloudinary; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.CRC32; + +import android.text.TextUtils; +import android.util.Base64; + +import com.cloudinary.Cloudinary.Configuration; + +public class Url { + private final Configuration config; + boolean shorten; + String publicId = null; + String type = "upload"; + String resourceType = "image"; + String format = null; + String version = null; + Transformation transformation = null; + boolean signUrl; + + public Url(Cloudinary cloudinary) { + this.config = new Configuration(cloudinary.config); + } + + private static Pattern identifierPattern = Pattern.compile( + "^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); + /** + * Parses a cloudinary identifier of the form: + * [/][/][v/][.][#] + */ + public Url fromIdentifier(String identifier) { + Matcher matcher = identifierPattern.matcher(identifier); + if (!matcher.matches()) { + throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); + } + + String resourceType = matcher.group(1); + if (resourceType != null) { + resourceType(resourceType); + } + + String type = matcher.group(2); + if (type != null) { + type(type); + } + + String version = matcher.group(3); + if (version != null) { + version(version); + } + + String publicId = matcher.group(4); + if (publicId != null) { + publicId(publicId); + } + + String format = matcher.group(5); + if (format != null) { + format(format); + } + + // Signature (group 6) is not used + + return this; + } + + public Url type(String type) { + this.type = type; + return this; + } + + public Url resourcType(String resourceType) { + return resourceType(resourceType); + } + + public Url resourceType(String resourceType) { + this.resourceType = resourceType; + return this; + } + + public Url publicId(Object publicId) { + this.publicId = Cloudinary.asString(publicId); + return this; + } + + public Url format(String format) { + this.format = format; + return this; + } + + public Url cloudName(String cloudName) { + this.config.cloudName = cloudName; + return this; + } + + public Url secureDistribution(String secureDistribution) { + this.config.secureDistribution = secureDistribution; + return this; + } + + public Url cname(String cname) { + this.config.cname = cname; + return this; + } + + public Url version(Object version) { + this.version = Cloudinary.asString(version); + return this; + } + + public Url transformation(Transformation transformation) { + this.transformation = transformation; + return this; + } + + public Url secure(boolean secure) { + this.config.secure = secure; + return this; + } + + public Url privateCdn(boolean privateCdn) { + this.config.privateCdn = privateCdn; + return this; + } + + public Url cdnSubdomain(boolean cdnSubdomain) { + this.config.cdnSubdomain = cdnSubdomain; + return this; + } + + public Url shorten(boolean shorten) { + this.config.shorten = shorten; + return this; + } + + public Transformation transformation() { + if (this.transformation == null) + this.transformation = new Transformation(); + return this.transformation; + } + + public Url signed(boolean signUrl) { + this.signUrl = signUrl; + return this; + } + + public String generate() { + return generate(null); + } + + public String generate(String source) { + if (type.equals("fetch") && !TextUtils.isEmpty(format)) { + transformation().fetchFormat(format); + this.format = null; + } + String transformationStr = transformation().generate(); + if (TextUtils.isEmpty(this.config.cloudName)) { + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + } + + if (source == null) { + if (publicId == null) { + return null; + } + source = publicId; + } + String original_source = source; + + if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { + if ("upload".equals(type) || "asset".equals(type)) { + return original_source; + } + source = SmartUrlEncoder.encode(source); + } else { + try { + source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + if (format != null) source = source + "." + format; + } + String prefix; + boolean sharedDomain = !config.privateCdn; + if (config.secure) { + if (TextUtils.isEmpty(config.secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(config.secureDistribution)) { + config.secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + } + sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(config.secureDistribution) ; + prefix = "https://" + config.secureDistribution; + } else { + CRC32 crc32 = new CRC32(); + crc32.update(source.getBytes()); + String subdomain = config.cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; + String host = config.cname != null ? config.cname : (config.privateCdn ? config.cloudName + "-" : "") + "res.cloudinary.com"; + prefix = "http://" + subdomain + host; + } + if (sharedDomain) prefix = prefix + "/" + config.cloudName; + + if (config.shorten && resourceType.equals("image") && type.equals("upload")) { + resourceType = "iu"; + type = ""; + } + + if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && TextUtils.isEmpty(version)) { + version = "1"; + } + + if (version == null) + version = ""; + else + version = "v" + version; + + String rest = TextUtils.join("/", new String[] {transformationStr, version, source }); + rest = rest.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); + + if (signUrl) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } + catch(NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest((rest + this.config.apiSecret).getBytes()); + String signature = Base64.encodeToString(digest, Base64.NO_PADDING | Base64.URL_SAFE); + rest = "s--" + signature.substring(0, 8) + "--/" + rest; + } + + return TextUtils.join("/", new String[] { prefix, resourceType, type, rest }).replaceAll("([^:])\\/+", "$1/"); + } + + @SuppressWarnings("unchecked") + public String imageTag(String source) { + return imageTag(source, Cloudinary.emptyMap()); + } + + public String imageTag(String source, Map attributes) { + String url = generate(source); + attributes = new TreeMap(attributes); // Make sure they are ordered. + if (transformation().getHtmlHeight() != null) + attributes.put("height", transformation().getHtmlHeight()); + if (transformation().getHtmlWidth() != null) + attributes.put("width", transformation().getHtmlWidth()); + StringBuilder builder = new StringBuilder(); + builder.append(" attr : attributes.entrySet()) { + builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); + } + builder.append("/>"); + return builder.toString(); + } +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Util.java b/cloudinary-android/src/main/java/com/cloudinary/Util.java new file mode 100644 index 00000000..f5dbce37 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/Util.java @@ -0,0 +1,52 @@ +package com.cloudinary; + +import java.util.Map; + +import android.text.TextUtils; + +public class Util { + protected static final void processWriteParameters( + Map options, Map params) { + if (options.get("headers") != null) + params.put("headers", buildCustomHeaders(options.get("headers"))); + if (options.get("tags") != null) + params.put("tags", TextUtils.join(",", + Cloudinary.asArray(options.get("tags")))); + if (options.get("face_coordinates") != null) + params.put("face_coordinates", options.get("face_coordinates") + .toString()); + if (options.get("context") != null) + params.put("context", Cloudinary.encodeMap(options.get("context"))); + if (options.get("ocr") != null) + params.put("ocr", options.get("ocr")); + if (options.get("raw_convert") != null) + params.put("raw_convert", options.get("raw_convert")); + if (options.get("categorization") != null) + params.put("categorization", options.get("categorization")); + if (options.get("detection") != null) + params.put("detection", options.get("detection")); + if (options.get("similarity_search") != null) + params.put("similarity_search", options.get("similarity_search")); + if (options.get("auto_tagging") != null) + params.put("auto_tagging", + Cloudinary.asFloat(options.get("auto_tagging"))); + } + + protected static final String buildCustomHeaders(Object headers) { + if (headers == null) { + return null; + } else if (headers instanceof String) { + return (String) headers; + } else if (headers instanceof Object[]) { + return TextUtils.join("\n", (Object[]) headers) + "\n"; + } else { + Map headersMap = (Map) headers; + StringBuilder builder = new StringBuilder(); + for (Map.Entry header : headersMap.entrySet()) { + builder.append(header.getKey()).append(": ") + .append(header.getValue()).append("\n"); + } + return builder.toString(); + } + } +} diff --git a/pom.xml b/pom.xml index 164a8661..637917d5 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ cloudinary-core + cloudinary-android cloudinary-taglib cloudinary-http42 From b8c4d94c7457404950cccc4fcf0c095f9b1d554f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 9 Sep 2014 14:37:07 +0300 Subject: [PATCH 005/592] removed shared classes --- .../main/java/com/cloudinary/Coordinates.java | 37 --- .../com/cloudinary/EagerTransformation.java | 26 -- .../java/com/cloudinary/MultipartUtility.java | 121 -------- .../java/com/cloudinary/SmartUrlEncoder.java | 14 - .../java/com/cloudinary/Transformation.java | 206 -------------- .../src/main/java/com/cloudinary/Url.java | 262 ------------------ .../src/main/java/com/cloudinary/Util.java | 52 ---- 7 files changed, 718 deletions(-) delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/Coordinates.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/Transformation.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/Url.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/Util.java diff --git a/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java deleted file mode 100644 index 3efe95d5..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/Coordinates.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.cloudinary; - -import java.util.ArrayList; -import java.util.Collection; - -import android.graphics.Rect; -import android.text.TextUtils; -public class Coordinates { - - Collection coordinates = new ArrayList(); - - public Coordinates() { - } - - public Coordinates(Collection coordinates) { - this.coordinates = coordinates; - } - - public void addRect(Rect rect) { - this.coordinates.add(rect); - } - - - public Collection underlaying() { - return this.coordinates; - } - - @Override - public String toString() { - ArrayList rects = new ArrayList(); - for (Rect rect : this.coordinates) { - rects.add(rect.left + "," + rect.top + "," + rect.width() + "," + rect.height()); - } - return TextUtils.join("|", rects); - } - -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java deleted file mode 100644 index 0d6e5e0c..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/EagerTransformation.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.cloudinary; - -import java.util.List; -import java.util.Map; - -public class EagerTransformation extends Transformation { - protected String format; - - @SuppressWarnings("rawtypes") - public EagerTransformation(List transformations) { - super(transformations); - } - - public EagerTransformation() { - super(); - } - - public EagerTransformation format(String format) { - this.format = format; - return this; - } - - public String getFormat() { - return format; - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java deleted file mode 100644 index 4fcfebd7..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/MultipartUtility.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.cloudinary; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; - -/** - * This utility class provides an abstraction layer for sending multipart HTTP - * POST requests to a web server. - * - * @author www.codejava.net - * @author Cloudinary - */ -public class MultipartUtility { - private final String boundary; - private static final String LINE_FEED = "\r\n"; - private HttpURLConnection httpConn; - private String charset; - private OutputStream outputStream; - private PrintWriter writer; - - /** - * This constructor initializes a new HTTP POST request with content type is - * set to multipart/form-data - * - * @param requestURL - * @param charset - * @throws IOException - */ - public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { - this.charset = charset; - this.boundary = boundary; - - URL url = new URL(requestURL); - httpConn = (HttpURLConnection) url.openConnection(); - httpConn.setDoOutput(true); // indicates POST method - httpConn.setDoInput(true); - httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); - httpConn.setRequestProperty("User-Agent", Cloudinary.USER_AGENT); - outputStream = httpConn.getOutputStream(); - writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); - } - - /** - * Adds a form field to the request - * - * @param name - * field name - * @param value - * field value - */ - public void addFormField(String name, String value) { - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); - writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); - writer.append(LINE_FEED); - writer.append(value).append(LINE_FEED); - writer.flush(); - } - - /** - * Adds a upload file section to the request - * - * @param fieldName - * name attribute in - * @param uploadFile - * a File to be uploaded - * @throws IOException - */ - public void addFilePart(String fieldName, File uploadFile) throws IOException { - String fileName = uploadFile.getName(); - FileInputStream inputStream = new FileInputStream(uploadFile); - addFilePart(fieldName, inputStream, fileName); - } - - public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); - writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED); - writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); - writer.append(LINE_FEED); - writer.flush(); - - byte[] buffer = new byte[4096]; - int bytesRead = -1; - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - outputStream.flush(); - inputStream.close(); - - writer.append(LINE_FEED); - writer.flush(); - } - - public void addFilePart(String fieldName, InputStream inputStream) throws IOException { - addFilePart(fieldName, inputStream, "file"); - } - - /** - * Completes the request and receives response from the server. - * - * @return a list of Strings as response in case the server returned status - * OK, otherwise an exception is thrown. - * @throws IOException - */ - public HttpURLConnection execute() throws IOException { - writer.append("--" + boundary + "--").append(LINE_FEED); - writer.close(); - - return httpConn; - } - -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java b/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java deleted file mode 100644 index 8c72f9fd..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/SmartUrlEncoder.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.cloudinary; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -public class SmartUrlEncoder { - public static String encode(String input) { - try { - return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Transformation.java b/cloudinary-android/src/main/java/com/cloudinary/Transformation.java deleted file mode 100644 index 5fc4bb0f..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/Transformation.java +++ /dev/null @@ -1,206 +0,0 @@ -package com.cloudinary; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import android.text.TextUtils; - -@SuppressWarnings({"rawtypes","unchecked"}) -public class Transformation { - protected Map transformation; - protected List transformations; - protected String htmlWidth; - protected String htmlHeight; - - public Transformation(Transformation transformation) { - this(dup(transformation.transformations)); - } - - // Warning: options will destructively updated! - public Transformation(List transformations) { - this.transformations = transformations; - if (transformations.isEmpty()) { - chain(); - } else { - this.transformation = transformations.get(transformations.size() - 1); - } - } - - public Transformation() { - this.transformations = new ArrayList(); - chain(); - } - - public Transformation width(Object value) { return param("width", value); } - public Transformation height(Object value) { return param("height", value); } - public Transformation named(String...value) { return param("transformation", value); } - public Transformation crop(String value) { return param("crop", value); } - public Transformation background(String value) { return param("background", value); } - public Transformation color(String value) { return param("color", value); } - public Transformation effect(String value) { return param("effect", value); } - public Transformation effect(String effect, Object param) { return param("effect", effect + ":" + param); } - public Transformation angle(int value) { return param("angle", value); } - public Transformation angle(String...value) { return param("angle", value); } - public Transformation border(String value) { return param("border", value); } - public Transformation border(int width, String color) { return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); } - public Transformation x(Object value) { return param("x", value); } - public Transformation y(Object value) { return param("y", value); } - public Transformation radius(Object value) { return param("radius", value); } - public Transformation quality(Object value) { return param("quality", value); } - public Transformation defaultImage(String value) { return param("default_image", value); } - public Transformation gravity(String value) { return param("gravity", value); } - public Transformation colorSpace(String value) { return param("color_space", value); } - public Transformation prefix(String value) { return param("prefix", value); } - public Transformation overlay(String value) { return param("overlay", value); } - public Transformation underlay(String value) { return param("underlay", value); } - public Transformation fetchFormat(String value) { return param("fetch_format", value); } - public Transformation density(Object value) { return param("density", value); } - public Transformation page(Object value) { return param("page", value); } - public Transformation delay(Object value) { return param("delay", value); } - public Transformation rawTransformation(String value) { return param("raw_transformation", value); } - public Transformation flags(String...value) { return param("flags", value); } - - // Warning: options will destructively updated! - public Transformation params(Map transformation) { - this.transformation = transformation; - transformations.add(transformation); - return this; - } - - public Transformation chain() { - return params(new HashMap()); - } - - public Transformation param(String key, Object value) { - transformation.put(key, value); - return this; - } - - public String generate() { - return generate(transformations); - } - - public String generate(Iterable optionsList) { - List components = new ArrayList(); - for (Map options : optionsList) { - components.add(generate(options)); - } - return TextUtils.join("/", components); - } - - public String generate(Map options) { - String size = (String) options.get("size"); - if (size != null) { - String[] size_components = size.split("x"); - options.put("width", size_components[0]); - options.put("height", size_components[1]); - } - String width = this.htmlWidth = Cloudinary.asString(options.get("width")); - String height = this.htmlHeight = Cloudinary.asString(options.get("height")); - boolean has_layer = !TextUtils.isEmpty((String) options.get("overlay")) || - !TextUtils.isEmpty((String) options.get("underlay")); - - String crop = (String) options.get("crop"); - String angle = TextUtils.join(".", Cloudinary.asArray(options.get("angle"))); - - boolean no_html_sizes = has_layer || !TextUtils.isEmpty(angle) || "fit".equals(crop) || "limit".equals(crop); - if (width != null && (Float.parseFloat(width) < 1 || no_html_sizes)) { - this.htmlWidth = null; - } - if (height != null && (Float.parseFloat(height) < 1 || no_html_sizes)) { - this.htmlHeight = null; - } - - String background = (String) options.get("background"); - if (background != null) { - background = background.replaceFirst("^#", "rgb:"); - } - - String color = (String) options.get("color"); - if (color != null) { - color = color.replaceFirst("^#", "rgb:"); - } - - List transformations = Cloudinary.asArray(options.get("transformation")); - boolean allNamed = true; - for (Object baseTransformation : transformations) { - if (baseTransformation instanceof Map) { - allNamed = false; - break; - } - } - String namedTransformation = null; - if (allNamed) { - namedTransformation = TextUtils.join(".", transformations); - transformations = new ArrayList(); - } else { - List ts = transformations; - transformations = new ArrayList(); - for (Object baseTransformation : ts) { - String transformationString; - if (baseTransformation instanceof Map) { - transformationString = generate((Map) baseTransformation); - } else { - Map map = new HashMap(); - map.put("transformation", baseTransformation); - transformationString = generate(map); - } - transformations.add(transformationString); - } - } - - String flags = TextUtils.join(".", Cloudinary.asArray(options.get("flags"))); - - SortedMap params = new TreeMap(); - params.put("w", width); - params.put("h", height); - params.put("t", namedTransformation); - params.put("c", crop); - params.put("b", background); - params.put("co", color); - params.put("a", angle); - params.put("fl", flags); - String[] simple_params = new String[]{ - "x", "x", "y", "y", "r", "radius", "d", "default_image", "g", "gravity", "cs", "color_space", - "p", "prefix", "l", "overlay", "u", "underlay", "f", "fetch_format", "dn", "density", - "pg", "page", "dl", "delay", "e", "effect", "bo", "border", "q", "quality" - }; - for (int i = 0; i < simple_params.length; i+=2) { - params.put(simple_params[i], Cloudinary.asString(options.get(simple_params[i+1]))); - } - List components = new ArrayList(); - for (Map.Entry param : params.entrySet()) { - if (!TextUtils.isEmpty(param.getValue())) { - components.add(param.getKey() + "_" + param.getValue()); - } - } - String raw_transformation = (String) options.get("raw_transformation"); - if (raw_transformation != null) { - components.add(raw_transformation); - } - if (!components.isEmpty()) { - transformations.add(TextUtils.join(",", components)); - } - return TextUtils.join("/", transformations); - } - - public String getHtmlWidth() { - return htmlWidth; - } - - public String getHtmlHeight() { - return htmlHeight; - } - - private static List dup(List transformations) { - List result = new ArrayList(); - for (Map params : transformations) { - result.add(new HashMap(params)); - } - return result; - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Url.java b/cloudinary-android/src/main/java/com/cloudinary/Url.java deleted file mode 100644 index 063d15c3..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/Url.java +++ /dev/null @@ -1,262 +0,0 @@ -package com.cloudinary; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.CRC32; - -import android.text.TextUtils; -import android.util.Base64; - -import com.cloudinary.Cloudinary.Configuration; - -public class Url { - private final Configuration config; - boolean shorten; - String publicId = null; - String type = "upload"; - String resourceType = "image"; - String format = null; - String version = null; - Transformation transformation = null; - boolean signUrl; - - public Url(Cloudinary cloudinary) { - this.config = new Configuration(cloudinary.config); - } - - private static Pattern identifierPattern = Pattern.compile( - "^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + - "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); - /** - * Parses a cloudinary identifier of the form: - * [/][/][v/][.][#] - */ - public Url fromIdentifier(String identifier) { - Matcher matcher = identifierPattern.matcher(identifier); - if (!matcher.matches()) { - throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); - } - - String resourceType = matcher.group(1); - if (resourceType != null) { - resourceType(resourceType); - } - - String type = matcher.group(2); - if (type != null) { - type(type); - } - - String version = matcher.group(3); - if (version != null) { - version(version); - } - - String publicId = matcher.group(4); - if (publicId != null) { - publicId(publicId); - } - - String format = matcher.group(5); - if (format != null) { - format(format); - } - - // Signature (group 6) is not used - - return this; - } - - public Url type(String type) { - this.type = type; - return this; - } - - public Url resourcType(String resourceType) { - return resourceType(resourceType); - } - - public Url resourceType(String resourceType) { - this.resourceType = resourceType; - return this; - } - - public Url publicId(Object publicId) { - this.publicId = Cloudinary.asString(publicId); - return this; - } - - public Url format(String format) { - this.format = format; - return this; - } - - public Url cloudName(String cloudName) { - this.config.cloudName = cloudName; - return this; - } - - public Url secureDistribution(String secureDistribution) { - this.config.secureDistribution = secureDistribution; - return this; - } - - public Url cname(String cname) { - this.config.cname = cname; - return this; - } - - public Url version(Object version) { - this.version = Cloudinary.asString(version); - return this; - } - - public Url transformation(Transformation transformation) { - this.transformation = transformation; - return this; - } - - public Url secure(boolean secure) { - this.config.secure = secure; - return this; - } - - public Url privateCdn(boolean privateCdn) { - this.config.privateCdn = privateCdn; - return this; - } - - public Url cdnSubdomain(boolean cdnSubdomain) { - this.config.cdnSubdomain = cdnSubdomain; - return this; - } - - public Url shorten(boolean shorten) { - this.config.shorten = shorten; - return this; - } - - public Transformation transformation() { - if (this.transformation == null) - this.transformation = new Transformation(); - return this.transformation; - } - - public Url signed(boolean signUrl) { - this.signUrl = signUrl; - return this; - } - - public String generate() { - return generate(null); - } - - public String generate(String source) { - if (type.equals("fetch") && !TextUtils.isEmpty(format)) { - transformation().fetchFormat(format); - this.format = null; - } - String transformationStr = transformation().generate(); - if (TextUtils.isEmpty(this.config.cloudName)) { - throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); - } - - if (source == null) { - if (publicId == null) { - return null; - } - source = publicId; - } - String original_source = source; - - if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { - if ("upload".equals(type) || "asset".equals(type)) { - return original_source; - } - source = SmartUrlEncoder.encode(source); - } else { - try { - source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - if (format != null) source = source + "." + format; - } - String prefix; - boolean sharedDomain = !config.privateCdn; - if (config.secure) { - if (TextUtils.isEmpty(config.secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(config.secureDistribution)) { - config.secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; - } - sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(config.secureDistribution) ; - prefix = "https://" + config.secureDistribution; - } else { - CRC32 crc32 = new CRC32(); - crc32.update(source.getBytes()); - String subdomain = config.cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; - String host = config.cname != null ? config.cname : (config.privateCdn ? config.cloudName + "-" : "") + "res.cloudinary.com"; - prefix = "http://" + subdomain + host; - } - if (sharedDomain) prefix = prefix + "/" + config.cloudName; - - if (config.shorten && resourceType.equals("image") && type.equals("upload")) { - resourceType = "iu"; - type = ""; - } - - if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && TextUtils.isEmpty(version)) { - version = "1"; - } - - if (version == null) - version = ""; - else - version = "v" + version; - - String rest = TextUtils.join("/", new String[] {transformationStr, version, source }); - rest = rest.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); - - if (signUrl) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } - catch(NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest((rest + this.config.apiSecret).getBytes()); - String signature = Base64.encodeToString(digest, Base64.NO_PADDING | Base64.URL_SAFE); - rest = "s--" + signature.substring(0, 8) + "--/" + rest; - } - - return TextUtils.join("/", new String[] { prefix, resourceType, type, rest }).replaceAll("([^:])\\/+", "$1/"); - } - - @SuppressWarnings("unchecked") - public String imageTag(String source) { - return imageTag(source, Cloudinary.emptyMap()); - } - - public String imageTag(String source, Map attributes) { - String url = generate(source); - attributes = new TreeMap(attributes); // Make sure they are ordered. - if (transformation().getHtmlHeight() != null) - attributes.put("height", transformation().getHtmlHeight()); - if (transformation().getHtmlWidth() != null) - attributes.put("width", transformation().getHtmlWidth()); - StringBuilder builder = new StringBuilder(); - builder.append(" attr : attributes.entrySet()) { - builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); - } - builder.append("/>"); - return builder.toString(); - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Util.java b/cloudinary-android/src/main/java/com/cloudinary/Util.java deleted file mode 100644 index f5dbce37..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/Util.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.cloudinary; - -import java.util.Map; - -import android.text.TextUtils; - -public class Util { - protected static final void processWriteParameters( - Map options, Map params) { - if (options.get("headers") != null) - params.put("headers", buildCustomHeaders(options.get("headers"))); - if (options.get("tags") != null) - params.put("tags", TextUtils.join(",", - Cloudinary.asArray(options.get("tags")))); - if (options.get("face_coordinates") != null) - params.put("face_coordinates", options.get("face_coordinates") - .toString()); - if (options.get("context") != null) - params.put("context", Cloudinary.encodeMap(options.get("context"))); - if (options.get("ocr") != null) - params.put("ocr", options.get("ocr")); - if (options.get("raw_convert") != null) - params.put("raw_convert", options.get("raw_convert")); - if (options.get("categorization") != null) - params.put("categorization", options.get("categorization")); - if (options.get("detection") != null) - params.put("detection", options.get("detection")); - if (options.get("similarity_search") != null) - params.put("similarity_search", options.get("similarity_search")); - if (options.get("auto_tagging") != null) - params.put("auto_tagging", - Cloudinary.asFloat(options.get("auto_tagging"))); - } - - protected static final String buildCustomHeaders(Object headers) { - if (headers == null) { - return null; - } else if (headers instanceof String) { - return (String) headers; - } else if (headers instanceof Object[]) { - return TextUtils.join("\n", (Object[]) headers) + "\n"; - } else { - Map headersMap = (Map) headers; - StringBuilder builder = new StringBuilder(); - for (Map.Entry header : headersMap.entrySet()) { - builder.append(header.getKey()).append(": ") - .append(header.getValue()).append("\n"); - } - return builder.toString(); - } - } -} From 5f6db769da0551f60c495ab81b1a771805ba39b5 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 16 Sep 2014 14:57:45 +0300 Subject: [PATCH 006/592] changed architecture to core + strategies --- .../main/java/com/cloudinary/Cloudinary.java | 320 --------- .../UploaderStrategy.java} | 0 .../cloudinary/{api/ApiBase.java => Api.java} | 24 +- .../{CloudinaryBase.java => Cloudinary.java} | 88 ++- .../main/java/com/cloudinary/StoredFile.java | 2 +- .../{UploaderBase.java => Uploader.java} | 22 +- .../src/main/java/com/cloudinary/Url.java | 8 +- .../src/main/java/com/cloudinary/Util.java | 2 + .../strategies/AbstractApiStrategy.java | 16 + .../strategies/AbstractUploaderStrategy.java | 21 + .../AbstractUrlBuilderStrategy.java | 18 + .../cloudinary/strategies/StrategyLoader.java | 97 +++ .../utils/AbstractURLBuilderWrapper.java | 12 - .../com/cloudinary/utils/ObjectUtils.java | 38 ++ .../java/com/cloudinary/test/ApiTest.java | 646 ------------------ .../com/cloudinary/test/CloudinaryTest.java | 491 ------------- .../com/cloudinary/test/UploaderTest.java | 365 ---------- .../com/cloudinary/test/stubs/ApiStub.java | 21 - .../cloudinary/test/stubs/CloudinaryStub.java | 41 -- .../test/stubs/URLBuilderWrapperStub.java | 23 - .../cloudinary/test/stubs/UploaderStub.java | 22 - cloudinary-core/src/test/resources/docx.docx | Bin 20453 -> 0 bytes .../src/test/resources/favicon.ico | Bin 1150 -> 0 bytes cloudinary-core/src/test/resources/logo.png | Bin 3381 -> 0 bytes .../main/java/com/cloudinary/Cloudinary.java | 39 -- .../com/cloudinary/URLBuilderWrapper.java | 28 - .../{Api.java => http42/ApiStrategy.java} | 32 +- .../UploaderStrategy.java} | 22 +- .../cloudinary/http42/UrlBuilderStrategy.java | 23 + .../java/com/cloudinary/test/ApiTest.java | 6 +- .../com/cloudinary/test/CloudinaryTest.java | 1 + 31 files changed, 353 insertions(+), 2075 deletions(-) delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java rename cloudinary-android/src/main/java/com/cloudinary/{Uploader.java => android/UploaderStrategy.java} (100%) rename cloudinary-core/src/main/java/com/cloudinary/{api/ApiBase.java => Api.java} (94%) rename cloudinary-core/src/main/java/com/cloudinary/{CloudinaryBase.java => Cloudinary.java} (68%) rename cloudinary-core/src/main/java/com/cloudinary/{UploaderBase.java => Uploader.java} (94%) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java delete mode 100644 cloudinary-core/src/test/resources/docx.docx delete mode 100644 cloudinary-core/src/test/resources/favicon.ico delete mode 100644 cloudinary-core/src/test/resources/logo.png delete mode 100644 cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java delete mode 100644 cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java rename cloudinary-http42/src/main/java/com/cloudinary/{Api.java => http42/ApiStrategy.java} (81%) rename cloudinary-http42/src/main/java/com/cloudinary/{Uploader.java => http42/UploaderStrategy.java} (87%) create mode 100644 cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java diff --git a/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java deleted file mode 100644 index d57fe2cf..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/Cloudinary.java +++ /dev/null @@ -1,320 +0,0 @@ -package com.cloudinary; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.net.Uri; -import android.text.TextUtils; - -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class Cloudinary { - public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; - public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; - 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.0.2"; - public final static String USER_AGENT = "cld-android-" + VERSION; - - public final Configuration config; - - public static class Configuration { - public String cloudName; - public String apiKey; - public String apiSecret; - public String secureDistribution; - public String cname; - public String uploadPrefix; - public boolean secure = false; - public boolean privateCdn = false; - public boolean cdnSubdomain = false; - public boolean shorten = false; - - public Configuration() { - } - - public Configuration(String cloudName) { - this.cloudName = cloudName; - } - - public Configuration(Configuration other) { - this.cloudName = other.cloudName; - this.apiKey = other.apiKey; - this.apiSecret = other.apiSecret; - this.secureDistribution = other.secureDistribution; - this.cname = other.cname; - this.uploadPrefix = other.uploadPrefix; - this.secure = other.secure; - this.privateCdn = other.privateCdn; - this.cdnSubdomain = other.cdnSubdomain; - this.shorten = other.shorten; - } - - public Configuration(Map config) { - update(config); - } - - public void update(Map config) { - this.cloudName = (String) config.get("cloud_name"); - this.apiKey = (String) config.get("api_key"); - this.apiSecret = (String) config.get("api_secret"); - this.secureDistribution = (String) config.get("secure_distribution"); - this.cname = (String) config.get("cname"); - this.secure = asBoolean(config.get("secure"), false); - this.privateCdn = asBoolean(config.get("private_cdn"), false); - this.cdnSubdomain = asBoolean(config.get("cdn_subdomain"), false); - this.shorten = asBoolean(config.get("shorten"), false); - this.uploadPrefix = (String) config.get("upload_prefix"); - } - } - - public Cloudinary(Map config) { - this.config = new Configuration(config); - } - - public Cloudinary(Configuration config) { - this.config = new Configuration(config); - } - - public Cloudinary(String cloudinaryUrl) { - this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); - } - - public Cloudinary(Context context) { - this.config = new Configuration(); - try { - PackageManager packageManager = context.getPackageManager(); - ApplicationInfo info = packageManager.getApplicationInfo( context.getPackageName(), PackageManager.GET_META_DATA); - if (info == null|| info.metaData == null) return; - String cloudinaryUrl = (String) info.metaData.get("CLOUDINARY_URL"); - if (cloudinaryUrl == null) return; - this.config.update(parseConfigUrl(cloudinaryUrl)); - } catch (NameNotFoundException e) { - // No metadata found - } - - } - - public Url url() { - return new Url(this); - } - - public Uploader uploader() { - return new Uploader(this); - } - - public String cloudinaryApiUrl(String action, Map options) { - String cloudinary = asString(options.get("upload_prefix"), asString(this.config.uploadPrefix, "https://api.cloudinary.com")); - String cloud_name = asString(options.get("cloud_name"), asString(this.config.cloudName)); - if (cloud_name == null) - throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); - String resource_type = asString(options.get("resource_type"), "image"); - return TextUtils.join("/", new String[] { cloudinary, "v1_1", cloud_name, resource_type, action }); - } - - private final static SecureRandom RND = new SecureRandom(); - - public String randomPublicId() { - byte[] bytes = new byte[8]; - RND.nextBytes(bytes); - return encodeHexString(bytes); - } - - public String signedPreloadedImage(Map result) { - try { - return signedPreloadedImage(new JSONObject(result)); - } catch (JSONException e) { - throw new IllegalArgumentException("Bad result map"); - } - } - - public String signedPreloadedImage(JSONObject result) throws JSONException { - return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") - + (result.has("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); - } - - public String apiSignRequest(Map paramsToSign, String apiSecret) { - Collection params = new ArrayList(); - for (Map.Entry param : new TreeMap(paramsToSign).entrySet()) { - if (param.getValue() instanceof Collection) { - params.add(param.getKey() + "=" + TextUtils.join(",", (Collection) param.getValue())); - } else if (param.getValue() instanceof String) { - String value = (String) param.getValue(); - if (!TextUtils.isEmpty(value)) { - params.add(param.getKey() + "=" + value); - } - } - } - String to_sign = TextUtils.join("&", params); - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest((to_sign + apiSecret).getBytes()); - return encodeHexString(digest); - } - - private String encodeHexString(byte[] bytes) { - StringBuffer hex = new StringBuffer(); - for (byte aByte : bytes) { - hex.append(Integer.toHexString(0x100 | (((int) aByte) & 0xFF)).substring(1)); - } - - return hex.toString(); - } - - public String privateDownload(String publicId, String format, Map options) throws URISyntaxException { - String apiKey = Cloudinary.asString(options.get("api_key"), this.config.apiKey); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = Cloudinary.asString(options.get("api_secret"), this.config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("format", format); - params.put("attachment", options.get("attachment")); - params.put("type", options.get("type")); - for (Iterator iterator = params.values().iterator(); iterator.hasNext();) { - Object value = iterator.next(); - if (value == null || "".equals(value)) { - iterator.remove(); - } - } - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); - Uri.Builder builder = Uri.parse(cloudinaryApiUrl("download", options)).buildUpon(); - for (Map.Entry param : params.entrySet()) { - builder.appendQueryParameter(param.getKey(), param.getValue().toString()); - } - return builder.toString(); - } - - protected Map parseConfigUrl(String cloudinaryUrl) { - Map params = new HashMap(); - URI cloudinaryUri = URI.create(cloudinaryUrl); - params.put("cloud_name", cloudinaryUri.getHost()); - if (cloudinaryUri.getUserInfo() != null) { - String[] creds = cloudinaryUri.getUserInfo().split(":"); - params.put("api_key", creds[0]); - params.put("api_secret", creds[1]); - } - params.put("private_cdn", !TextUtils.isEmpty(cloudinaryUri.getPath())); - params.put("secure_distribution", cloudinaryUri.getPath()); - if (cloudinaryUri.getQuery() != null) { - for (String param : cloudinaryUri.getQuery().split("&")) { - String[] keyValue = param.split("="); - try { - params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - } - return params; - } - - public static String asString(Object value) { - if (value == null) { - return null; - } else { - return value.toString(); - } - } - - public static String asString(Object value, String defaultValue) { - if (value == null) { - return defaultValue; - } else { - return value.toString(); - } - } - - public static List asArray(Object value) { - if (value == null) { - return Collections.EMPTY_LIST; - } else if (value instanceof Object[]) { - return Arrays.asList((Object[]) value); - } else if (value instanceof List) { - return (List) value; - } else { - List array = new ArrayList(); - array.add(value); - return array; - } - } - - public static Boolean asBoolean(Object value, Boolean defaultValue) { - if (value == null) { - return defaultValue; - } else if (value instanceof Boolean) { - return (Boolean) value; - } else { - return "true".equals(value); - } - } - - public static Map asMap(Object... values) { - if (values.length % 2 != 0) - throw new RuntimeException("Usage - (key, value, key, value, ...)"); - Map result = new HashMap(values.length / 2); - for (int i = 0; i < values.length; i += 2) { - result.put(values[i], values[i + 1]); - } - return result; - } - - public static Map emptyMap() { - return Collections.EMPTY_MAP; - } - - public static String encodeMap(Object arg) { - if (arg != null && arg instanceof Map) { - Map mapArg = (Map) arg; - HashSet out = new HashSet(); - for (Map.Entry entry : mapArg.entrySet()) { - out.add(entry.getKey() + "=" + entry.getValue()); - } - return TextUtils.join("|", out.toArray()); - } else if (arg == null) { - return null; - } else { - return arg.toString(); - } - } - - public static Float asFloat(Object value) { - if (value == null) { - return null; - } else if (value instanceof Float) { - return (Float) value; - } else { - return Float.parseFloat(value.toString()); - } - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/Uploader.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java similarity index 100% rename from cloudinary-android/src/main/java/com/cloudinary/Uploader.java rename to cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java similarity index 94% rename from cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java rename to cloudinary-core/src/main/java/com/cloudinary/Api.java index 0d7cdb0b..c06043c9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/ApiBase.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -1,4 +1,4 @@ -package com.cloudinary.api; +package com.cloudinary; import java.util.ArrayList; import java.util.Arrays; @@ -8,18 +8,19 @@ import org.apache.http.conn.ClientConnectionManager; -import com.cloudinary.CloudinaryBase; -import com.cloudinary.Util; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.AuthorizationRequired; import com.cloudinary.api.exceptions.AlreadyExists; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.GeneralError; import com.cloudinary.api.exceptions.NotAllowed; import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.api.exceptions.RateLimited; +import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; @SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class ApiBase { +public class Api { public enum HttpMethod { GET, POST, PUT, DELETE } public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); @@ -33,13 +34,18 @@ public enum HttpMethod { GET, POST, PUT, DELETE } CLOUDINARY_API_ERROR_CLASSES.put(500, GeneralError.class); } - protected final CloudinaryBase cloudinary; - protected ClientConnectionManager connectionManager = null; + public final Cloudinary cloudinary; + public ClientConnectionManager connectionManager = null; + private AbstractApiStrategy strategy; - protected abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception ; + protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + return this.strategy.callApi(method,uri,params,options); + } - public ApiBase(CloudinaryBase cloudinary) { + public Api(Cloudinary cloudinary,AbstractApiStrategy strategy) { this.cloudinary = cloudinary; + this.strategy = strategy; + this.strategy.init(this); } public ApiResponse ping(Map options) throws Exception { @@ -224,7 +230,7 @@ public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); } - public ApiBase withConnectionManager(ClientConnectionManager connectionManager) { + public Api withConnectionManager(ClientConnectionManager connectionManager) { this.connectionManager = connectionManager; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java similarity index 68% rename from cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java rename to cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 171e33c4..33db0494 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/CloudinaryBase.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -7,21 +7,30 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; //import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.ClientConnectionManager; -import com.cloudinary.api.ApiBase; -import com.cloudinary.utils.AbstractURLBuilderWrapper; +import com.cloudinary.strategies.AbstractApiStrategy; +import com.cloudinary.strategies.StrategyLoader; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.strategies.AbstractUrlBuilderStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class CloudinaryBase { +public class Cloudinary { + + private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList("com.cloudinary.android.UploaderStrategy","com.cloudinary.http42.UploaderStrategy","com.cloudinary.http43.UploaderStrategy")); + private static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy" )); + private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UrlBuilderStrategy", "com.cloudinary.http42.UrlBuilderStrategy", "com.cloudinary.http43.UrlBuilderStrategy" )); + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; @@ -31,21 +40,69 @@ public abstract class CloudinaryBase { public final static String USER_AGENT = "cld-java-" + VERSION; private final Map config = new HashMap(); + private AbstractUploaderStrategy uploaderStrategy; + private AbstractApiStrategy apiStrategy; + private AbstractUrlBuilderStrategy urlBuilderStrategy; protected ClientConnectionManager connectionManager = null; - protected abstract AbstractURLBuilderWrapper urlBuilder(String source) throws Exception; - public abstract UploaderBase uploader(); - public abstract ApiBase api(); + public Uploader uploader(){ + return new Uploader(this,uploaderStrategy).withConnectionManager(connectionManager); + + }; + + public Api api(){ + return new Api(this,apiStrategy).withConnectionManager(connectionManager); + }; - public CloudinaryBase(Map config) { + public static void registerUploaderStrategy(String className){ + if (!UPLOAD_STRATEGIES.contains(className)){ + UPLOAD_STRATEGIES.add(className); + } + + } + + public static void registerAPIStrategy(String className){ + if (!API_STRATEGIES.contains(className)){ + API_STRATEGIES.add(className); + } + } + + public static void registerUrlBuilderStrategy(String className){ + if (!URLBUILDER_STRATEGIES.contains(className)){ + URLBUILDER_STRATEGIES.add(className); + } + } + + private void loadStrategies() { + uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); + if (uploaderStrategy==null){ + throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); + } + + apiStrategy= StrategyLoader.find(API_STRATEGIES); + if (apiStrategy==null){ + throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); + } + + urlBuilderStrategy= StrategyLoader.find(URLBUILDER_STRATEGIES); + if (urlBuilderStrategy==null){ + throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(URLBUILDER_STRATEGIES, ",") + "]"); + } + } + + public Cloudinary(Map config) { + loadStrategies(); this.config.putAll(config); + } - public CloudinaryBase(String cloudinaryUrl) { + public Cloudinary(String cloudinaryUrl) { + loadStrategies(); initFromUrl(cloudinaryUrl); } - public CloudinaryBase() { + public Cloudinary() { + loadStrategies(); String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); if (cloudinaryUrl != null) { initFromUrl(cloudinaryUrl); @@ -57,10 +114,9 @@ public Url url() { return new Url(this); } - - public String cloudinaryApiUrl(String action, Map options) { - String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.config.get("upload_prefix"), "https://api.cloudinary.com")); + String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), + ObjectUtils.asString(this.config.get("upload_prefix"), "https://api.cloudinary.com")); String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.get("cloud_name"))); if (cloud_name == null) throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); @@ -116,7 +172,6 @@ public void signRequest(Map params, Map options) params.put("api_key", apiKey); } - public String privateDownload(String publicId, String format, Map options) throws Exception { Map params = new HashMap(); params.put("public_id", publicId); @@ -125,7 +180,8 @@ public String privateDownload(String publicId, String format, Map param : params.entrySet()) { builder.addParam(param.getKey(), param.getValue().toString()); } @@ -145,7 +201,7 @@ public String zipDownload(String tag, Map options) throws Except } params.put("transformation", transformation); signRequest(params, options); - AbstractURLBuilderWrapper builder = urlBuilder(cloudinaryApiUrl("download_tag.zip", options)); + AbstractUrlBuilderStrategy builder= urlBuilderStrategy.init(cloudinaryApiUrl("download_tag.zip", options)); for (Map.Entry param : params.entrySet()) { builder.addParam(param.getKey(), param.getValue().toString()); } @@ -188,7 +244,7 @@ public void setConfig(String key, Object value) { this.config.put(key, value); } - public CloudinaryBase withConnectionManager(ClientConnectionManager connectionManager) { + public Cloudinary withConnectionManager(ClientConnectionManager connectionManager) { this.connectionManager = connectionManager; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java index 5d9805aa..957ef1f6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java +++ b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java @@ -108,7 +108,7 @@ public void setPreloadedFile(String uri) { } } - public String getComputedSignature(CloudinaryBase cloudinary) { + public String getComputedSignature(Cloudinary cloudinary) { Map params = new HashMap(); params.put("version", getVersion().toString()); params.put("public_id", getPublicIdForSigning()); diff --git a/cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java similarity index 94% rename from cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java rename to cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 3c126921..f8d12f4f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/UploaderBase.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -13,18 +13,28 @@ import org.apache.http.conn.ClientConnectionManager; import org.json.JSONObject; +import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class UploaderBase { - public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException; +public class Uploader { + public Map callApi(String action, Map params, Map options, Object file) throws IOException{ + return strategy.callApi(action,params,options,file); + } - protected ClientConnectionManager connectionManager = null; - protected final CloudinaryBase cloudinary; + public ClientConnectionManager connectionManager = null; + private Cloudinary cloudinary; + private AbstractUploaderStrategy strategy; - public UploaderBase(CloudinaryBase cloudinary) { + public Uploader(Cloudinary cloudinary,AbstractUploaderStrategy strategy) { this.cloudinary = cloudinary; + this.strategy = strategy; + this.strategy.init(this); + } + + public Cloudinary cloudinary(){ + return this.cloudinary; } public Map buildUploadParams(Map options) { @@ -262,7 +272,7 @@ public void signRequestParams(Map params, Map options) { cloudinary.signRequest(params, options); } - public UploaderBase withConnectionManager(ClientConnectionManager connectionManager) { + public Uploader withConnectionManager(ClientConnectionManager connectionManager) { this.connectionManager = connectionManager; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index fb1e542a..082bcc7a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -30,7 +30,7 @@ public class Url { Transformation transformation = null; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - public Url(CloudinaryBase cloudinary) { + public Url(Cloudinary cloudinary) { this.cloudName = cloudinary.getStringConfig("cloud_name"); this.secureDistribution = cloudinary.getStringConfig("secure_distribution"); this.cname = cloudinary.getStringConfig("cname"); @@ -171,10 +171,10 @@ public String generate() { String prefix; boolean sharedDomain = !privateCdn; if (secure) { - if (StringUtils.isBlank(secureDistribution) || CloudinaryBase.OLD_AKAMAI_SHARED_CDN.equals(secureDistribution)) { - secureDistribution = privateCdn ? cloudName + "-res.cloudinary.com" : CloudinaryBase.SHARED_CDN; + if (StringUtils.isBlank(secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(secureDistribution)) { + secureDistribution = privateCdn ? cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; } - sharedDomain = sharedDomain || CloudinaryBase.SHARED_CDN.equals(secureDistribution); + sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(secureDistribution); prefix = "https://" + secureDistribution; } else { CRC32 crc32 = new CRC32(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 25958eec..ad526711 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -124,4 +124,6 @@ public static void clearEmpty(Map params) { } } } + + } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java new file mode 100644 index 00000000..ca302717 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -0,0 +1,16 @@ +package com.cloudinary.strategies; + +import java.util.Map; + +import com.cloudinary.Api; +import com.cloudinary.Api.HttpMethod; +import com.cloudinary.api.ApiResponse; + +public abstract class AbstractApiStrategy { + protected Api api; + public void init(Api api){ + this.api = api; + } + @SuppressWarnings("rawtypes") + public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java new file mode 100644 index 00000000..d413bcf6 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -0,0 +1,21 @@ +package com.cloudinary.strategies; + +import java.io.IOException; +import java.util.Map; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Uploader; + +public abstract class AbstractUploaderStrategy { + protected Uploader uploader; + public void init(Uploader uploader){ + this.uploader = uploader; + } + + public Cloudinary cloudinary(){ + return this.uploader.cloudinary(); + } + + @SuppressWarnings("rawtypes") + public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException ; +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java new file mode 100644 index 00000000..4ae98aea --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java @@ -0,0 +1,18 @@ +package com.cloudinary.strategies; + + +public abstract class AbstractUrlBuilderStrategy { + protected String source; + public AbstractUrlBuilderStrategy(){ + } + + public abstract void addParam(String key, Object value); + public abstract String url(); + public abstract void initialize() throws Exception; + + public AbstractUrlBuilderStrategy init(String source) throws Exception{ + this.source = source; + initialize(); + return this; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java new file mode 100644 index 00000000..ab69008c --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java @@ -0,0 +1,97 @@ +package com.cloudinary.strategies; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import com.cloudinary.utils.StringUtils; + +public class StrategyLoader { + + @SuppressWarnings("unchecked") + public static T load(String className) { + T result = null; + try { + Class clazz = Class.forName(className); + result = (T) clazz.newInstance(); + } catch (Exception e) { + } + return result; + } + + public static T find(List strategies) { + for (int i = 0; i < strategies.size(); i++) { + T strategy = load(strategies.get(i)); + if (strategy != null) { + return strategy; + } + } + return null; + + } + + public boolean exists(List strategies) { + return find(strategies) != null; + } + + + @Deprecated + public static ArrayList getClassNamesFromPackage(String packageName) throws IOException, URISyntaxException { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + URL packageURL; + ArrayList names = new ArrayList(); + + + packageName = packageName.replace(".", "/"); + packageURL = classLoader.getResource(packageName); + + if (packageURL.getProtocol().equals("jar")) { + String jarFileName; + JarFile jf; + Enumeration jarEntries; + String entryName; + + // build jar file name, then loop through zipped entries + jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8"); + jarFileName = jarFileName.substring(5, jarFileName.indexOf("!")); + System.out.println(">" + jarFileName); + jf = new JarFile(jarFileName); + jarEntries = jf.entries(); + while (jarEntries.hasMoreElements()) { + entryName = jarEntries.nextElement().getName(); + if (entryName.startsWith(packageName) && entryName.length() > packageName.length() + 5) { + entryName = entryName.substring(packageName.length(), entryName.lastIndexOf('.')); + names.add(entryName); + } + } + + // loop through files in classpath + } else { + URI uri = new URI(packageURL.toString()); + File folder = new File(uri.getPath()); + // won't work with path which contains blank (%20) + // File folder = new File(packageURL.getFile()); + File[] contenuti = folder.listFiles(); + System.out.println("files:"); + System.out.println(StringUtils.join(contenuti, "\n")); + System.out.println("-- -- --"); + String entryName; + for (File actual : contenuti) { + entryName = actual.getName(); + System.out.println(entryName); + entryName = entryName.substring(0, entryName.lastIndexOf('.')); + names.add(entryName); + } + } + return names; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java b/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java deleted file mode 100644 index b32f8827..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/AbstractURLBuilderWrapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.cloudinary.utils; - -public abstract class AbstractURLBuilderWrapper { - - protected String source; - public AbstractURLBuilderWrapper(String source){ - this.source = source; - } - - public abstract void addParam(String key, Object value); - public abstract String url(); -} 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 06a6f8e9..e641dedf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -5,9 +5,14 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + public class ObjectUtils { public static String asString(Object value) { @@ -109,4 +114,37 @@ public static String encodeMap(Object arg) { return result; } + @SuppressWarnings("rawtypes") + public static Map toMap(JSONObject object) throws JSONException { + @SuppressWarnings("unchecked") + Map map = new HashMap(); + Iterator keys = object.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + map.put(key, fromJson(object.get(key))); + } + return map; + } + + private static Object fromJson(Object json) throws JSONException { + if (json == JSONObject.NULL) { + return null; + } else if (json instanceof JSONObject) { + return toMap((JSONObject) json); + } else if (json instanceof JSONArray) { + return toList((JSONArray) json); + } else { + return json; + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List toList(JSONArray array) throws JSONException { + List list = new ArrayList(); + for (int i = 0; i < array.length(); i++) { + list.add(fromJson(array.get(i))); + } + return list; + } + } diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java deleted file mode 100644 index 614d93ba..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/ApiTest.java +++ /dev/null @@ -1,646 +0,0 @@ -package com.cloudinary.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.json.JSONArray; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; -import com.cloudinary.api.ApiBase; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.BadRequest; -import com.cloudinary.api.exceptions.NotFound; -import com.cloudinary.test.stubs.CloudinaryStub; -import com.cloudinary.utils.ObjectUtils; - -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class ApiTest { - - private CloudinaryStub cloudinary; - private ApiBase api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); - - @BeforeClass - public static void setUpClass() throws IOException { - CloudinaryStub cloudinary = new CloudinaryStub(); - if (cloudinary.getStringConfig("api_secret") == null) { - System.err.println("Please setup environment for Upload test to run"); - return; - } - ApiBase api = cloudinary.api(); - try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", - Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - options.put("public_id", "api_test1"); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - } - - @Before - public void setUp() { - this.cloudinary = new CloudinaryStub(); - assumeNotNull(cloudinary.getStringConfig("api_secret")); - this.api = cloudinary.api(); - } - - public Map findByAttr(List elements, String attr, Object value) { - for (Map element : elements) { - if (value.equals(element.get(attr))) { - return element; - } - } - return null; - } - - @Ignore - public void test01ResourceTypes() throws Exception { - // should allow listing resource_types - Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); - } - - @Ignore - public void test02Resources() throws Exception { - // should allow listing resources - Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } - - @Ignore - public void test03ResourcesCursor() throws Exception { - // should allow listing resources with cursor - Map options = new HashMap(); - options.put("max_results", 1); - Map result = api.resources(options); - List resources = (List) result.get("resources"); - assertNotNull(resources); - assertEquals(1, resources.size()); - assertNotNull(result.get("next_cursor")); - - options.put("next_cursor", result.get("next_cursor")); - Map result2 = api.resources(options); - List resources2 = (List) result2.get("resources"); - assertNotNull(resources2); - assertEquals(resources2.size(), 1); - assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); - } - - @Ignore - public void test04ResourcesByType() throws Exception { - // should allow listing resources by type - Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - } - - @Ignore - public void test05ResourcesByPrefix() throws Exception { - // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - JSONArray tags = (JSONArray) r.get("tags"); - - found = found || contains(tags, "api_test_tag"); - } - assertTrue(found); - } - - @Ignore - public void testResourcesListingDirection() throws Exception { - // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); - List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); - List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); - } - - @Ignore - public void testResourcesListingStartAt() throws Exception { - // should allow listing resources by start date - make sure your clock - // is set correctly!!! - Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); - Thread.sleep(2000L); - Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); - List resources = (List) listResources.get("resources"); - assertEquals(response.get("public_id"), resources.get(0).get("public_id")); - } - - @Ignore - public void testResourcesByPublicIds() throws Exception { - // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - JSONArray tags = (JSONArray) r.get("tags"); - found = found || contains(tags, "api_test_tag"); - } - assertTrue(found); - } - - @Ignore - public void test06ResourcesTag() throws Exception { - // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); - assertNotNull(resource); - List resources = (List) result.get("resources"); - boolean found = false; - for (Map r : resources) { - JSONArray tags = (JSONArray) r.get("tags"); - found = found || contains(tags, "api_test_tag"); - } - assertTrue(found); - } - - @Ignore - public void test07ResourceMetadata() throws Exception { - // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); - assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); - assertEquals(resource.get("bytes"), 3381L); - assertEquals(((List) resource.get("derived")).size(), 1); - } - - @Ignore - public void test08DeleteDerived() throws Exception { - // should allow deleting derived resource - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - List derived = (List) resource.get("derived"); - assertEquals(derived.size(), 1); - String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - derived = (List) resource.get("derived"); - assertEquals(derived.size(), 0); - } - -// @Test(expected = NotFound.class) - @Ignore - public void test09DeleteResources() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); - } - -// @Test(expected = NotFound.class) - @Ignore - public void test09aDeleteResourcesByPrefix() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - } - -// @Test(expected = NotFound.class) - @Ignore - public void test09aDeleteResourcesByTags() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); - } - - @Ignore - public void test10Tags() throws Exception { - // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - } - - @Ignore - public void test11TagsPrefix() throws Exception { - // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); - tags = (List) result.get("tags"); - assertEquals(0, tags.size()); - } - - @Ignore - public void test12Transformations() throws Exception { - // should allow listing transformations - Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); - - assertNotNull(transformation); - assertTrue((Boolean) transformation.get("used")); - } - - @Ignore - public void test13TransformationMetadata() throws Exception { - // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); - } - - @Ignore - public void test14TransformationUpdate() throws Exception { - // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), false); - } - - @Ignore - public void test15TransformationCreate() throws Exception { - // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); - assertEquals(transformation.get("used"), false); - } - -// @Test - @Ignore - public void test15aTransformationUnsafeUpdate() throws Exception { - // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), - ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test16aTransformationDelete() throws Exception { - // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - -// @Test(expected = NotFound.class) - @Ignore - public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test - public void test17aTransformationDeleteImplicit() throws Exception { - // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ -// @Test(expected = NotFound.class) - @Ignore - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - -// @Test - @Ignore - public void test18Usage() throws Exception { - // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); - assertNotNull(result.get("last_updated")); - } - - @Ignore - public void test19Ping() throws Exception { - // should support ping API call - Map result = api.ping(ObjectUtils.emptyMap()); - assertEquals(result.get("status"), "ok"); - } - - // This test must be last because it deletes (potentially) all dependent - // transformations which some tests rely on. - // Add @Test if you really want to test it - This test deletes derived - // resources! - public void testDeleteAllResources() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((JSONArray) result.get("derived")).length()); - api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); - // assertEquals(0, ((JSONArray) result.get("derived")).size()); - } - -// @Test - @Ignore - public void testManualModeration() throws Exception { - // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); - } - -// @Test - @Ignore - public void testOcrUpdate() { - // should support requesting ocr info - try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Ignore - public void testRawConvertUpdate() { - // should support requesting raw conversion - try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Ignore - public void testCategorizationUpdate() { - // should support requesting categorization - try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Ignore - public void testDetectionUpdate() { - // should support requesting detection - try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Ignore - public void testSimilaritySearchUpdate() { - // should support requesting similarity search - try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Ignore - public void testUpdateCustomCoordinates() throws IOException, Exception { - // should update custom coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - long[] expected = new long[] { 121L, 31L, 110L, 151L }; - JSONArray actual = ((JSONArray) ((JSONArray) ((Map) result.get("coordinates")).get("custom")).get(0)); - for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i], actual.get(i)); - } - } - - @Ignore - public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response - // header - ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); - ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); - assertNotNull(result1.apiRateLimit()); - assertNotNull(result2.apiRateLimit()); - assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); - assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); - assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); - assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); - assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); - } - - @Ignore - public void testListUploadPresets() throws Exception { - // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); - JSONArray presets = (JSONArray) api.uploadPresets(ObjectUtils.emptyMap()).get("presets"); - assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } - - @Ignore - public void testGetUploadPreset() throws Exception { - // should allow getting a single upload_preset - String[] tags = { "a", "b", "c" }; - Map context = ObjectUtils.asMap("a", "b", "c", "d"); - Transformation transformation = new Transformation(); - transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", - context)); - 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"); - Map outTransformation = (Map) ((JSONArray) settings.get("transformation")).get(0); - assertEquals(outTransformation.get("width"), 100L); - assertEquals(outTransformation.get("crop"), "scale"); - JSONArray outTags = ((JSONArray) settings.get("tags")); - for (int i = 0; i < tags.length; i++){ - assertEquals(tags[i], outTags.get(i)); - } - Map outContext = (Map) settings.get("context"); - assertEquals(context, outContext); - } - - @Ignore - public void testDeleteUploadPreset() throws Exception { - // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - boolean error = false; - try { - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - error = true; - } - assertTrue(error); - } - - @Ignore - public void testUpdateUploadPreset() throws Exception { - // should allow updating upload_presets - 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)); - 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, preset.get("settings")); - api.deleteUploadPreset(name, ObjectUtils.emptyMap()); - } - - @Ignore - public void testListByModerationUpdate() throws Exception { - // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); - Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); - Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); - Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); - } - - // For this test to work, "Auto-create folders" should be enabled in the - // Upload Settings. - // Uncomment @Test if you really want to test it. - // @Test - public void testFolderApi() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); - Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("name")); - result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("path")); - try { - api.subFolders("test_folder", null); - } catch (Exception e) { - assertTrue(e instanceof NotFound); - } - api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); - } - - private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } - - private boolean contains(JSONArray source, String findMe) { - for (int i = 0; i < source.length(); i++) { - if (findMe.equals(source.get(i))) - return true; - } - return 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 deleted file mode 100644 index 6b2d8a47..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ /dev/null @@ -1,491 +0,0 @@ -package com.cloudinary.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import com.cloudinary.Transformation; -import com.cloudinary.test.stubs.CloudinaryStub; -import com.cloudinary.utils.ObjectUtils; - -public class CloudinaryTest { - - private CloudinaryStub cloudinary; - - @Before - public void setUp() { - this.cloudinary = new CloudinaryStub("cloudinary://a:b@test123"); - } - - @Test - public void testCloudName() { - // should use cloud_name from config - String result = cloudinary.url().generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testSecureDistribution() { - // should use default secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistributionOverwrite() { - // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); - assertEquals("https://something.else.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistibution() { - // should take secure distribution from config if secure=TRUE - cloudinary.setConfig("secure_distribution", "config.secure.distribution.com"); - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); - } - - @Test - public void testSecureAkamai() { - // should default to akamai if secure is given with private_cdn and no - // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn", true); - String result = cloudinary.url().generate("test"); - assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); - } - - @Test - public void testSecureNonAkamai() { - // should not add cloud_name if private_cdn and secure non akamai - // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn", true); - cloudinary.setConfig("secure_distribution", "something.cloudfront.net"); - String result = cloudinary.url().generate("test"); - assertEquals("https://something.cloudfront.net/image/upload/test", result); - } - - @Test - public void testHttpPrivateCdn() { - // should not add cloud_name if private_cdn and not secure - cloudinary.setConfig("private_cdn", true); - String result = cloudinary.url().generate("test"); - assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); - } - - @Test - public void testFormat() { - // should use format from options - String result = cloudinary.url().format("jpg").generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); - } - - @Test - public void testCrop() { - Transformation transformation = new Transformation().width(100).height(101); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); - assertEquals("101", transformation.getHtmlHeight().toString()); - assertEquals("100", transformation.getHtmlWidth().toString()); - transformation = new Transformation().width(100).height(101).crop("crop"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); - } - - @Test - public void testVariousOptions() { - // should use x, y, radius, prefix, gravity and quality from options - Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); - } - - @Test - public void testTransformationSimple() { - // should support named transformation - Transformation transformation = new Transformation().named("blip"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); - } - - @Test - public void testTransformationArray() { - // should support array of named transformations - Transformation transformation = new Transformation().named("blip", "blop"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); - } - - @Test - public void testBaseTransformations() { - // should support base transformation - Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); - } - - @Test - public void testBaseTransformationArray() { - // should support array of base transformations - Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop") - .width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); - } - - @Test - public void testNoEmptyTransformation() { - // should not include empty transformations - Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); - } - - @Test - 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); - } - - @SuppressWarnings("deprecation") - @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); - } - - @Test - public void testIgnoreHttp() { - // should ignore http links only if type is not given or is asset - String result = cloudinary.url().generate("http://test"); - assertEquals("http://test", result); - 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); - } - - @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); - } - - @Test - public void testCname() { - // should support external cname - String result = cloudinary.url().cname("hello.com").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"); - assertEquals("http://a2.hello.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testBackground() { - // should support background - Transformation transformation = new Transformation().background("red"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); - transformation = new Transformation().background("#112233"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); - } - - @Test - public void testDefaultImage() { - // should support default_image - Transformation transformation = new Transformation().defaultImage("default"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); - } - - @Test - public void testAngle() { - // should support angle - Transformation transformation = new Transformation().angle(12); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); - transformation = new Transformation().angle("exif", "12"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); - } - - @Test - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); - } - - @Test - public void testUnderlay() { - Transformation transformation = new Transformation().underlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); - } - - @Test - public void testFetchFormat() { - // should support format for fetch urls - String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/logo.png"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/logo.png", result); - } - - @Test - public void testEffect() { - // should support effect - Transformation transformation = new Transformation().effect("sepia"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); - } - - @Test - public void testEffectWithParam() { - // should support effect with param - Transformation transformation = new Transformation().effect("sepia", 10); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); - } - - @Test - public void testDensity() { - // should support density - Transformation transformation = new Transformation().density(150); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); - } - - @Test - public void testPage() { - // should support page - Transformation transformation = new Transformation().page(5); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); - } - - @Test - public void testBorder() { - // should support border - Transformation transformation = new Transformation().border(5, "black"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); - transformation = new Transformation().border(5, "#ffaabbdd"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); - transformation = new Transformation().border("1px_solid_blue"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); - } - - @Test - public void testFlags() { - // should support flags - Transformation transformation = new Transformation().flags("abc"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); - transformation = new Transformation().flags("abc", "def"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); - } - - @Test - public void testOpacity() { - // should support opacity - Transformation transformation = new Transformation().opacity(50); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testImageTag() { - Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals( - "my image", - result); - transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); - assertEquals( - "my image", - result); - transformation = new Transformation().width("auto").crop("crop"); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); - assertEquals( - "my image", - result); - } - - @Test - public void testFolders() { - // should add version if public_id contains / - String result = cloudinary.url().generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); - result = cloudinary.url().version(123).generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); - } - - @Test - public void testFoldersWithVersion() { - // should not add version if public_id contains version already - String result = cloudinary.url().generate("v1234/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); - } - - @Test - public void testShorten() { - // should allow to shorted image/upload urls - String result = cloudinary.url().shorten(true).generate("test"); - assertEquals("http://res.cloudinary.com/test123/iu/test", result); - } - - @SuppressWarnings("unchecked") - @Ignore - public void testPrivateDownload() throws Exception { - String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("img", parameters.get("public_id")); - assertEquals("jpg", parameters.get("format")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download", uri.getPath()); - } - - @SuppressWarnings("unchecked") - @Ignore - public void testZipDownload() throws Exception { - String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("ttag", parameters.get("tag")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); - } - - @Test - public void testSpriteCss() { - String result = cloudinary.url().generateSpriteCss("test"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - result = cloudinary.url().generateSpriteCss("test.css"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testEscapePublicId() { - // should escape public_ids - Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); - for (Map.Entry entry : tests.entrySet()) { - String result = cloudinary.url().generate(entry.getKey()); - assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); - } - } - - @Test - public void testSignedUrl() { - // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"; - String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) - .generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"; - actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; - actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - } - - @Test - public void testResponsiveWidth() { - // should support responsive width - Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - String result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); - trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(null); - } - - public void testUtils() { - assertEquals(ObjectUtils.asBoolean(true, null), true); - assertEquals(ObjectUtils.asBoolean(false, null), false); - } - - public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { - Map params = new HashMap(); - for (String param : uri.getQuery().split("&")) { - String pair[] = param.split("="); - String key = URLDecoder.decode(pair[0], "UTF-8"); - String value = ""; - if (pair.length > 1) { - value = URLDecoder.decode(pair[1], "UTF-8"); - } - params.put(new String(key), new String(value)); - } - return params; - } -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java deleted file mode 100644 index 5f143c1d..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/UploaderTest.java +++ /dev/null @@ -1,365 +0,0 @@ -package com.cloudinary.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.json.JSONArray; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; -import com.cloudinary.test.stubs.CloudinaryStub; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.Rectangle; - -@SuppressWarnings({"rawtypes", "unchecked"}) -public class UploaderTest { - - private CloudinaryStub cloudinary; - - @BeforeClass - public static void setUpClass() { - CloudinaryStub cloudinary = new CloudinaryStub(); - if (cloudinary.getStringConfig("api_secret") == null) { - System.err.println("Please setup environment for Upload test to run"); - } - } - - @Before - public void setUp() { - this.cloudinary = new CloudinaryStub(); - assumeNotNull(cloudinary.getStringConfig("api_secret")); - } - - @Ignore - public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); - assertEquals(result.get("width"), 241L); - assertEquals(result.get("height"), 51L); - assertNotNull(result.get("colors")); - assertNotNull(result.get("predominant")); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); - assertEquals(result.get("signature"), expected_signature); - } - - @Ignore - public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 241L); - assertEquals(result.get("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); - assertEquals(result.get("signature"), expected_signature); - } - - @Ignore - public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 16L); - assertEquals(result.get("height"), 16L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); - assertEquals(result.get("signature"), expected_signature); - } - - @Ignore - public void testRename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - - cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); - assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); - - Map result2 = cloudinary.uploader().upload("src/test/resources/favicon.ico", ObjectUtils.emptyMap()); - boolean error_found=false; - try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); - } catch(Exception e) { - error_found=true; - } - assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); - assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); - } - - @Ignore - public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); - assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals((String) result.get("public_id"), "logo"); - } - @Ignore - public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); - String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); - String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); - String cloudName = cloudinary.getStringConfig("cloud_name"); - assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); - } - - @Test - public void testEager() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - } - - @Test - public void testHeaders() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); - } - - @Ignore - public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); - assertTrue(((Long) result.get("width")) > 1); - assertTrue(((Long) result.get("height")) > 1); - } - - @Test - public void testImageUploadTag() { - String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); - assertTrue(tag.contains("type='file'")); - assertTrue(tag.contains("data-cloudinary-field='test-field'")); - assertTrue(tag.contains("class='cloudinary-fileupload'")); - assertTrue(tag.contains("htmlattr='htmlvalue'")); - tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); - assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); - } - - @Ignore - public void testSprite() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); - assertEquals(2, ((Map) result.get("image_infos")).size()); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); - assertTrue(((String) result.get("css_url")).contains("w_100")); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); - assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); - } - - @Ignore - public void testMulti() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); - assertTrue(((String) result.get("url")).endsWith(".gif")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); - assertTrue(((String) result.get("url")).contains("w_100")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); - assertTrue(((String) result.get("url")).contains("w_111")); - assertTrue(((String) result.get("url")).endsWith(".pdf")); - } - - @Ignore - public void testTags() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - String public_id = (String)result.get("public_id"); - Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - String public_id2 = (String)result2.get("public_id"); - cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); - cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); - List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); - tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); - cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); - cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); - } - - @Ignore - public void testAllowedFormats() throws Exception { - //should allow whitelisted formats if allowed_formats - String[] formats = {"png"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); - assertEquals(result.get("format"), "png"); - } - - @Ignore - public void testAllowedFormatsWithIllegalFormat() throws Exception { - //should prevent non whitelisted formats from being uploaded if allowed_formats is specified - boolean errorFound = false; - String[] formats = {"jpg"}; - try{ - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); - } catch(Exception e) { - errorFound=true; - } - assertTrue(errorFound); - } - - @Ignore - public void testAllowedFormatsWithFormat() throws Exception { - //should allow non whitelisted formats if type is specified and convert to that type - String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); - assertEquals("jpg", result.get("format")); - } - - @Ignore - public void testFaceCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121,31,110,151); - Rectangle rect2 = new Rectangle(120,30,109,150); - coordinates.addRect(rect1); - coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); - JSONArray resultFaces = (JSONArray) result.get("faces"); - assertEquals(2, resultFaces.length()); - - JSONArray resultCoordinates = ((JSONArray) resultFaces.get(0)); - - assertEquals((long)rect1.x, resultCoordinates.get(0)); - assertEquals((long)rect1.y, resultCoordinates.get(1)); - assertEquals((long)rect1.width, resultCoordinates.get(2)); - assertEquals((long)rect1.height, resultCoordinates.get(3)); - - resultCoordinates = ((JSONArray) resultFaces.get(1)); - - assertEquals((long)rect2.x, resultCoordinates.get(0)); - assertEquals((long)rect2.y, resultCoordinates.get(1)); - assertEquals((long)rect2.width, resultCoordinates.get(2)); - assertEquals((long)rect2.height, resultCoordinates.get(3)); - - Coordinates differentCoordinates = new Coordinates(); - Rectangle rect3 = new Rectangle(122,32,111,152); - differentCoordinates.addRect(rect3); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); - - resultFaces = (JSONArray) info.get("faces"); - assertEquals(1, resultFaces.length()); - resultCoordinates = ((JSONArray) resultFaces.get(0)); - - assertEquals((long)rect3.x, resultCoordinates.get(0)); - assertEquals((long)rect3.y, resultCoordinates.get(1)); - assertEquals((long)rect3.width, resultCoordinates.get(2)); - assertEquals((long)rect3.height, resultCoordinates.get(3)); - - } - - @Ignore - public void testCustomCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - long[] expected = new long[]{121L,31L,110L,151L}; - JSONArray actual = ((JSONArray)((JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual.get(i)); - } - - coordinates = new Coordinates(new int[]{122,32,110,152}); - cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); - result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - expected = new long[]{122L,32L,110L,152L}; - actual = ((JSONArray)((JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual.get(i)); - } - } - - @Ignore - public void testContext() throws Exception { - //should allow sending context - Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("context", context)); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); - Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); - info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); - } - - @Ignore - public void testModerationRequest() throws Exception { - //should support requesting manual moderation - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); - assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); - } - - - @Test - public void testRawConvertRequest() { - //should support requesting raw conversion - try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("raw_convert", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testCategorizationRequest() { - //should support requesting categorization - try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("categorization", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testDetectionRequest() { - //should support requesting detection - try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("detection", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testAutoTaggingRequest() { - //should support requesting auto tagging - try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("auto_tagging", 0.5f)); - } catch(Exception e) { - assertTrue(e.getMessage().matches("^Must use(.*)")); - } - } - - @Ignore - public void testUploadLargeRawFiles() throws Exception { - // support uploading large raw files - Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); - assertEquals(new java.io.File("src/test/resources/docx.docx").length(), response.get("bytes")); - assertEquals(Boolean.TRUE, response.get("done")); - } - - @Ignore - public void testUnsignedUpload() throws Exception { - // should support unsigned uploading using presets - Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload("src/test/resources/logo.png", preset.get("name").toString(), ObjectUtils.emptyMap()); - assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); - cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); - } - -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java deleted file mode 100644 index 6fcc9481..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/ApiStub.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.cloudinary.test.stubs; - -import java.util.Map; - -import com.cloudinary.CloudinaryBase; -import com.cloudinary.api.ApiBase; -import com.cloudinary.api.ApiResponse; - -public class ApiStub extends ApiBase { - public ApiStub(CloudinaryBase cloudinary) { - super(cloudinary); - // TODO Auto-generated constructor stub - } - - @Override - protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java deleted file mode 100644 index f16fef9e..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/CloudinaryStub.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.cloudinary.test.stubs; - -import java.net.URISyntaxException; -import java.util.Map; - -import com.cloudinary.CloudinaryBase; -import com.cloudinary.UploaderBase; -import com.cloudinary.api.ApiBase; -import com.cloudinary.utils.AbstractURLBuilderWrapper; - -public class CloudinaryStub extends CloudinaryBase { - public CloudinaryStub() { - super(); - } - - public CloudinaryStub(String string) { - super(string); - } - - @SuppressWarnings("rawtypes") - public CloudinaryStub(Map config) { - super(config); - } - - @Override - public AbstractURLBuilderWrapper urlBuilder(String source) throws URISyntaxException { - return new URLBuilderWrapperStub(source); - } - - @Override - public UploaderBase uploader() { - return new UploaderStub(this).withConnectionManager(connectionManager); - } - - @Override - public ApiBase api() { - return new ApiStub(this).withConnectionManager(connectionManager); - } - - -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java deleted file mode 100644 index 4e3428c9..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/URLBuilderWrapperStub.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cloudinary.test.stubs; - -import java.net.URISyntaxException; - -import com.cloudinary.utils.AbstractURLBuilderWrapper; - -public class URLBuilderWrapperStub extends AbstractURLBuilderWrapper { - - - public URLBuilderWrapperStub(String source) throws URISyntaxException { - super(source); - } - - @Override - public void addParam(String key, Object value) { - } - - @Override - public String url() { - return ""; - } - -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java b/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java deleted file mode 100644 index 99059838..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/test/stubs/UploaderStub.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.cloudinary.test.stubs; - -import java.io.IOException; -import java.util.Map; - -import com.cloudinary.CloudinaryBase; -import com.cloudinary.UploaderBase; - -public class UploaderStub extends UploaderBase { - - public UploaderStub(CloudinaryBase cloudinary) { - super(cloudinary); - // TODO Auto-generated constructor stub - } - - @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/cloudinary-core/src/test/resources/docx.docx b/cloudinary-core/src/test/resources/docx.docx deleted file mode 100644 index d179509837c5687f4f30a6c9d9b75b0665c1f1b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20453 zcmeFZ1Cyk|wy53Hwr$&XPuuQk+qP}nwr$(CZB4s-+W30*x%+de)s>M=l@{|G$u`1Z_vXBUW2^C0@nBmk%i=wQ=iNhkFfsKdXP$> ziU@rMy5bDm>82V(o$rUp8tev-9nE@aEo8s{ExLm86gvy56ER^<3VFkc{(GhAx$9lq z8xz_TsN7evLQQdknXM=Y!zq@)%SfjfqofJ68_eVLKct7I9! z#<)RN0ZewctWaB!`gkW-J|j9jL-dn7p7>M_S!X1yONB-$GHWS3RcYJfK1Z_6p|sUL zHJ~cWwGUkY*YQ*|n{9RJ<8YbSBDeKdm^b+d`W*o=k@PIqh()P(GDj~)`mlNGxkpo~ zKd8`E+wXF-L5kl`?=$w#Q5yXF;|l>&^J?IQR$#8~{_;;^Z+yw?8O1-Q$Y^^gU?;lo zmrQR9S+7BbHm?n&K0n~bH>VF|I3jKUDs)T=TnV#5%mnc9PO&}s#7zi#Gng1Ik{ZBi z$bru8LLHvQi5MVFKHiM8hJ28D`*#);syNxbz%ZkI`8lEZv3c=BPTpMfW8KE$YtA;1 z3GGvh*r+{nl)aqBo_qp@P>VY`9d~4-u>-HKm>%RUda-Yk1smx;zkT-W3kX2&|8RNS zILwy&Z#$Ox{ub)n<#p_hEFI`*|Gxf@tN%aj-~Y1o$oLHjAb#lJYmgniQ7^U)NW%HG zZLvHT!rwrFX%|0SU?&ULySobT+vPU2j~`AZWS27nG8VZ?xIt_5)RuBrGjv6FEL^Qx zBEaO-Mm5s?O<+W)Yt5`}mg%_T67#qt87lJk8-n?Xc?pe~Y6~=jM!S{TW3j~{6l3Bh zvHnC>w0Dm@r~sv>6|bwon3-JvY#LEz)metsV#D-?G`?P)2tyeK)3aHl)u*%+AGVJ& zr}#sSX-Pjrk`z_-jMFU9L3Paz{%=6LjDbn|8$xk z-|_Lg_y5~ZWn!Pq0R6Yoh;<4kaY=LO0ySEr{ai;0W;ZEYP-YfSa_!yDwp?0|wpt@e zk#JV}((&fXp7t@q@d~I*QpONJ2o%a~ou>L_cxdqAe*cp&Lh7h;8IrO7hvad??}{_5 zMd&Tr=1NSgHp+41LLtj+@}^GUG0RFUxho-4B8>J~Bi)xE70*AVTAaQfJY0;W-4ud_v-@MdyUvpmYDR;xjdPk(qL-(qFnd(Uy; zwfI1pIKp5bu?2`$0wS}8YLGr-+zzf63- z4eZ}-wO@L3|KS_oXxIS&5Wai-!&d**N-xwWtT)(^x8Uf%ewyO-T#|}h=G$c!N5}w; zG(gGCB3jiYi@}JiIjMX^z4*P3P}rFcl0e z%#5mZE-)hKx4e>T##Hl}rjwPSAx1HVePkyhvXCtNWBmSRQ&W&Pa5+mRMF8J-NDp}t zcSMN-ogr=3F*s7cr>J2&mQt?g&@+}IX`Wy_7_@NFxK4Agf!``!2<(M4$*QUz#@<66 z1s3R6O@hX&T;DfTer8Yy8-Nsxt1FDQSYQrSuMwaGji;8UJ204=Fp1w~h>n%`E~XM1 zkA~uYkc1`<964N!kGtBN=4e*yaF93OE6m1X=8%GfwIg{V%-^9hpVH=;gRKdU%}?nJ zD7jDRzc(1+Nf#Y3eN9{OhPYAHktxoYFKo!VITDyZy@-gXm>lM&YI@fPB808Da}5j^ zFq!F#OLoblVN%ab4_8~sLO(juK?;W`=YJIy0i;i`gWIjcKzq{<6p`-?cNX3_JWGwb zxptoOBRqK4GIMRzylHx)hI64T$-5n265CxOMpNeX!525&v2`> zN>uj`s0WdSOp<8j@v{?B;~)}hW{p!wtGp_CswfV8VG!d*fM3q`t%GIFA51G) z%VG@r^2KM$2L?eVLO1eUy*#+opw)%(M0tpX#{Kx1ur>UJRxg-Kl6#VmREJLXkGyCy z))^;4c%6I*6G+ac_qw(elaI$kZkYE<%QU-M#!o!5OZG%ay25NwzCOVOY<`vMc?BMS z96$!!))%H-Ik%DkYbtTgcBMf~^d8hqx`V=Wn~psJp#%truOJReU zf8v2vb(53}Eh$rY|3LXD=N=vs5@aX_LYuXeo9qZbk`=N zG{q9EzxE%fv#~>@kbl}GnMW}xMwig;JV`-Z1vYPYLZSe!Q&H0=U!IU*iWu4n`0p|NGgft(WbZCk6{5v)%dJWnN($>mvSny ziJIh+8vbH&Z=_|p;&%SAlq#FrFu*oQ8K5GKwJj)HT|+|DUS9E(M0RD7MkdGbD+&? zceM(>E^Jex{>FdSE|==Gg!BmU(4j2$Qh$aB8drb+ zEfOCAwS9qz8t9a+Rv!W_i^kuD1)%l$^U@}a_g>hb=XW9O36_Zs+Eo@$n`*@^_JwLf z)m2b<0ZGtwwX_C2J&bl1@sg~;&8T(9jkK`sE#tvW4kUen&XizxT`0o7m;=GW#8JAt z_B&Y+8H+1d_{Xfl&9N1B0hWI#o~x&%SivlbOGa;X$&Rpp7!iQxm?KO)bC>PB=1J^TmCARKkApJpSz)57IZ9wOvhw|y-E zxo@0Ze6j3Dy0>aip#hfv87N6dmlzd+FhU(qcG|%$hD_vuKOWV728u+O8`9<$HY#uChzdS1xJrK`Ah252UXqRaa!Aqd_UJX(E;5Mq zqxTmn@?h!}kJQ%UP2$9oU&8OHNVVGqn{0dtfenmJ8TXZo=`2MXfOS@b2nV&y1Knqk zF(j0^0NJ=^mLA(x2d>kas@j!5a61P)`dyU+n27QwRr3)|(Cri=IQO+!n!52MJnt&Q zFQJhgRqdRaua={x1tVh7^Uw#4$*5%A zz1Q%Du0NkvAi>wiTrDDHm@$2^L^DIYfgw6PdBWqtqpM2C4Xrn4E;XErR8eKvqN=_b zs$kH6E3WlAJ35d1qf>lnCaQ)>vZId42BSg^CxdJ$#4syHQHr6u7AjUPRx(0~W8qBA zj*|U}jSyw6Uc8j%wp`=}=@^<~qJ9Mw`O4?Yk;nB2XWI4~Rl1 z4`cBNmZMD6Ek_wdD`k?s3`J2|WrA$)VlG$&pz3igse8;$775wZQF8hyzIv(BC2lJ5 z)LZoY&Ohr3ilgICg)Bp&F94(+{)ybrApZ%G3V%u*PJgGn>Tm!6@c%?4V;dVsBYTFw z9DkvrCA&e7?3L5_1^<#ost00)K;bWd04Ioo$JgtWIZRYXvtdTwl^XfoJU{n%jzA)|Qq%YA)W~k^S}c zC^uZ8fSY~FNl6^BU=khUrJEFkrWO}Dk~~k?98?IoTTD@;R0ksIxq~0QHBQN2u8Lq0 z{T^hw)Q{@bR6qf?P9YD{^5JJ`n~tuN#yHN;l+{{{i8M7p=h~hAQ1qm5QJB1}=vW@a zrNH!<$G~%=Is$92JE7S6+|Aa|?tBhgcufUmcR%)->BL{wRlI`TVb9g+b(I_;D@ z2WEEpRzM16U!GQ&A+d8oVeOT^^A3eSvV-)llxZReP;+*!ut6aV8l)h`odIrYX3G&! zQ#a{vUd2KbT%7S;u$P%T&Z^C1ExM=|9n%`J(l#Ko7F!k2lHr~HLMINi$k|7|rH~=n zU2&$syaZj8fIi~12FSn(cpiJt=uL`>rfu7Y5Yl#O8K2{hR^tS+NpG!;6PbXAfF}&; z;CiJ3-5MM+o3KRG5G|IMq+tEO3a<^A9f86iVqq^+Lp1@&G%@uEJacz+-w9~~_3tMa ze<)4;lm=$QT&^R9UgS}L7PL^7cOLxn>H@e_jfTVLwWEIp%97iSr-3cnlnn$)8(uUo z6aZ753s-J(?Qw4F9vNlCFO*OYn4;e4$hLxoqx=a0%T9C0H&nh}GAk!Q?Z zzeh6J0klT>)Tukgp8Dh|c4c8V)R!z{q(wpX*%lF@+d@HuVu?b_twCu~*&rlVyF9%G zUPs@yt$p}%$C30EE}9~VP`gvcMmGeX+?!ms<>J2lwL20^GsC)Q))dlNNnI80y3fIU zmX4LTS7$^<&hkbz?^Km{<#u6~wt@BA4sUtKnvUreN|4WR!Onk74@~`vkov!q)AVo8 zK>SzRxBfPNhriZen7Ad=Pmk<-E$tan^fWt-UH}zP3Vcw-2PfAN%~E~BY&CLtc?pVj z3NGKeCcEN&!o|atHQDWmQB~*!NkDR&8Ykx8E7iwHa%~eaM-!HL5_^ltUu;N&lO|0^ zN9q($?+9*Xcf}wmxbsS0kq6uqltNq{noxDdq zi@MfsI1+V0u(n{VL>tueN5**RuUF--8R!iLW0q9&`0gP+wz^gnAUBglKrvc3XI_!VWgeG2jm{&5C(8AxBK36lcpiZj3>X&p1PS$ zH#FU`vt&%UP^*i*u3*apFai6hO4obJ2%c|Z^`O0|Z}nAAinA5l zWqqkf9&+o9SImL6^IO@3DNU6kI7L`Vq!1#oF{Cnun}?Q6wfG@-NMs5)BMH{o=;iyn zr7{^J<6nit|DOuaF#m_bmHvN)gG8utF`))I(ms3a)*eSYvhx(@#ld?XnX)`thdT?i zwBRMB0k`jIcU367Nb5FK;092XQ4?q7j^URwh&TJU%i8FP%46SjG->9RHUjUs7b++7 z1Nl=Wt_9udsDsy?l2pZj)GZtNRI}b5jqbqT+1S_CcEXme{t#^iN{O?Ia%}4gA?@j? z{3IT}P^H%r!5qxpV?*qIyGSyv*6n@2Hh-jJQyQeWew))b0_70N67lel@moSZ#!IZL zXuf+CbnpR;lz1xI6^R#%I5Yz>eeu9RUb;0}>=a|%Vfm}@;Xr9Jn3l}~5;CKAS<^lL(ZxT%Ed1ZT6@Kuo@c$j&OpWvm|5tdcOx*b1x&Pjo0lUC~ z-Xd#`U`CJR6rQWJpaTWAdIE^798`A#BpI35h-o$;%ZkVk*;0it@_rXj?`(Z9_|=GX zTExO3SppFexYrzOoY9~Y!W-4b>C#%=5VL=BvcDG_^w71W4lYVm}}6v2t0C**U?Nmy>K-G@};EP7-M19`z4 z#fS}6Al_fr0*+w()kX1ovKNNi2A0#5%xnC!hVYB6hFQ4-a~`|o5or6)*^(1fG*YHS z!{o?jwZkd%I1A6@Q3})^;vFmTt7c0*Djklj;cSKP$+*-A=H^2P?EPJGk@*huK4wdrY| zA@zpaGTUb6pIW|ir@V=~z!A(dSIxtOl_(VD3u=p=ahCiZ{lZQnZ$+&$YVpJ;_ty;$ z%Nfed_CyB?mbnUgo(-G~O@}DWBZ8|`{G&LfnOl(L!1b+dFH;^=C2E&dt8IHxEPV@5 zyKRNZnt8zKWa^eG!#STPO8HmyLDgp%iV`qEGA_As1{p^UiI@mq44PR%Lsmt7LQnl0 zkY-*Ye0@r}m?U`%OJ(LcjrnLk-{$b|zYO$Sf)u6SzZr#C001cej0KLSMpj02|9Jf` z7*dn9* z)ha|!!w+?}=#6=e(-LR4rdCuTen?e2o^>_6ikBGi<>2iL7q6^hTO_S7e(B5Q`FN=NBGJwA$@;cFTxeOjfg~vm6YT{ zxB*q|V+wIy>q+5Al+JKl3ixO6(_u@qHf0U1a0Y4a#9j2!ZOoW5*;%=S2Uh!i9?+Vw zc=uit{akS*lb}9OFsG99R1}-U)kQ6;KS}a_yL7Jp^b{PoR&sACx{x8h*b63 z=EiCp@3nSIe%o40*Kk`U`l=_aDk=>SFCblW+?C&d+PmEOX8>xi1B#WC8K*>is zl7BwhWjnVlSF&h&hJB7_PFBg3ELa-VtLB3m;$b;FH?5l+IIQ(y#SXEZ+(b?;ms@299{r;0`M8)F!7Jd)_P)QHLtAoH8~%D7AnSR5 zcVmH{HzA#lpv!rGycmn9;r;mN5JkC>_7gRFjvW@t&o~6Xyhlf7Qnn)@BeLs88313n z3!frRWbaFt1o_Ag$9|ZiFivy9=HMJ`+Qs>CQ~>GfWj}-93Lh4hkCN$P2xpd<(ts}(3_c(I((V_?&XINSsml$J3~Kuu zEJ<{SYz=6O*=Z;<6g&?T0!nwMOe<1P00owTz61=iQ;G5j^Giqn6uFZ1#NIw6TjDsH z4qtWbn-$GkMy@+OS*wzhe_ z#xRJQUP8Z^Lv10YJ+>Bv1k&PQNhI0=2l6qh3#)EtpQ$_+Ezbo*tFRF(v~XAVPS}tv?Ri$^?(F zgVacFiRz>F8zs~P!n`RatQo#cW>jf1utA*L{?WVSEvZ!mQy^GEO^$a0`;$b|B{e)(XV+RrV?(NKh(ogdE-JNe&2DlY+~i2P#58*h)f!TPAi#|(46Oav zxOE~|4<+dlomi4R`qzxo+#AFuZzb?L5U(Z-RHS2ZBM+_8U>;-Cnc zhv`+Bg(x-&2tL3b`6xY&dz?>UoJODWnhBp>^TPO7e=6zJM}f_Q%)>_K+SLcJrZQ7l ztXDeCD^IZfhPrZu>L!byHa!+)U>ZE!sJuf3@MeyUv&B849Y3;?hS z^)JxtVC3j%W^MA1ET>Uik3EXi%XP>9G> zWJ83@u;}j0ua}V%(xpLk_yr(ec+R6E!gWl=qJlWumOvseLMlSvw&ZXkAkiiayTa^q zb5#j#0zs(p(zTdjJMI{D^tMoQl6h49QM0nbWDA2|3T@g8@r^mTdEQ6bHEt9W=}3B; zQAEP%tN20N^AcdppE`5BLskH5UWbFx4frd>x1#Y?9Z^cp!Urmo4UkFkBy*UEgJ~`) zM3maP@n=}MDRMbKOGQ7g;(`GG5Ig&19i?~8U5_=-?7OIw7kZ}W0aT)NO4YClP%6=r zmy@Q6oXE-Z6&(hUR)ta$h|5LWVMkE|Wyby9#257Kk`EQcpvjL%Anx>K^$@v=!%wSd ziewZ-9=(EJo6!DUfB4{Qcr`(%$r zWT6Y?EXMr3uxt)_1`!LzLj!LOy-HX^YPn z5W&{{2o;;=2V0FC#9$J#h&U=hZD*1(2$LYc_*-O65wifMeT}7GAQHJ;XK3(Po}8q! zawMl&jU@1u@A?43GyzL5Hu-CqvDK|lTTV0pF}{FYWewSO)Zk#UM%8Hst9uJfm4AiF zDJmW_n44!@pjkCJDeZ4sVGLlV627sZ-ksb-ib=T?-<{fBBB0RPKh9tcUwS%mk@$*& z>z^ePnns3^%4|v$Wz`_TpC4NjxkBxZ{pN$~$4j5@zU5G6p$AKb-bt07)YL5pgAFfB zj>{8jqz$qOywmL2(hU{7)5O`@)E;xi_ja9}mkVje2hVr)F%GCB2i{Q0V`tChEB;4n z@mhvnrq?6*Tp1)4MmaLg=~KoA6MkYXw?}1Kxy?i$IW+4(o#N_bV4$sZBKO)2IjwF>>31mL9JSEgW)W*ib?VIhuARpLquXzo%luYQs5yV~ z>m!**hwd3z3+FkfjbB1VZswNz>p>C@k*WzrS~m|zIRTMD(=oV^vmPX~HA|cC8oFKi zO8mwUYvEJw{qk3OTbLvDV?8synZB`Kh-QUqR1?hr!|VjkaD++_WT4E}{rCQo3C3u7ugirg?t%Ub?o&T7-`E677-9Z2VTyX;cVEz-y9UR>(jT}_W z98HCdjg7u{6aQ}0ENX1nZ?GeJ?Ua9jC2!;W-d_zfPo&rgBdZ>F<*rkFgO-mSViC7> ztJ{BhHDmq>ub%)D{|a%_gM z5-~fO{lIS8Ks7)tLOPMf96KD8()yf)r!CP{V&8p}H6t_jC30NQ^M=Dj!&VqbY?m{# zPC-Dt@&f;3!?h`j9it<04mS?o7)CI;mdm6oK2W&Kk7JU#+0GA+F6?^8Uf#%5>_$k916F%&8U?_&$) zAtM5xZ5#8)Ja=#1vftEg(IQpAI=V>+hY&3-5ooyVl5!Q9^ z4;BSD6BGu?yZ{$S5oko&xF4+>S%aGDB2+((U*@)6z=31r16~*AiJ9EX|L4eLlqs!Tw>^=B{yqxXm%Bdet0Xx~Ot?=#Yk)0t z>=jZR_?d0{CklgvIx2RT3(oABZTX|%E;7F%iHX)w{7tIK*YhN7af54RPj8|n zPlA~~?r58^?j9yO5^Wkv@rH~-QO6tcjOc4#2{eJEiev=ZbVq&$!wQj$RHenUd3 zqvWxu66G}`FbQRni&~yAgP>CQKBExS5PVX;>vyMrYY0i9ctYQO!es{-GfI*BQSw3i zNhyPDhfK)i?RreF0l;{LSDGTnJD~Z)B#yL{P30%QhiZkM6JQW3BHtD&g8%PLTZm*I z4B@-a{zC2K-=Ts^{c6AGuX(#BuEZ_x{X@R2a{@lGRB78N2z3Zro+5k#o(B2vJpV4{ zzc*neLT~u*K2*Fg6PEc3;3}b*;QyFsPL|M+3Ns!4_c}5B5tIIkFF%#wTPzs)LHeN* z^8bg({wcoytLPNF)g#<=dww)vaNhF$xd=#y^+_v2`;%4U1`DH*nRx^oQa+>qR(F%; zJ?nN61Rvw)=3tg#7CeJB@H5{C8-hFEd)s51XP{-~;mFmeqxS^XeJl?P<~UvIwikH0FyvplQ7xpl3q>s*>tZk9R_pAJAnsJs5M0==~cV+Z|Eq$3inA@Zf~F0BcT$HG984n6Pp(b=cn%l*7WMp6#vP z!$D9Vm|e;yipjcU2K?_tWE!vwPg-!8g-g0-`5Uys&M^N7fH}8`b;Sp;_mG29eAd&7D#3w|mTX5+%1q3l-ub zr^^3;9q3$ssOp>XeoCsRTBwWmaNALX1H#U4=XNFy3h#*grS33AV4W2Pl$Qlr^X6)40%S1W9oSa7pSmD!yHI_-Cj>ixR%yuiDZ^9B)ZQlf z+mF)~RD(dSXX=rHf1;pBxVA@#^G|8Qxy5e;Wstz_tWDu&m&DOH_XaA1&FL`L1p zFb${sBD)AI_T9wgfdr$se54G)4nZWF582@(3*r1i(#3=3*SjjAm3 zBbSHkX9T(lKqN~rG-$oPU=Ny7tfxac?Sl$q#DPqzg%Aw-)gzVj5uR-!qcU}c>1Cp< z8Wi~Y2ex=TnzKewkT7PpxhR7aNP@$Op z@{<2NAm?5tFM?Z7FKt+G=8;qBSUlsxx=;x&oPJ%D;{--tTN|}H`GU1Lay*h7H$(QS zedgTYdJ~h%D*EZqDypv)0wNeQ*e-aADg)+$=mFXSC^mfKyObssEHbw=@{}+Eg#(Y? zF@>acduH<=Nm0{Dl->H96hAls0KU($|EtdLzew?a;~^`&*R|A_-@ud5%4%4##EA3$ zFjLuYk?DstLV^eu@zPEqyPoa{5O97?L_)*1g>F^EFrHrRX^#*x4_Tq}Kuc zDi!R8c&@5N)Kv1`u?@iRZpxJh(((mXZ@E@(xK{SK_4xor3qt!v{F!E{bBO>&cB%LN zHRCI}lRv9X@pFHAG7s9A?R%K9cestZ;!-2gZ#y&p*$Frz#P!y#$dwouc%Qts|D*WP zA7hc76x5OhB8)!b@Wm_Bx7uR)+iTAZz+z)sRW1=t7`t&>Q^!!aLseDI&o7k`q8`lm z1C`hz$Ec>8_K|EZCD{&ml>gJ*ZZBXLfTffVMw(Tfj-xz}l^uGsB~~6-M6Y6o+tY9^ zKM>0Sj}U>*yQ^)&AX~8TqMAk((*&^W|UFHr`z1nKHHtKNN*D`l|nija#xT0a@2d743Pa;^(0nIrG zaXO<}PNhz#HxZn;P`Go@%tbxEFlp{29`YVL2%ThYo_wT=*r%*Qqik5;&P@31Pu+bC zEC~q;%BNZAKBEvn;pU>Is9a<`ag!D2JlV*OPn~_DHjEg6tM$2C2i)64ZGoLZ#LEp&)p(Chx@BDf>`0sPkNxtW{9V-3XIZ}OSqof9PJ$0I;#=@t_KGE~it*{B z`P1-fgoTrh&wR5~%Rg9ZqjEMkm6TGr4kO<$|9hU9-;4RT_RUf;-xyjqRAQVat^}zE zB@d(bcb@wVNu|%>#~6RtDJYk(@K=1u3*Ta46=arU{zkcf71=-E|KCLyu3`akKgp&` zDkyqYlC33o@JZLOnBQHCGLfef7}v1*DfdUsk`0x2;fXV*>+pSU!G>0vN&3dE08gw_ zN{Mw7k+5_QlTltImN z$1oKmM3amA79m;uZ30B92=3%5%JKuzFH|qyyO2fA&o0WF`?#ZZaHOO@$jh8E ziR)sY!%~x1lg%@{rr?BE6UIz!P4)MLUuBWsWLEI;m&_Kw$!vH*rbLh5ji3YrR^dJa z7PDX>`z%+yBtR+3y*OaqRDcLCU~ed@HNu1FpJYY~HAdUhOkH@l-J>t$xEg^mN3S;1 z3bi5^&>w%4f9jZszr<6zgAk!9N~i#H_dpI8qNlyll^L9~bwwpCSZsvzwBOx?>&`CZ z>~;7gl?tidZ%M|QiHp>fgX%$lc%VuSNz$*X+YzUm=rf^8?3%4)A z(b*gbw-+_`vT4nmcmlm@{RbJJo^bLo6ZQ^>J&LtnmasMp$_Hsp&6-#~yju6wKeSH1 z6yH2zJX=yhIQNyeJ*_wQL1Ed89&8QQ@=gbjXCG$=HNM7LxqlHunhWq)m(yjZ?cNbA z!SO;T^&x)s{owV>f9@qwe>sHJ9oTy@)cQBhCO9T&s>D-hwjwSl zm-G%5(1n}{gfC)Xux3QC66;^I5J{8l?HXctUi6zOKUtIDv|ypPjYgh0wf5M1&;`(D zCN~bL5`4IL0JPY3o`5v;YU{cjkO%T zs?2h11&*M6SX9}DE~c2lLp4ZZQ!nCWXVuv!u}Yr6t6j%hqB@)1WUw)G+*Dk);WwY;l zb^mik=o?uVmHYk{qIAKn@}YOPL|-mi0SXVz>QCrt01aBj1dvjN(Q6^sepQd~PDchHiGVdmx zu0mdJ;xnQ{;K5BftrVU>Z$_Ukorc|GDSJozf9Ch|dZ206eit2e27MCKT*0tsnr)GJ zK!%i8Z~6Uj0j@J#y1M^#?c2h>dXIuG8Cdx7zFyH!3p(=gSswWHAM>m_Kp-QqZ)a}! zE|mF~f=O#fB|UvhqrdLlnW$y6PLCXXO?JsA)JBz>zX@8n`a5N-moS-NanHW=#5)=q#Sw5RcCu)_^>S#p9>pl=C^Hh=B z4eb-(;I1S$PQZkv~V-%pk zyF)pj*PXYapR>dwkx3mRi4ot2T~jnk1-@Y8WLI4~RjXu#l1rsb#22Z^2wl4DP${4j^G{l%z#NdJk4oh)?{y1$Pbwn6L|W*YA|^Tmz8 z?42H3=+-tS&p`qhGw*EjL4e?5RUb^IKb9zjOc$%#f;eVm^OR=c(vDdz@%P&hj*u&|avu?k*G4+-I~r(KUT zaX%np3Pe|$1^Dh%ZVQe&ynLGKX*PFW_Uh>f z5aw+7Pc_)XePd+S09tAIap}WzlE_ZW96s>dj zIZ@0siUyaR=G{7eD#>i8lzE2180WPCDPp5%*B#Ws+cDmx)p`AQR5ns_2lKdz(H)np2};ypwDJI&+ld~hIFK6L^PcYy?PVZ4IQtJ zUXmcp3dNYdXAGCJTCcS*hTeH#v$jfh%i!@DkyLMYLuninup>z50UN}Qs zL{b~j;(Bceaj_IF#v4c}G5*|=W5irGj%?WIC0U#4lRlfK!Ypwrwp6k%*WQ1LhBeYB z?jKw`M)WbG<70yQJahMAja+X;#Qc3vvX|1>a7~W1^KvT)P8gRO1gW?2ruR{|NOeC6 zLXuei2P(*T(cV5iIRpWZ8X*=n>^yQ_pJtmrNq5&?wFXwZE_>6Yt+=_FZtL1?YK00m77h!VvMy)!JO-#{$4uXX#%m|x2Zn>;^>#Su zkJ)50G`Sj)v{csepyo)jACV4C#FbGP%5Y!ygmX9xCwhCQT9A-2^2VUi7rccMnr|<6BD!H}Z5!_wl+d z+&y88vV-$J>SUa;QkYapnL31Q1s%_35kk3J5a{Hk=64=T~wicTps zxwBs)8gTmqTUIK-)4axAv#myk)0jsXnwcJ0LWomcMYxfyRjMD9P|giw!Bs}6(u{m+ zt34EvnP{auU7>nbh$-9CF2nhADJ&8CWT|6RTn)!ZyQ@K_yd^(hIn5Zba6>Z+@S}8E2;EGS4Chv4m~NjIo-eK z4<0JjSdRq@`(U8gn|4!he}51*ru4*Pp^|jPv{g$Wi662y=AwoHfmNI5<*}xjp9elJ zOt1K6;FGgLhmQd986;@(b1iZQ84FO6OyrrX z=zpRKPceYctoYt^CG&{l;};NsCpCGbcrc0rY|%36Bu|f(58t^^dWLC>l`i2bx4vp zR+^2KNWMLD)35b0q(nQvN@%BGOWU8BrfG|)>{0%z1uzyx^M_lm@E5MMO$MZv(teD= zm;KMtWogjA4gR~P_itT4@;^6!U5xY<|E0nya(2obkO3a-#v|{#_ON;k6+uliNFgxW z3J*UcODjN}R7#|P*Q=m8Xg;FFbMig)g>HrHx|~R^9$j5tHmMXJ{lW@ept};2j6P+tiUn!9)6E z2s^`Rf$@f#$qThkVtRy4Fs{5UdZiy5GZ>to%}0DM3WDJJTB>*BduVdptC%T%QGJYy z%Zvq|mB zl1T_L?HsiQ@J<$B_~sN!HqlGQh%CzwnQf7i z-@;O7E0>UgEO1yrbEk>~*a1@CNsJ#$rMO;ZPD9jL!o;tyG9`kgM8z$>RHz|HuVHEZ z7>hsj7qd-N6cvY$d?*bC6Q33l|FAxn@nr&J5tEyaXOhRp88zg_x7s#pMM|M%Tft>(Z#rq>s*m~4DZz>U7b(vzxSLd z|MB%>~ zCl5Z{@2YNGRb2CDdgy&%C_@^Mj7++~?2P>|E^uTZ5m=7q3h+iX0QG1eB)trunHd<6 z4*EgYhfCs+QPtZ+5KaB!m)?wh_2*%kI=-SbDY9h2h1NOzh+L88cqU%QA z>xR(%2)Nw=t{c7)3SB??&MAccE5JeptRHzF6}ooxeK-j1Mqg(4fzbU9 zxLpHmIr>%&bR*Cg!6J+}sRT6wc}Xm~cJ#Gq2XSE@UIiOw< zx@Od|G=x?L1~+R42K13NblvEkcZ7and#KfEz!UD^U3ql<=)E|EelJI;ew02Px_^#Qn`Tm~o@Ar9r&mMRl5eq-7 zRXDcG%|;Qsh@1p1I+Z>}ggM*civ4pAhGEpz!ng=r^W)NYCX|Vt2;~xrJM!b4W3HS< z3Au}3idV0HE3DqgY<&$Rv#(#y_*=}ZjT(L$81a((0|URDZwfwghT?o*j+2eRP#!KO zXUW{g0ZijEnKtZHPfe%txBa9`)C8$;>hwW_?A zOb6mvM=QCr(EI=PJl;G-?z4FfA6Y>Z57d~91K+WVF|vX|sQ=$jPN1%F*UYNC2Tn6T zKrWA7?7G@Xr2~6O8jWufRbLjih*@N_6U=q4ltEzq0kV6#Dpq|qb^fRfI)%|AH-opw$ y;T9U8=l|7uzkj^`y#FFWi~b^*)h%*-m~Th7NGK?hUlJ)j5&3yUWZPBAsN7$TB`?AN diff --git a/cloudinary-core/src/test/resources/logo.png b/cloudinary-core/src/test/resources/logo.png deleted file mode 100644 index 10d58c4b80281d2bacb3e8c5439111a242c43972..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3381 zcmV-54a)L~P)nAibqOU||W@$8x`b=@e-#2UnyM{SU-6uG->Y0%@+w zauXm$0^DYv_T%n&G`~2cWSu<^AS{dIa5OXDd*6HWX1K-fL4JPpw+{c)Rf-u%v1Axc;&viopW}xA>yxd;sz92zXY>7G0$olqI_z1GdN$AEP0mSzYU9069pI`v5Tq@CpAjw1D=Uy^lI)hH_&5XCKNSGL+X10k0qd#LWSifH&8iCZJ7x z0Efu-vBpA^72gTLt{?%#&4Bn;0ki_Nch*4pV}MzfgqJ@ET^b~SxE{dBTAnGW^Ffoz z8nE^k3W~iGl0t$!Hfef_=~w~aZqvX59q+Qc!7!$=&_RL(5Ib7Oo+(%8M=S=+I-y&GJSJ%@u*EIl zJABMJuy)q@Blxq4#^09DXYc2rWg$O03)4_R9-2H^L79_#0Pqngd<4H-72rC?^<=d# zLzY*NM{kL-GN~ZiEF^f)|J!QE*kf+<$F+gX2i+KhGIDI6Q+eQ`oVkMW>^!6a41mfx_6h%u z1Dq!Xb}hAK#?S8t*gAl?;dQ`7Ri_P6=(&%j2czgSdqx+ns_J?S$Ov`wncgDhG2nwU z6Uuw1xbA>oGkZWF9xM7I=f80W=ZCPuvh0d&6+|&l>X-iB+$6$i!K#X<3 zeM=CCKt$PCiA-U?|K@r0_s@?gLOsKZA_5IfIURut`wD6737+fSi2(C1euws1u-vY4 z(Ez|Ajn#R~GDG~}&Ue8lHh>uEibI3;BY$yV$;3Jn2O|Lv7Lb5*SP>ke+%!3_3k8!{ zl!l8H?lXO~zJLNFUzQlkHrR_!3H9Lz2H}D@!7kdc3~C*OmRS}t+2f>6z=vDmaT!43 zpKqSMd_5H{AeDh&lMm>UrhhmAnb(JDm`9(Vb4eXNHYq*Pcf+7eI8Zat??^)@y(|qc zw+!Tog?GeVf^43xsxB^m|!vDE_ieN!d5T_jj(>)T{i>i}v4w71jk2TmjqztrVrpft+r# z27|AC&D6Vv7z1*c9=Xt*?m7TLwfo67b)F`$HR0#R8aANY@kH#$k*ZFb-=PemY3fbo zP89>qYO3od9KB7$9u-aQ96|4Z@s=-3?yH6DTq@wk{ z(nO%oMLL@AI*tH5QCMKzShkgQ=2_qJ{>aftsJ@F@niIL>+^+4d4jtwgb>*NP3WG9Y z`px!MA*U4^o*4pKm0Sp)Of1kf6J0Fns!E2S#f^(Ypl_4ht5~w2eM`MlL%bq(m?C5b zrLUU*?>UaSSxYlett*klK$cDW^J|9|0LKcjcVL+-$-iFzvBQr!Kb#vCS!G2<5bHp> zj(N;ugD+0W5M5+8>0E`Q71c}Uf>2i4HvHLb&7f^kNP+FbzwR0onvHQJ@J+iocHncf z8e=6Fl)?xM?9!mD%g-H6eJ@+8=$kDXj6hbE{j5&-J+TJBZ9{;l&(HTO0Jx+!F{?iB znrf)dMhQBzqX~SaGG%A#skB}V%b}}Rq_Gqfj2cNw&NS6l8^BOxnZR@=4s2>Fft>)R zic?(Gs2jOZqEaXo!ptG=qH0@;$j@3;>7XHyW%ZSCuL$6X*;gZ-RS0y6^uEtQU1gc# z4f|8&F1PydU4N_fuSR7@BhJx-WytEUuP{Qg?*$)YB>GClxJm-PeoI7+ZdyY0ZM*ZtVRdaPA=5Hr~(#4nW;zn z?=|Qv?`@Ex>1pa&`l=L&DferHXQiG^Zpc+Jk|uVY+?#@y>Os5VuKeC-L^fj_ zX9ntDxFqM}8WjMKWvdH?(boi7a)aV%qwbMlspg&vCKl5@sZ9L8u`YnFKhPelepQgm z9QvJc_XMfi>*)GB ztHc`4HG_$Af>ZSAK69J)xzZ@ZMcsLMRq3EgN%o-#%3xr`HeX1QT?lQ}VfD7Fs?vtT zDnM@`TS*+yJxk8;y$S+Zbr#Ss1-g(werL6e%Ns0UjSpD;NNXam1hPDLZ1C4(3^pBr zQz=iovFs_7n;P0Z-cSI~43vM)J_aF8b&nfr!_4C9EX`uHG!FG9zt8B9ecUzDM>2pe zHu!C$ij8hc?Q;R}z_H$<_&hU%(h=*QS6~4yfIo1oS5eVVuXC-j4D?}A;U3kqzx4RM zPIOt4rTOHRKnul(Z0svSISn51qstsu3n^rAKR;%$!m$MCsl4rw3y_6eW!J%#mVpxu zO_v8is$c2qOTG?L?BB5PIZhUNRTzkF*8TF zwLbL{h_TIVS8(1-AP*lxOa0;7-;@9*`b?YQb;HZlf^2}8rmu)0PMtw4fSG*GU0ABc zo_@p_pcK^hWWE`9-DSZiopUu{Zs`Vi*6^QWh;Sp2i^ zd@8!w#uho$Z#?cVKK=TK#ruzuwdJ!wJ9+y2)gX+SARFix)0o~Dtnwm7*|tR0>m}pg z9{;xY>nH!X@VGStkP8mvG4qQqCmhHF09h4=cM(dv3_fs>2O(R&H)7AM6AuvOptI>0 zFW!>oaeWo2C={hT|MBy$vLAo(Rn_+=mqJwfvYc)T66B{U{}*5YYcwb29g!=G00000 LNkvXXu0mjfH5q86 diff --git a/cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java deleted file mode 100644 index 6268a468..00000000 --- a/cloudinary-http42/src/main/java/com/cloudinary/Cloudinary.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.cloudinary; - -import java.net.URISyntaxException; -import java.util.Map; - -import com.cloudinary.api.ApiBase; -import com.cloudinary.utils.AbstractURLBuilderWrapper; - -public class Cloudinary extends CloudinaryBase { - - public Cloudinary() { - super(); - } - - public Cloudinary(String string) { - super(string); - } - - @SuppressWarnings("rawtypes") - public Cloudinary(Map config) { - super(config); - } - - @Override - public AbstractURLBuilderWrapper urlBuilder(String source) throws URISyntaxException { - return new URLBuilderWrapper(source); - } - - @Override - public UploaderBase uploader() { - return new Uploader(this).withConnectionManager(connectionManager); - } - - @Override - public ApiBase api() { - return new Api(this).withConnectionManager(connectionManager); - } - -} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java b/cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java deleted file mode 100644 index 2d63f211..00000000 --- a/cloudinary-http42/src/main/java/com/cloudinary/URLBuilderWrapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.cloudinary; - -import java.net.URISyntaxException; - -import org.apache.http.client.utils.URIBuilder; - -import com.cloudinary.utils.AbstractURLBuilderWrapper; - -public class URLBuilderWrapper extends AbstractURLBuilderWrapper { - - private URIBuilder builder; - - public URLBuilderWrapper(String source) throws URISyntaxException { - super(source); - this.builder = new URIBuilder(source); - } - - @Override - public void addParam(String key, Object value) { - builder.addParameter(key, (String) value); - } - - @Override - public String url() { - return builder.toString(); - } - -} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/Api.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java similarity index 81% rename from cloudinary-http42/src/main/java/com/cloudinary/Api.java rename to cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java index d2877f6c..c32843c0 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -1,4 +1,4 @@ -package com.cloudinary; +package com.cloudinary.http42; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -17,7 +17,9 @@ import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; -import com.cloudinary.api.ApiBase; +import com.cloudinary.Api; +import com.cloudinary.Api.HttpMethod; +import com.cloudinary.Cloudinary; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.Response; import com.cloudinary.api.exceptions.GeneralError; @@ -25,27 +27,21 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -public class Api extends ApiBase { - - public Api(CloudinaryBase cloudinary) { - super(cloudinary); - // TODO Auto-generated constructor stub - } - +public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { + + @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - - protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + 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"), this.cloudinary.getStringConfig("upload_prefix", "https://api.cloudinary.com")); - String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.cloudinary.getStringConfig("cloud_name")); + String prefix = ObjectUtils.asString(options.get("upload_prefix"), this.api.cloudinary.getStringConfig("upload_prefix", "https://api.cloudinary.com")); + String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.getStringConfig("cloud_name")); if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); - String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary.getStringConfig("api_key")); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.getStringConfig("api_key")); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary.getStringConfig("api_secret")); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.getStringConfig("api_secret")); if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); @@ -63,7 +59,7 @@ protected ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map exceptionClass = CLOUDINARY_API_ERROR_CLASSES.get(code); + Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); if (code != 200 && exceptionClass == null) { throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); } diff --git a/cloudinary-http42/src/main/java/com/cloudinary/Uploader.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java similarity index 87% rename from cloudinary-http42/src/main/java/com/cloudinary/Uploader.java rename to cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 0812c8cc..85f2afc5 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -1,4 +1,4 @@ -package com.cloudinary; +package com.cloudinary.http42; import java.io.File; import java.io.IOException; @@ -18,31 +18,33 @@ import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; +import com.cloudinary.Cloudinary; +import com.cloudinary.Util; +import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -public class Uploader extends UploaderBase { - - public Uploader(CloudinaryBase cloudinary) { - super(cloudinary); - } +public class UploaderStrategy extends AbstractUploaderStrategy { @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public Map callApi(String action, Map params, Map options, Object file) throws IOException { - if (options == null) + // initialize options if passed as null + if (options == null){ options = ObjectUtils.emptyMap(); + } + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - signRequestParams(params, options); + uploader.signRequestParams(params, options); } else { Util.clearEmpty(params); } - String apiUrl = cloudinary.cloudinaryApiUrl(action, options); + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); - HttpClient client = new DefaultHttpClient(connectionManager); + HttpClient client = new DefaultHttpClient(uploader.connectionManager); HttpPost postMethod = new HttpPost(apiUrl); postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT); diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java new file mode 100644 index 00000000..a56a0b8e --- /dev/null +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java @@ -0,0 +1,23 @@ +package com.cloudinary.http42; + +import org.apache.http.client.utils.URIBuilder; + +public class UrlBuilderStrategy extends com.cloudinary.strategies.AbstractUrlBuilderStrategy { + + private URIBuilder builder; + @Override + public void addParam(String key, Object value){ + builder.addParameter(key, (String) value); + } + + @Override + public String url(){ + return builder.toString(); + } + + @Override + public void initialize() throws Exception { + this.builder = new URIBuilder(source); + } + +} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index ce1391a7..9fc5a5e6 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -21,10 +21,10 @@ import org.junit.Ignore; import org.junit.Test; +import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.Transformation; -import com.cloudinary.api.ApiBase; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; @@ -34,7 +34,7 @@ public class ApiTest { private Cloudinary cloudinary; - private ApiBase api; + private Api api; private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); @BeforeClass @@ -44,7 +44,7 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); return; } - ApiBase api = cloudinary.api(); + Api api = cloudinary.api(); try { api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); } catch (Exception e) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index c22b3a08..c0e1e084 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -26,6 +26,7 @@ public void setUp() { this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); } + @Test public void testCloudName() { // should use cloud_name from config From a001eedeaf9c96233551e7f23262d4b6b49eb37b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Thu, 18 Sep 2014 10:38:08 +0300 Subject: [PATCH 007/592] http42 + android tests pass --- cloudinary-android-test/AndroidManifest.xml | 20 + cloudinary-android-test/assets/docx.docx | Bin 0 -> 20453 bytes .../assets/images/favicon.ico | Bin 0 -> 1150 bytes .../assets/images/logo.png | Bin 0 -> 3381 bytes cloudinary-android-test/pom.xml | 63 +++ cloudinary-android-test/project.properties | 14 + .../res/drawable-hdpi/icon.png | Bin 0 -> 4147 bytes .../res/drawable-ldpi/icon.png | Bin 0 -> 1723 bytes .../res/drawable-mdpi/icon.png | Bin 0 -> 2574 bytes cloudinary-android-test/res/layout/main.xml | 12 + .../res/values/strings.xml | 5 + .../com/cloudinary/test/CloudinaryTest.java | 362 ++++++++++++++++++ .../com/cloudinary/test/UploaderTest.java | 306 +++++++++++++++ cloudinary-android/pom.xml | 3 +- cloudinary-android/project.properties | 15 + .../com/cloudinary/android/ApiStrategy.java | 17 + .../cloudinary/android/MultipartUtility.java | 125 ++++++ .../cloudinary/android/UploaderStrategy.java | 279 ++------------ .../android/UrlBuilderStrategy.java | 33 ++ .../java/com/cloudinary/android/Utils.java | 22 ++ .../main/java/com/cloudinary/Cloudinary.java | 6 +- .../main/java/com/cloudinary/Uploader.java | 4 +- .../com/cloudinary/utils/StringUtils.java | 4 + .../com/cloudinary/test/CloudinaryTest.java | 1 - pom.xml | 1 + 25 files changed, 1036 insertions(+), 256 deletions(-) create mode 100644 cloudinary-android-test/AndroidManifest.xml create mode 100755 cloudinary-android-test/assets/docx.docx create mode 100755 cloudinary-android-test/assets/images/favicon.ico create mode 100755 cloudinary-android-test/assets/images/logo.png create mode 100644 cloudinary-android-test/pom.xml create mode 100644 cloudinary-android-test/project.properties create mode 100644 cloudinary-android-test/res/drawable-hdpi/icon.png create mode 100644 cloudinary-android-test/res/drawable-ldpi/icon.png create mode 100644 cloudinary-android-test/res/drawable-mdpi/icon.png create mode 100644 cloudinary-android-test/res/layout/main.xml create mode 100644 cloudinary-android-test/res/values/strings.xml create mode 100644 cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java create mode 100644 cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java create mode 100644 cloudinary-android/project.properties create mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java create mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/Utils.java diff --git a/cloudinary-android-test/AndroidManifest.xml b/cloudinary-android-test/AndroidManifest.xml new file mode 100644 index 00000000..d4fc81a5 --- /dev/null +++ b/cloudinary-android-test/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cloudinary-android-test/assets/docx.docx b/cloudinary-android-test/assets/docx.docx new file mode 100755 index 0000000000000000000000000000000000000000..d179509837c5687f4f30a6c9d9b75b0665c1f1b3 GIT binary patch literal 20453 zcmeFZ1Cyk|wy53Hwr$&XPuuQk+qP}nwr$(CZB4s-+W30*x%+de)s>M=l@{|G$u`1Z_vXBUW2^C0@nBmk%i=wQ=iNhkFfsKdXP$> ziU@rMy5bDm>82V(o$rUp8tev-9nE@aEo8s{ExLm86gvy56ER^<3VFkc{(GhAx$9lq z8xz_TsN7evLQQdknXM=Y!zq@)%SfjfqofJ68_eVLKct7I9! z#<)RN0ZewctWaB!`gkW-J|j9jL-dn7p7>M_S!X1yONB-$GHWS3RcYJfK1Z_6p|sUL zHJ~cWwGUkY*YQ*|n{9RJ<8YbSBDeKdm^b+d`W*o=k@PIqh()P(GDj~)`mlNGxkpo~ zKd8`E+wXF-L5kl`?=$w#Q5yXF;|l>&^J?IQR$#8~{_;;^Z+yw?8O1-Q$Y^^gU?;lo zmrQR9S+7BbHm?n&K0n~bH>VF|I3jKUDs)T=TnV#5%mnc9PO&}s#7zi#Gng1Ik{ZBi z$bru8LLHvQi5MVFKHiM8hJ28D`*#);syNxbz%ZkI`8lEZv3c=BPTpMfW8KE$YtA;1 z3GGvh*r+{nl)aqBo_qp@P>VY`9d~4-u>-HKm>%RUda-Yk1smx;zkT-W3kX2&|8RNS zILwy&Z#$Ox{ub)n<#p_hEFI`*|Gxf@tN%aj-~Y1o$oLHjAb#lJYmgniQ7^U)NW%HG zZLvHT!rwrFX%|0SU?&ULySobT+vPU2j~`AZWS27nG8VZ?xIt_5)RuBrGjv6FEL^Qx zBEaO-Mm5s?O<+W)Yt5`}mg%_T67#qt87lJk8-n?Xc?pe~Y6~=jM!S{TW3j~{6l3Bh zvHnC>w0Dm@r~sv>6|bwon3-JvY#LEz)metsV#D-?G`?P)2tyeK)3aHl)u*%+AGVJ& zr}#sSX-Pjrk`z_-jMFU9L3Paz{%=6LjDbn|8$xk z-|_Lg_y5~ZWn!Pq0R6Yoh;<4kaY=LO0ySEr{ai;0W;ZEYP-YfSa_!yDwp?0|wpt@e zk#JV}((&fXp7t@q@d~I*QpONJ2o%a~ou>L_cxdqAe*cp&Lh7h;8IrO7hvad??}{_5 zMd&Tr=1NSgHp+41LLtj+@}^GUG0RFUxho-4B8>J~Bi)xE70*AVTAaQfJY0;W-4ud_v-@MdyUvpmYDR;xjdPk(qL-(qFnd(Uy; zwfI1pIKp5bu?2`$0wS}8YLGr-+zzf63- z4eZ}-wO@L3|KS_oXxIS&5Wai-!&d**N-xwWtT)(^x8Uf%ewyO-T#|}h=G$c!N5}w; zG(gGCB3jiYi@}JiIjMX^z4*P3P}rFcl0e z%#5mZE-)hKx4e>T##Hl}rjwPSAx1HVePkyhvXCtNWBmSRQ&W&Pa5+mRMF8J-NDp}t zcSMN-ogr=3F*s7cr>J2&mQt?g&@+}IX`Wy_7_@NFxK4Agf!``!2<(M4$*QUz#@<66 z1s3R6O@hX&T;DfTer8Yy8-Nsxt1FDQSYQrSuMwaGji;8UJ204=Fp1w~h>n%`E~XM1 zkA~uYkc1`<964N!kGtBN=4e*yaF93OE6m1X=8%GfwIg{V%-^9hpVH=;gRKdU%}?nJ zD7jDRzc(1+Nf#Y3eN9{OhPYAHktxoYFKo!VITDyZy@-gXm>lM&YI@fPB808Da}5j^ zFq!F#OLoblVN%ab4_8~sLO(juK?;W`=YJIy0i;i`gWIjcKzq{<6p`-?cNX3_JWGwb zxptoOBRqK4GIMRzylHx)hI64T$-5n265CxOMpNeX!525&v2`> zN>uj`s0WdSOp<8j@v{?B;~)}hW{p!wtGp_CswfV8VG!d*fM3q`t%GIFA51G) z%VG@r^2KM$2L?eVLO1eUy*#+opw)%(M0tpX#{Kx1ur>UJRxg-Kl6#VmREJLXkGyCy z))^;4c%6I*6G+ac_qw(elaI$kZkYE<%QU-M#!o!5OZG%ay25NwzCOVOY<`vMc?BMS z96$!!))%H-Ik%DkYbtTgcBMf~^d8hqx`V=Wn~psJp#%truOJReU zf8v2vb(53}Eh$rY|3LXD=N=vs5@aX_LYuXeo9qZbk`=N zG{q9EzxE%fv#~>@kbl}GnMW}xMwig;JV`-Z1vYPYLZSe!Q&H0=U!IU*iWu4n`0p|NGgft(WbZCk6{5v)%dJWnN($>mvSny ziJIh+8vbH&Z=_|p;&%SAlq#FrFu*oQ8K5GKwJj)HT|+|DUS9E(M0RD7MkdGbD+&? zceM(>E^Jex{>FdSE|==Gg!BmU(4j2$Qh$aB8drb+ zEfOCAwS9qz8t9a+Rv!W_i^kuD1)%l$^U@}a_g>hb=XW9O36_Zs+Eo@$n`*@^_JwLf z)m2b<0ZGtwwX_C2J&bl1@sg~;&8T(9jkK`sE#tvW4kUen&XizxT`0o7m;=GW#8JAt z_B&Y+8H+1d_{Xfl&9N1B0hWI#o~x&%SivlbOGa;X$&Rpp7!iQxm?KO)bC>PB=1J^TmCARKkApJpSz)57IZ9wOvhw|y-E zxo@0Ze6j3Dy0>aip#hfv87N6dmlzd+FhU(qcG|%$hD_vuKOWV728u+O8`9<$HY#uChzdS1xJrK`Ah252UXqRaa!Aqd_UJX(E;5Mq zqxTmn@?h!}kJQ%UP2$9oU&8OHNVVGqn{0dtfenmJ8TXZo=`2MXfOS@b2nV&y1Knqk zF(j0^0NJ=^mLA(x2d>kas@j!5a61P)`dyU+n27QwRr3)|(Cri=IQO+!n!52MJnt&Q zFQJhgRqdRaua={x1tVh7^Uw#4$*5%A zz1Q%Du0NkvAi>wiTrDDHm@$2^L^DIYfgw6PdBWqtqpM2C4Xrn4E;XErR8eKvqN=_b zs$kH6E3WlAJ35d1qf>lnCaQ)>vZId42BSg^CxdJ$#4syHQHr6u7AjUPRx(0~W8qBA zj*|U}jSyw6Uc8j%wp`=}=@^<~qJ9Mw`O4?Yk;nB2XWI4~Rl1 z4`cBNmZMD6Ek_wdD`k?s3`J2|WrA$)VlG$&pz3igse8;$775wZQF8hyzIv(BC2lJ5 z)LZoY&Ohr3ilgICg)Bp&F94(+{)ybrApZ%G3V%u*PJgGn>Tm!6@c%?4V;dVsBYTFw z9DkvrCA&e7?3L5_1^<#ost00)K;bWd04Ioo$JgtWIZRYXvtdTwl^XfoJU{n%jzA)|Qq%YA)W~k^S}c zC^uZ8fSY~FNl6^BU=khUrJEFkrWO}Dk~~k?98?IoTTD@;R0ksIxq~0QHBQN2u8Lq0 z{T^hw)Q{@bR6qf?P9YD{^5JJ`n~tuN#yHN;l+{{{i8M7p=h~hAQ1qm5QJB1}=vW@a zrNH!<$G~%=Is$92JE7S6+|Aa|?tBhgcufUmcR%)->BL{wRlI`TVb9g+b(I_;D@ z2WEEpRzM16U!GQ&A+d8oVeOT^^A3eSvV-)llxZReP;+*!ut6aV8l)h`odIrYX3G&! zQ#a{vUd2KbT%7S;u$P%T&Z^C1ExM=|9n%`J(l#Ko7F!k2lHr~HLMINi$k|7|rH~=n zU2&$syaZj8fIi~12FSn(cpiJt=uL`>rfu7Y5Yl#O8K2{hR^tS+NpG!;6PbXAfF}&; z;CiJ3-5MM+o3KRG5G|IMq+tEO3a<^A9f86iVqq^+Lp1@&G%@uEJacz+-w9~~_3tMa ze<)4;lm=$QT&^R9UgS}L7PL^7cOLxn>H@e_jfTVLwWEIp%97iSr-3cnlnn$)8(uUo z6aZ753s-J(?Qw4F9vNlCFO*OYn4;e4$hLxoqx=a0%T9C0H&nh}GAk!Q?Z zzeh6J0klT>)Tukgp8Dh|c4c8V)R!z{q(wpX*%lF@+d@HuVu?b_twCu~*&rlVyF9%G zUPs@yt$p}%$C30EE}9~VP`gvcMmGeX+?!ms<>J2lwL20^GsC)Q))dlNNnI80y3fIU zmX4LTS7$^<&hkbz?^Km{<#u6~wt@BA4sUtKnvUreN|4WR!Onk74@~`vkov!q)AVo8 zK>SzRxBfPNhriZen7Ad=Pmk<-E$tan^fWt-UH}zP3Vcw-2PfAN%~E~BY&CLtc?pVj z3NGKeCcEN&!o|atHQDWmQB~*!NkDR&8Ykx8E7iwHa%~eaM-!HL5_^ltUu;N&lO|0^ zN9q($?+9*Xcf}wmxbsS0kq6uqltNq{noxDdq zi@MfsI1+V0u(n{VL>tueN5**RuUF--8R!iLW0q9&`0gP+wz^gnAUBglKrvc3XI_!VWgeG2jm{&5C(8AxBK36lcpiZj3>X&p1PS$ zH#FU`vt&%UP^*i*u3*apFai6hO4obJ2%c|Z^`O0|Z}nAAinA5l zWqqkf9&+o9SImL6^IO@3DNU6kI7L`Vq!1#oF{Cnun}?Q6wfG@-NMs5)BMH{o=;iyn zr7{^J<6nit|DOuaF#m_bmHvN)gG8utF`))I(ms3a)*eSYvhx(@#ld?XnX)`thdT?i zwBRMB0k`jIcU367Nb5FK;092XQ4?q7j^URwh&TJU%i8FP%46SjG->9RHUjUs7b++7 z1Nl=Wt_9udsDsy?l2pZj)GZtNRI}b5jqbqT+1S_CcEXme{t#^iN{O?Ia%}4gA?@j? z{3IT}P^H%r!5qxpV?*qIyGSyv*6n@2Hh-jJQyQeWew))b0_70N67lel@moSZ#!IZL zXuf+CbnpR;lz1xI6^R#%I5Yz>eeu9RUb;0}>=a|%Vfm}@;Xr9Jn3l}~5;CKAS<^lL(ZxT%Ed1ZT6@Kuo@c$j&OpWvm|5tdcOx*b1x&Pjo0lUC~ z-Xd#`U`CJR6rQWJpaTWAdIE^798`A#BpI35h-o$;%ZkVk*;0it@_rXj?`(Z9_|=GX zTExO3SppFexYrzOoY9~Y!W-4b>C#%=5VL=BvcDG_^w71W4lYVm}}6v2t0C**U?Nmy>K-G@};EP7-M19`z4 z#fS}6Al_fr0*+w()kX1ovKNNi2A0#5%xnC!hVYB6hFQ4-a~`|o5or6)*^(1fG*YHS z!{o?jwZkd%I1A6@Q3})^;vFmTt7c0*Djklj;cSKP$+*-A=H^2P?EPJGk@*huK4wdrY| zA@zpaGTUb6pIW|ir@V=~z!A(dSIxtOl_(VD3u=p=ahCiZ{lZQnZ$+&$YVpJ;_ty;$ z%Nfed_CyB?mbnUgo(-G~O@}DWBZ8|`{G&LfnOl(L!1b+dFH;^=C2E&dt8IHxEPV@5 zyKRNZnt8zKWa^eG!#STPO8HmyLDgp%iV`qEGA_As1{p^UiI@mq44PR%Lsmt7LQnl0 zkY-*Ye0@r}m?U`%OJ(LcjrnLk-{$b|zYO$Sf)u6SzZr#C001cej0KLSMpj02|9Jf` z7*dn9* z)ha|!!w+?}=#6=e(-LR4rdCuTen?e2o^>_6ikBGi<>2iL7q6^hTO_S7e(B5Q`FN=NBGJwA$@;cFTxeOjfg~vm6YT{ zxB*q|V+wIy>q+5Al+JKl3ixO6(_u@qHf0U1a0Y4a#9j2!ZOoW5*;%=S2Uh!i9?+Vw zc=uit{akS*lb}9OFsG99R1}-U)kQ6;KS}a_yL7Jp^b{PoR&sACx{x8h*b63 z=EiCp@3nSIe%o40*Kk`U`l=_aDk=>SFCblW+?C&d+PmEOX8>xi1B#WC8K*>is zl7BwhWjnVlSF&h&hJB7_PFBg3ELa-VtLB3m;$b;FH?5l+IIQ(y#SXEZ+(b?;ms@299{r;0`M8)F!7Jd)_P)QHLtAoH8~%D7AnSR5 zcVmH{HzA#lpv!rGycmn9;r;mN5JkC>_7gRFjvW@t&o~6Xyhlf7Qnn)@BeLs88313n z3!frRWbaFt1o_Ag$9|ZiFivy9=HMJ`+Qs>CQ~>GfWj}-93Lh4hkCN$P2xpd<(ts}(3_c(I((V_?&XINSsml$J3~Kuu zEJ<{SYz=6O*=Z;<6g&?T0!nwMOe<1P00owTz61=iQ;G5j^Giqn6uFZ1#NIw6TjDsH z4qtWbn-$GkMy@+OS*wzhe_ z#xRJQUP8Z^Lv10YJ+>Bv1k&PQNhI0=2l6qh3#)EtpQ$_+Ezbo*tFRF(v~XAVPS}tv?Ri$^?(F zgVacFiRz>F8zs~P!n`RatQo#cW>jf1utA*L{?WVSEvZ!mQy^GEO^$a0`;$b|B{e)(XV+RrV?(NKh(ogdE-JNe&2DlY+~i2P#58*h)f!TPAi#|(46Oav zxOE~|4<+dlomi4R`qzxo+#AFuZzb?L5U(Z-RHS2ZBM+_8U>;-Cnc zhv`+Bg(x-&2tL3b`6xY&dz?>UoJODWnhBp>^TPO7e=6zJM}f_Q%)>_K+SLcJrZQ7l ztXDeCD^IZfhPrZu>L!byHa!+)U>ZE!sJuf3@MeyUv&B849Y3;?hS z^)JxtVC3j%W^MA1ET>Uik3EXi%XP>9G> zWJ83@u;}j0ua}V%(xpLk_yr(ec+R6E!gWl=qJlWumOvseLMlSvw&ZXkAkiiayTa^q zb5#j#0zs(p(zTdjJMI{D^tMoQl6h49QM0nbWDA2|3T@g8@r^mTdEQ6bHEt9W=}3B; zQAEP%tN20N^AcdppE`5BLskH5UWbFx4frd>x1#Y?9Z^cp!Urmo4UkFkBy*UEgJ~`) zM3maP@n=}MDRMbKOGQ7g;(`GG5Ig&19i?~8U5_=-?7OIw7kZ}W0aT)NO4YClP%6=r zmy@Q6oXE-Z6&(hUR)ta$h|5LWVMkE|Wyby9#257Kk`EQcpvjL%Anx>K^$@v=!%wSd ziewZ-9=(EJo6!DUfB4{Qcr`(%$r zWT6Y?EXMr3uxt)_1`!LzLj!LOy-HX^YPn z5W&{{2o;;=2V0FC#9$J#h&U=hZD*1(2$LYc_*-O65wifMeT}7GAQHJ;XK3(Po}8q! zawMl&jU@1u@A?43GyzL5Hu-CqvDK|lTTV0pF}{FYWewSO)Zk#UM%8Hst9uJfm4AiF zDJmW_n44!@pjkCJDeZ4sVGLlV627sZ-ksb-ib=T?-<{fBBB0RPKh9tcUwS%mk@$*& z>z^ePnns3^%4|v$Wz`_TpC4NjxkBxZ{pN$~$4j5@zU5G6p$AKb-bt07)YL5pgAFfB zj>{8jqz$qOywmL2(hU{7)5O`@)E;xi_ja9}mkVje2hVr)F%GCB2i{Q0V`tChEB;4n z@mhvnrq?6*Tp1)4MmaLg=~KoA6MkYXw?}1Kxy?i$IW+4(o#N_bV4$sZBKO)2IjwF>>31mL9JSEgW)W*ib?VIhuARpLquXzo%luYQs5yV~ z>m!**hwd3z3+FkfjbB1VZswNz>p>C@k*WzrS~m|zIRTMD(=oV^vmPX~HA|cC8oFKi zO8mwUYvEJw{qk3OTbLvDV?8synZB`Kh-QUqR1?hr!|VjkaD++_WT4E}{rCQo3C3u7ugirg?t%Ub?o&T7-`E677-9Z2VTyX;cVEz-y9UR>(jT}_W z98HCdjg7u{6aQ}0ENX1nZ?GeJ?Ua9jC2!;W-d_zfPo&rgBdZ>F<*rkFgO-mSViC7> ztJ{BhHDmq>ub%)D{|a%_gM z5-~fO{lIS8Ks7)tLOPMf96KD8()yf)r!CP{V&8p}H6t_jC30NQ^M=Dj!&VqbY?m{# zPC-Dt@&f;3!?h`j9it<04mS?o7)CI;mdm6oK2W&Kk7JU#+0GA+F6?^8Uf#%5>_$k916F%&8U?_&$) zAtM5xZ5#8)Ja=#1vftEg(IQpAI=V>+hY&3-5ooyVl5!Q9^ z4;BSD6BGu?yZ{$S5oko&xF4+>S%aGDB2+((U*@)6z=31r16~*AiJ9EX|L4eLlqs!Tw>^=B{yqxXm%Bdet0Xx~Ot?=#Yk)0t z>=jZR_?d0{CklgvIx2RT3(oABZTX|%E;7F%iHX)w{7tIK*YhN7af54RPj8|n zPlA~~?r58^?j9yO5^Wkv@rH~-QO6tcjOc4#2{eJEiev=ZbVq&$!wQj$RHenUd3 zqvWxu66G}`FbQRni&~yAgP>CQKBExS5PVX;>vyMrYY0i9ctYQO!es{-GfI*BQSw3i zNhyPDhfK)i?RreF0l;{LSDGTnJD~Z)B#yL{P30%QhiZkM6JQW3BHtD&g8%PLTZm*I z4B@-a{zC2K-=Ts^{c6AGuX(#BuEZ_x{X@R2a{@lGRB78N2z3Zro+5k#o(B2vJpV4{ zzc*neLT~u*K2*Fg6PEc3;3}b*;QyFsPL|M+3Ns!4_c}5B5tIIkFF%#wTPzs)LHeN* z^8bg({wcoytLPNF)g#<=dww)vaNhF$xd=#y^+_v2`;%4U1`DH*nRx^oQa+>qR(F%; zJ?nN61Rvw)=3tg#7CeJB@H5{C8-hFEd)s51XP{-~;mFmeqxS^XeJl?P<~UvIwikH0FyvplQ7xpl3q>s*>tZk9R_pAJAnsJs5M0==~cV+Z|Eq$3inA@Zf~F0BcT$HG984n6Pp(b=cn%l*7WMp6#vP z!$D9Vm|e;yipjcU2K?_tWE!vwPg-!8g-g0-`5Uys&M^N7fH}8`b;Sp;_mG29eAd&7D#3w|mTX5+%1q3l-ub zr^^3;9q3$ssOp>XeoCsRTBwWmaNALX1H#U4=XNFy3h#*grS33AV4W2Pl$Qlr^X6)40%S1W9oSa7pSmD!yHI_-Cj>ixR%yuiDZ^9B)ZQlf z+mF)~RD(dSXX=rHf1;pBxVA@#^G|8Qxy5e;Wstz_tWDu&m&DOH_XaA1&FL`L1p zFb${sBD)AI_T9wgfdr$se54G)4nZWF582@(3*r1i(#3=3*SjjAm3 zBbSHkX9T(lKqN~rG-$oPU=Ny7tfxac?Sl$q#DPqzg%Aw-)gzVj5uR-!qcU}c>1Cp< z8Wi~Y2ex=TnzKewkT7PpxhR7aNP@$Op z@{<2NAm?5tFM?Z7FKt+G=8;qBSUlsxx=;x&oPJ%D;{--tTN|}H`GU1Lay*h7H$(QS zedgTYdJ~h%D*EZqDypv)0wNeQ*e-aADg)+$=mFXSC^mfKyObssEHbw=@{}+Eg#(Y? zF@>acduH<=Nm0{Dl->H96hAls0KU($|EtdLzew?a;~^`&*R|A_-@ud5%4%4##EA3$ zFjLuYk?DstLV^eu@zPEqyPoa{5O97?L_)*1g>F^EFrHrRX^#*x4_Tq}Kuc zDi!R8c&@5N)Kv1`u?@iRZpxJh(((mXZ@E@(xK{SK_4xor3qt!v{F!E{bBO>&cB%LN zHRCI}lRv9X@pFHAG7s9A?R%K9cestZ;!-2gZ#y&p*$Frz#P!y#$dwouc%Qts|D*WP zA7hc76x5OhB8)!b@Wm_Bx7uR)+iTAZz+z)sRW1=t7`t&>Q^!!aLseDI&o7k`q8`lm z1C`hz$Ec>8_K|EZCD{&ml>gJ*ZZBXLfTffVMw(Tfj-xz}l^uGsB~~6-M6Y6o+tY9^ zKM>0Sj}U>*yQ^)&AX~8TqMAk((*&^W|UFHr`z1nKHHtKNN*D`l|nija#xT0a@2d743Pa;^(0nIrG zaXO<}PNhz#HxZn;P`Go@%tbxEFlp{29`YVL2%ThYo_wT=*r%*Qqik5;&P@31Pu+bC zEC~q;%BNZAKBEvn;pU>Is9a<`ag!D2JlV*OPn~_DHjEg6tM$2C2i)64ZGoLZ#LEp&)p(Chx@BDf>`0sPkNxtW{9V-3XIZ}OSqof9PJ$0I;#=@t_KGE~it*{B z`P1-fgoTrh&wR5~%Rg9ZqjEMkm6TGr4kO<$|9hU9-;4RT_RUf;-xyjqRAQVat^}zE zB@d(bcb@wVNu|%>#~6RtDJYk(@K=1u3*Ta46=arU{zkcf71=-E|KCLyu3`akKgp&` zDkyqYlC33o@JZLOnBQHCGLfef7}v1*DfdUsk`0x2;fXV*>+pSU!G>0vN&3dE08gw_ zN{Mw7k+5_QlTltImN z$1oKmM3amA79m;uZ30B92=3%5%JKuzFH|qyyO2fA&o0WF`?#ZZaHOO@$jh8E ziR)sY!%~x1lg%@{rr?BE6UIz!P4)MLUuBWsWLEI;m&_Kw$!vH*rbLh5ji3YrR^dJa z7PDX>`z%+yBtR+3y*OaqRDcLCU~ed@HNu1FpJYY~HAdUhOkH@l-J>t$xEg^mN3S;1 z3bi5^&>w%4f9jZszr<6zgAk!9N~i#H_dpI8qNlyll^L9~bwwpCSZsvzwBOx?>&`CZ z>~;7gl?tidZ%M|QiHp>fgX%$lc%VuSNz$*X+YzUm=rf^8?3%4)A z(b*gbw-+_`vT4nmcmlm@{RbJJo^bLo6ZQ^>J&LtnmasMp$_Hsp&6-#~yju6wKeSH1 z6yH2zJX=yhIQNyeJ*_wQL1Ed89&8QQ@=gbjXCG$=HNM7LxqlHunhWq)m(yjZ?cNbA z!SO;T^&x)s{owV>f9@qwe>sHJ9oTy@)cQBhCO9T&s>D-hwjwSl zm-G%5(1n}{gfC)Xux3QC66;^I5J{8l?HXctUi6zOKUtIDv|ypPjYgh0wf5M1&;`(D zCN~bL5`4IL0JPY3o`5v;YU{cjkO%T zs?2h11&*M6SX9}DE~c2lLp4ZZQ!nCWXVuv!u}Yr6t6j%hqB@)1WUw)G+*Dk);WwY;l zb^mik=o?uVmHYk{qIAKn@}YOPL|-mi0SXVz>QCrt01aBj1dvjN(Q6^sepQd~PDchHiGVdmx zu0mdJ;xnQ{;K5BftrVU>Z$_Ukorc|GDSJozf9Ch|dZ206eit2e27MCKT*0tsnr)GJ zK!%i8Z~6Uj0j@J#y1M^#?c2h>dXIuG8Cdx7zFyH!3p(=gSswWHAM>m_Kp-QqZ)a}! zE|mF~f=O#fB|UvhqrdLlnW$y6PLCXXO?JsA)JBz>zX@8n`a5N-moS-NanHW=#5)=q#Sw5RcCu)_^>S#p9>pl=C^Hh=B z4eb-(;I1S$PQZkv~V-%pk zyF)pj*PXYapR>dwkx3mRi4ot2T~jnk1-@Y8WLI4~RjXu#l1rsb#22Z^2wl4DP${4j^G{l%z#NdJk4oh)?{y1$Pbwn6L|W*YA|^Tmz8 z?42H3=+-tS&p`qhGw*EjL4e?5RUb^IKb9zjOc$%#f;eVm^OR=c(vDdz@%P&hj*u&|avu?k*G4+-I~r(KUT zaX%np3Pe|$1^Dh%ZVQe&ynLGKX*PFW_Uh>f z5aw+7Pc_)XePd+S09tAIap}WzlE_ZW96s>dj zIZ@0siUyaR=G{7eD#>i8lzE2180WPCDPp5%*B#Ws+cDmx)p`AQR5ns_2lKdz(H)np2};ypwDJI&+ld~hIFK6L^PcYy?PVZ4IQtJ zUXmcp3dNYdXAGCJTCcS*hTeH#v$jfh%i!@DkyLMYLuninup>z50UN}Qs zL{b~j;(Bceaj_IF#v4c}G5*|=W5irGj%?WIC0U#4lRlfK!Ypwrwp6k%*WQ1LhBeYB z?jKw`M)WbG<70yQJahMAja+X;#Qc3vvX|1>a7~W1^KvT)P8gRO1gW?2ruR{|NOeC6 zLXuei2P(*T(cV5iIRpWZ8X*=n>^yQ_pJtmrNq5&?wFXwZE_>6Yt+=_FZtL1?YK00m77h!VvMy)!JO-#{$4uXX#%m|x2Zn>;^>#Su zkJ)50G`Sj)v{csepyo)jACV4C#FbGP%5Y!ygmX9xCwhCQT9A-2^2VUi7rccMnr|<6BD!H}Z5!_wl+d z+&y88vV-$J>SUa;QkYapnL31Q1s%_35kk3J5a{Hk=64=T~wicTps zxwBs)8gTmqTUIK-)4axAv#myk)0jsXnwcJ0LWomcMYxfyRjMD9P|giw!Bs}6(u{m+ zt34EvnP{auU7>nbh$-9CF2nhADJ&8CWT|6RTn)!ZyQ@K_yd^(hIn5Zba6>Z+@S}8E2;EGS4Chv4m~NjIo-eK z4<0JjSdRq@`(U8gn|4!he}51*ru4*Pp^|jPv{g$Wi662y=AwoHfmNI5<*}xjp9elJ zOt1K6;FGgLhmQd986;@(b1iZQ84FO6OyrrX z=zpRKPceYctoYt^CG&{l;};NsCpCGbcrc0rY|%36Bu|f(58t^^dWLC>l`i2bx4vp zR+^2KNWMLD)35b0q(nQvN@%BGOWU8BrfG|)>{0%z1uzyx^M_lm@E5MMO$MZv(teD= zm;KMtWogjA4gR~P_itT4@;^6!U5xY<|E0nya(2obkO3a-#v|{#_ON;k6+uliNFgxW z3J*UcODjN}R7#|P*Q=m8Xg;FFbMig)g>HrHx|~R^9$j5tHmMXJ{lW@ept};2j6P+tiUn!9)6E z2s^`Rf$@f#$qThkVtRy4Fs{5UdZiy5GZ>to%}0DM3WDJJTB>*BduVdptC%T%QGJYy z%Zvq|mB zl1T_L?HsiQ@J<$B_~sN!HqlGQh%CzwnQf7i z-@;O7E0>UgEO1yrbEk>~*a1@CNsJ#$rMO;ZPD9jL!o;tyG9`kgM8z$>RHz|HuVHEZ z7>hsj7qd-N6cvY$d?*bC6Q33l|FAxn@nr&J5tEyaXOhRp88zg_x7s#pMM|M%Tft>(Z#rq>s*m~4DZz>U7b(vzxSLd z|MB%>~ zCl5Z{@2YNGRb2CDdgy&%C_@^Mj7++~?2P>|E^uTZ5m=7q3h+iX0QG1eB)trunHd<6 z4*EgYhfCs+QPtZ+5KaB!m)?wh_2*%kI=-SbDY9h2h1NOzh+L88cqU%QA z>xR(%2)Nw=t{c7)3SB??&MAccE5JeptRHzF6}ooxeK-j1Mqg(4fzbU9 zxLpHmIr>%&bR*Cg!6J+}sRT6wc}Xm~cJ#Gq2XSE@UIiOw< zx@Od|G=x?L1~+R42K13NblvEkcZ7and#KfEz!UD^U3ql<=)E|EelJI;ew02Px_^#Qn`Tm~o@Ar9r&mMRl5eq-7 zRXDcG%|;Qsh@1p1I+Z>}ggM*civ4pAhGEpz!ng=r^W)NYCX|Vt2;~xrJM!b4W3HS< z3Au}3idV0HE3DqgY<&$Rv#(#y_*=}ZjT(L$81a((0|URDZwfwghT?o*j+2eRP#!KO zXUW{g0ZijEnKtZHPfe%txBa9`)C8$;>hwW_?A zOb6mvM=QCr(EI=PJl;G-?z4FfA6Y>Z57d~91K+WVF|vX|sQ=$jPN1%F*UYNC2Tn6T zKrWA7?7G@Xr2~6O8jWufRbLjih*@N_6U=q4ltEzq0kV6#Dpq|qb^fRfI)%|AH-opw$ y;T9U8=l|7uzkj^`y#FFWi~b^*)h%*-m~Th7NGK?hUlJ)j5&3yUWZPBAsN7$TB`?AN literal 0 HcmV?d00001 diff --git a/cloudinary-android-test/assets/images/logo.png b/cloudinary-android-test/assets/images/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..10d58c4b80281d2bacb3e8c5439111a242c43972 GIT binary patch literal 3381 zcmV-54a)L~P)nAibqOU||W@$8x`b=@e-#2UnyM{SU-6uG->Y0%@+w zauXm$0^DYv_T%n&G`~2cWSu<^AS{dIa5OXDd*6HWX1K-fL4JPpw+{c)Rf-u%v1Axc;&viopW}xA>yxd;sz92zXY>7G0$olqI_z1GdN$AEP0mSzYU9069pI`v5Tq@CpAjw1D=Uy^lI)hH_&5XCKNSGL+X10k0qd#LWSifH&8iCZJ7x z0Efu-vBpA^72gTLt{?%#&4Bn;0ki_Nch*4pV}MzfgqJ@ET^b~SxE{dBTAnGW^Ffoz z8nE^k3W~iGl0t$!Hfef_=~w~aZqvX59q+Qc!7!$=&_RL(5Ib7Oo+(%8M=S=+I-y&GJSJ%@u*EIl zJABMJuy)q@Blxq4#^09DXYc2rWg$O03)4_R9-2H^L79_#0Pqngd<4H-72rC?^<=d# zLzY*NM{kL-GN~ZiEF^f)|J!QE*kf+<$F+gX2i+KhGIDI6Q+eQ`oVkMW>^!6a41mfx_6h%u z1Dq!Xb}hAK#?S8t*gAl?;dQ`7Ri_P6=(&%j2czgSdqx+ns_J?S$Ov`wncgDhG2nwU z6Uuw1xbA>oGkZWF9xM7I=f80W=ZCPuvh0d&6+|&l>X-iB+$6$i!K#X<3 zeM=CCKt$PCiA-U?|K@r0_s@?gLOsKZA_5IfIURut`wD6737+fSi2(C1euws1u-vY4 z(Ez|Ajn#R~GDG~}&Ue8lHh>uEibI3;BY$yV$;3Jn2O|Lv7Lb5*SP>ke+%!3_3k8!{ zl!l8H?lXO~zJLNFUzQlkHrR_!3H9Lz2H}D@!7kdc3~C*OmRS}t+2f>6z=vDmaT!43 zpKqSMd_5H{AeDh&lMm>UrhhmAnb(JDm`9(Vb4eXNHYq*Pcf+7eI8Zat??^)@y(|qc zw+!Tog?GeVf^43xsxB^m|!vDE_ieN!d5T_jj(>)T{i>i}v4w71jk2TmjqztrVrpft+r# z27|AC&D6Vv7z1*c9=Xt*?m7TLwfo67b)F`$HR0#R8aANY@kH#$k*ZFb-=PemY3fbo zP89>qYO3od9KB7$9u-aQ96|4Z@s=-3?yH6DTq@wk{ z(nO%oMLL@AI*tH5QCMKzShkgQ=2_qJ{>aftsJ@F@niIL>+^+4d4jtwgb>*NP3WG9Y z`px!MA*U4^o*4pKm0Sp)Of1kf6J0Fns!E2S#f^(Ypl_4ht5~w2eM`MlL%bq(m?C5b zrLUU*?>UaSSxYlett*klK$cDW^J|9|0LKcjcVL+-$-iFzvBQr!Kb#vCS!G2<5bHp> zj(N;ugD+0W5M5+8>0E`Q71c}Uf>2i4HvHLb&7f^kNP+FbzwR0onvHQJ@J+iocHncf z8e=6Fl)?xM?9!mD%g-H6eJ@+8=$kDXj6hbE{j5&-J+TJBZ9{;l&(HTO0Jx+!F{?iB znrf)dMhQBzqX~SaGG%A#skB}V%b}}Rq_Gqfj2cNw&NS6l8^BOxnZR@=4s2>Fft>)R zic?(Gs2jOZqEaXo!ptG=qH0@;$j@3;>7XHyW%ZSCuL$6X*;gZ-RS0y6^uEtQU1gc# z4f|8&F1PydU4N_fuSR7@BhJx-WytEUuP{Qg?*$)YB>GClxJm-PeoI7+ZdyY0ZM*ZtVRdaPA=5Hr~(#4nW;zn z?=|Qv?`@Ex>1pa&`l=L&DferHXQiG^Zpc+Jk|uVY+?#@y>Os5VuKeC-L^fj_ zX9ntDxFqM}8WjMKWvdH?(boi7a)aV%qwbMlspg&vCKl5@sZ9L8u`YnFKhPelepQgm z9QvJc_XMfi>*)GB ztHc`4HG_$Af>ZSAK69J)xzZ@ZMcsLMRq3EgN%o-#%3xr`HeX1QT?lQ}VfD7Fs?vtT zDnM@`TS*+yJxk8;y$S+Zbr#Ss1-g(werL6e%Ns0UjSpD;NNXam1hPDLZ1C4(3^pBr zQz=iovFs_7n;P0Z-cSI~43vM)J_aF8b&nfr!_4C9EX`uHG!FG9zt8B9ecUzDM>2pe zHu!C$ij8hc?Q;R}z_H$<_&hU%(h=*QS6~4yfIo1oS5eVVuXC-j4D?}A;U3kqzx4RM zPIOt4rTOHRKnul(Z0svSISn51qstsu3n^rAKR;%$!m$MCsl4rw3y_6eW!J%#mVpxu zO_v8is$c2qOTG?L?BB5PIZhUNRTzkF*8TF zwLbL{h_TIVS8(1-AP*lxOa0;7-;@9*`b?YQb;HZlf^2}8rmu)0PMtw4fSG*GU0ABc zo_@p_pcK^hWWE`9-DSZiopUu{Zs`Vi*6^QWh;Sp2i^ zd@8!w#uho$Z#?cVKK=TK#ruzuwdJ!wJ9+y2)gX+SARFix)0o~Dtnwm7*|tR0>m}pg z9{;xY>nH!X@VGStkP8mvG4qQqCmhHF09h4=cM(dv3_fs>2O(R&H)7AM6AuvOptI>0 zFW!>oaeWo2C={hT|MBy$vLAo(Rn_+=mqJwfvYc)T66B{U{}*5YYcwb29g!=G00000 LNkvXXu0mjfH5q86 literal 0 HcmV?d00001 diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml new file mode 100644 index 00000000..ca2229dd --- /dev/null +++ b/cloudinary-android-test/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + + com.cloudinary + cloudinary-parent + 1.0.15-SNAPSHOT + + + com.cloudinary + cloudinary-android-test + 1.0.15-SNAPSHOT + apk + Cloudinary Android Test Project + + + + com.google.android + android + 4.1.1.4 + provided + + + com.google.android + android-test + 2.3.1 + provided + + + com.cloudinary + cloudinary + jar + 1.0.15-SNAPSHOT + + + com.cloudinary + cloudinary-android + jar + 1.0.15-SNAPSHOT + + + junit + junit + 4.8.1 + + + + ${project.artifactId} + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + + + true + + + true + + + + diff --git a/cloudinary-android-test/project.properties b/cloudinary-android-test/project.properties new file mode 100644 index 00000000..22d0dca6 --- /dev/null +++ b/cloudinary-android-test/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-7 diff --git a/cloudinary-android-test/res/drawable-hdpi/icon.png b/cloudinary-android-test/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 GIT binary patch literal 4147 zcmV-35X|q1P)OwvMs$Q8_8nISM!^>PxsujeDCl4&hPxrxkp%Qc^^|l zp6LqAcf3zf1H4aA1Gv-O6ha)ktct9Y+VA@N^9i;p0H%6v>ZJZYQ`zEa396z-gi{r_ zDz)D=vgRv62GCVeRjK{15j7V@v6|2nafFX6W7z2j1_T0a zLyT3pGTubf1lB5)32>bl0*BflrA!$|_(WD2)iJIfV}37=ZKAC zSe3boYtQ=;o0i>)RtBvsI#iT{0!oF1VFeW`jDjF2Q4aE?{pGCAd>o8Kg#neIh*AMY zLl{;F!vLiem7s*x0<9FKAd6LoPz3~G32P+F+cuGOJ5gcC@pU_?C2fmix7g2)SUaQO$NS07~H)#fn!Q<}KQWtX}wW`g2>cMld+`7Rxgq zChaey66SG560JhO66zA!;sK1cWa2AG$9k~VQY??6bOmJsw9@3uL*z;WWa7(Nm{^TA zilc?y#N9O3LcTo2c)6d}SQl-v-pE4^#wb=s(RxaE28f3FQW(yp$ulG9{KcQ7r>7mQ zE!HYxUYex~*7IinL+l*>HR*UaD;HkQhkL(5I@UwN%Wz504M^d!ylo>ANvKPF_TvA< zkugG5;F6x}$s~J8cnev->_(Ic7%lGQgUi3n#XVo36lUpcS9s z)ympRr7}@|6WF)Ae;D{owN1;aZSR50al9h~?-WhbtKK%bDd zhML131oi1Bu1&Qb$Cp199LJ#;j5d|FhW8_i4KO1OI>}J^p2DfreMSVGY9aFlr&90t zyI2FvxQiKMFviSQeP$Ixh#70qj5O%I+O_I2t2XHWqmh2!1~tHpN3kA4n=1iHj?`@c<~3q^X6_Q$AqTDjBU`|!y<&lkqL|m5tG(b z8a!z&j^m(|;?SW(l*?tZ*{m2H9d&3jqBtXh>O-5e4Qp-W*a5=2NL&Oi62BUM)>zE3 zbSHb>aU3d@3cGggA`C-PsT9^)oy}%dHCaO~nwOrm5E54=aDg(&HR4S23Oa#-a^=}w%g?ZP-1iq8PSjE8jYaGZu z$I)?YN8he?F9>)2d$G6a*zm0XB*Rf&gZAjq(8l@CUDSY1tB#!i> zW$VfG%#SYSiZ};)>pHA`qlfDTEYQEwN6>NNEp+uxuqx({Fgr zjI@!4xRc?vk^9+~eU|mzH__dCDI=xb{Cd}4bELS9xRaS!*FXMwtMR-RR%SLMh0Cjl zencr8#Su<4(%}$yGVBU-HX{18v=yPH*+%^Vtknc>2A;%-~DrYFx^3XfuVgvZ{#1tA== zm3>IzAM2{3Iv_d1XG{P6^tN3|PkJMnjs&CWN7%7_CmjoVakUhsa&dMv==2~^ri?&x zVdv*rnfVyM+I1^Kg*S=23mR@+0T9BWFZUu~@toA8d)fw6be=`Yb6DSX6D?jB%2YT~ z*aHjtIOozfMhA!Jd*?u5_n!SnX>vX`=Ti-1HA4RiE>eI3vTn zz+>Ccf0HX6Ans-ebOB>RJST-Cyr#4XAk+mAlJgdQnoE{^iIN)OcYFSpgJUmXtl@tT z-^ZuUeSj5hSFrQwqX>~EtZ*{>Gi8Bu9_|o06oNtaXP?E936!a@DsvS*tsB@fa6kEA z5GkjwmH?EgpiG&itsB_Tb1NxtFnvxh_s@9KYX1Sttf?AlI~)z zT=6Y7ulx=}<8Scr_UqU-_z)5gPo%050PsbM*ZLno;_-ow&k?FZJtYmb2hPA$LkP)8 z=^d0Q6PImh6Y|QT?{grxj)S=uBKvY2EQUbm@ns9^yKiP~$DcD)c$5Em`zDSScH%iH zVov&m=cMo`1tYwA=!a}vb_ef_{)Q2?FUqn>BR$6phXQRv^1%=YfyE-F$AR4Q?9D!f zCzB^^#td~4u&l~l#rp2QLfe3+_ub9@+|x+m;=2(sQ`s%gO|j$XBb>A7Q(UydipiMw%igcweV#Cr~SP);q>w`bxts_4} znKHg?X==JDkQl3Y>Ckt%`s{n?Nq-1Fw5~%Mq$CAsi-`yu_bKm zxs#QdE7&vgJD%M84f4SNzSDv)S|V?|$!d5a#lhT5>>YWE4NGqa9-fbmV$=)@k&32kdEYetna>=j@0>V8+wRsL;po!3ivVwh<9tn z2S<1u9DAAQ>x1Sn=fk`)At|quvleV($B|#Kap_lB-F^*yV=wZ{9baUu(uXfokr95^ zA*!*W=5a>$2Ps`-F^+qRQT^{*cN>vipT*4!r#p%{(#I7s z0NN94*q?ib$KJjfDI_sjHNdmEVp5wB&j54O#VoFqBwy)gfA$%)4d_X4q${L9Xom2R3xy&ZBSNgt4a1d7K^CDWa9r zVb-_52m}Vp)`9;ZSKd#|U4ZYj5}Gp49{4utST|=c`~(#>KHF6}CCov1iHYw zt{bWo)A@yF2$~c(nR$rSAaFQ$(Wh{vkG1AlutDMw=mM`C`T=X&|Ad9fb5Od}ROt1z zOpczHqrb4Jo^rSCiW#&o(m7jFamnrsTpQb;*h4o8r#$aZ}2RaT-x2u^^ z%u@YyIv$U^u~@9(XGbSwU@fk6SikH>j+D1jQrYTKGJpW%vUT{!d}7THI5&Sa?~MKy zS0-mvMl+BOcroEJ@hN!2H_?coTEJ5Q<;Nd?yx;eIj4{$$E2?YUO|NtNPJ-PdDf;s} zab;}Mz0kbOI}5*w@3gROcnl#5)wQnEhDBfn!Xhy`u>C}*E~vWpO^HS)FC>8^umI=+ z&H;LW6w#;EF`}vQd_9Muru`KnQVPI9U?(sD)&Dg-0j3#(!fNKVZ_GoYH{la~d*1Yh$TI-TL>mI4vpNb@sU2=IZ8vL%AXUx0 zz{K0|nK(yizLHaeW#ZhRfQXoK^}1$=$#1{Yn002ovPDHLkV1n#w+^+xt literal 0 HcmV?d00001 diff --git a/cloudinary-android-test/res/drawable-ldpi/icon.png b/cloudinary-android-test/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c GIT binary patch literal 1723 zcmV;s21NOZP)AReP91Tc8>~sHP8V>Ys(CF=aT`Sk=;|pS}XrJPb~T1dys{sdO&0YpQBSz*~us zcN*3-J_EnE1cxrXiq*F~jZje~rkAe3vf3>;eR)3?Ox=jK*jEU7Do|T`2NqP{56w(* zBAf)rvPB_7rsfeKd0^!CaR%BHUC$tsP9m8a!i@4&TxxzagzsYHJvblx4rRUu#0Jlz zclZJwdC}7S3BvwaIMTiwb!98zRf|zoya>NudJkDGgEYs=q*HmC)>GExofw=92}s;l z_YgKLUT5`<1RBwq{f)K~I%M=gRE6d)b5BP`8{u9x0-wsG%H)w^ zRU7n9FwtlfsZSjiSB(k8~Y5+O>dyoSI477Ly?|FR?m))C!ci%BtY!2Sst8Uri#|SFX&)8{_Ou2 z9r5p3Vz9_GY#%D>%huqp_>U}K45YGy__TE!HZA@bMxX~@{;>cGYRgH~Ih*vd7EgV7h6Pg$#$lH+5=^lj{W80p{{l+;{7_t5cv3xVUy zl_BY4ht1JH*EEeRS{VwTC(QFIVu8zF&P8O$gJsMgsSO35SVvBrX`Vah$Yz2-5T>-`4DJNH;N zlSSY8-mfty+|1~*;BtTwLz_w5 z+lRv)J28~G%ouyvca(@|{2->WsPii&79&nju7ITE6hMX4AQc{|KqZN#)aAvemg3IZ zCr}Y+!r}JU&^>U1C2WyZC<=47itSYQ`?$5{VH?mtFMFFExfYTsfqK%*WzH@Onc#i` zI@a|rm-WbKk{5my{mF}H>Duc$bit&yLAgFfqo2vVbm~?FeG#0F?dSP*kxSo0Ff!o@ z(C}B;r&6pa-NY4;y~5lX8g&*MYQ>yLGd^tDWC4(sGy$Ow-*!eh%xt;>ve|J1q$*w< zh;B#cz!6l2=5bkX#nJ9PJQ`ew8t>7z$bxqf*QB=l2_UB$hK|1EIfloN-jQ=qcwChF zYAkkyp=;FwcnUB3v0=*tMYMA(HdyQ`Og{P|8RRXpj5bgrSmEzSMfBn+{{vpNxw?;5UX;iv9sYxy_`IQHs$i<61a_iv^L>h8s-`D(`e@|IgS*Fj zNGM876Gf;3D8*1UX9a%v>yJKD*QkCwW2AirU(L{qNA)JghmGItc;(H<$!ABY&gBy1vJIEUj-b8%el*o|VkG)LqNx#TG>Jvj^jIte!!+RY z)T4j$7+PoF1AkRBf}R#^T=-q|PaK1$c<4UH)Hpq3$4WA|xtr!ZQLC=*vNE>O6E9kp+5X0eKB$6>C(lPwI@3#oY zhS_%x7e|j!$yG?ECXmh~EH~^OeuK}+sWoJse3Z3?ha3n`MM9KvA?uqpEnBg4Q46)7 zM$p%a$@l;+O}vfvx%XjH`}a{(-HHth9!JaUwV0*VqGR48^gWNYN<&~7x)y$e!X>e` zZ5!6KZoxbKuV9XUDI%#M1~IVh?pNSdeb~6@$y`v|yk=XK+fHxnDqnUK4&=QRNyIVf zYbDM*cI>~qIy*a7=z7uqkw@agd(<=y-Q7L!ty_23SGdXmahO<;N=wB+j;lNm%=OHC zy zU|>La6h%92y4IPufI$9>Xu!@y`TaNgtg&41@PwMwBdmSm7)xAWDLoqjZ==P2#*k7! z3o1)cVSI3KP_!?d8G^Lg0FtLXC~JYdxi|c%h~lXEixY=%VSFF@!*3&&9>(Rb|iK54Cx5;s~PY5iaV1het%w`dgQFBAJ;aFK zImQC}(|QaCFYUm1JVfzSc)ebv=)ObI)0jwJb``}Zj9J0n0Xgn*Zc(rFM9$xh_makZbm-at_v5^SW zM1y1SW@%+FuIy*WR)i3A2N_q;(YO`O!A|Ts^%z}9ZepCj3ytlw#x%N_fNrKKtPh`< z|1{UqF`4LxHaCQ79+E=uUXCOZ35jAMRz%R%0(P!0FMv=sk>Nr8%+OzY^c-M9@+fz=G`qa@v4sF5u-2289-#$**LWnyNNDwDf1( zkUiMnw|y$tn>pQP=Vn!#|17L^5AGrjtBkN$D@v)Z7LXc5EFhLB4<;7Wehh)CMqX|W zqsiZaO^benJ_hwa&V0ub$-_HUk**?g6fm9|!@kguU6*zhK)$qn-<3*kFrYPIaqR=V zUaUvk>@F_89b@tHs8R!*QKY;INJ<2_U+K6Ca3e9Gsl2{qY0%a7J?uICWgHuLfj+MB z=GkAN1&ifT#2u}B+2S#~$5jA(Qn^;H%CCmIae4AE-Dsng|Hl*Ov!z72k3ZnJs{pp| z+pW`DDueC#mEWOf=ucJ!dTL}hzOeiS-i?m2E;`EKz4<&Lu~NnW?peqVU^@<+T3KKu z{yrI%Qy-Z%HEvLUz}n^~m?7x`xuCtNR#L2En!T>dQtIKdS#V-Hzt3RtwTeYtmQ&dR z6qXZvac*oc@BUYEH%@Ylv_1&tSjkbzzU6*h1(3^C`;1z;g_SmOtclS?KWk2VYE zM*oS<=C483XckW?GN|1jfh3Ro(h + + + diff --git a/cloudinary-android-test/res/values/strings.xml b/cloudinary-android-test/res/values/strings.xml new file mode 100644 index 00000000..76455bfc --- /dev/null +++ b/cloudinary-android-test/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello my-android-application-it! + my-android-application-it - tests + diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java new file mode 100644 index 00000000..5c86578a --- /dev/null +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java @@ -0,0 +1,362 @@ +package com.cloudinary.test; + +import java.util.Map; + +import android.test.AndroidTestCase; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; + +@SuppressWarnings({ "unchecked" }) +public class CloudinaryTest extends AndroidTestCase { + + private Cloudinary cloudinary; + + public void setUp() { + this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); + } + + public void testCloudName() { + // should use cloud_name from config + String result = cloudinary.url().generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); + } + + 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); + } + + public void testSecureDistribution() { + // should use default secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); + } + + public void testSecureDistributionOverwrite() { + // should allow overwriting secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); + assertEquals("https://something.else.com/test123/image/upload/test", result); + } + + public void testSecureDistibution() { + // should take secure distribution from config if secure=TRUE + cloudinary.setConfig("secure_distribution", "config.secure.distribution.com"); + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); + } + + public void testSecureAkamai() { + // should default to akamai if secure is given with private_cdn and no + // secure_distribution + cloudinary.setConfig("secure", true); + cloudinary.setConfig("private_cdn",true); + String result = cloudinary.url().generate("test"); + assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); + } + + public void testSecureNonAkamai() { + // should not add cloud_name if private_cdn and secure non akamai + // secure_distribution + cloudinary.setConfig("secure", true); + cloudinary.setConfig("private_cdn",true); + cloudinary.setConfig("secure_distribution", "something.cloudfront.net"); + String result = cloudinary.url().generate("test"); + assertEquals("https://something.cloudfront.net/image/upload/test", result); + } + + public void testHttpPrivateCdn() { + // should not add cloud_name if private_cdn and not secure + cloudinary.setConfig("private_cdn",true); + String result = cloudinary.url().generate("test"); + assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); + } + + public void testFormat() { + // should use format from options + String result = cloudinary.url().format("jpg").generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); + } + + public void testCrop() { + Transformation transformation = new Transformation().width(100).height(101); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); + assertEquals("101", transformation.getHtmlHeight().toString()); + assertEquals("100", transformation.getHtmlWidth().toString()); + transformation = new Transformation().width(100).height(101).crop("crop"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); + } + + public void testVariousOptions() { + // should use x, y, radius, prefix, gravity and quality from options + Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); + } + + public void testTransformationSimple() { + // should support named transformation + Transformation transformation = new Transformation().named("blip"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); + } + + public void testTransformationArray() { + // should support array of named transformations + Transformation transformation = new Transformation().named("blip", "blop"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); + } + + public void testBaseTransformations() { + // should support base transformation + Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); + } + + public void testBaseTransformationArray() { + // should support array of base transformations + Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop") + .width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); + } + + public void testNoEmptyTransformation() { + // should not include empty transformations + Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); + } + + 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); + } + + 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); + } + + public void testIgnoreHttp() { + // should ignore http links only if type is not given or is asset + String result = cloudinary.url().generate("http://test"); + assertEquals("http://test", result); + 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); + } + + 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); + } + + public void testCname() { + // should support extenal cname + String result = cloudinary.url().cname("hello.com").generate("test"); + assertEquals("http://hello.com/test123/image/upload/test", result); + } + + public void testCnameSubdomain() { + // should support extenal cname with cdn_subdomain on + String result = cloudinary.url().cname("hello.com").cdnSubdomain(true).generate("test"); + assertEquals("http://a2.hello.com/test123/image/upload/test", result); + } + + 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); + } + + public void testBackground() { + // should support background + Transformation transformation = new Transformation().background("red"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); + transformation = new Transformation().background("#112233"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); + } + + public void testDefaultImage() { + // should support default_image + Transformation transformation = new Transformation().defaultImage("default"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); + } + + public void testAngle() { + // should support angle + Transformation transformation = new Transformation().angle(12); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); + transformation = new Transformation().angle("exif", "12"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); + } + + public void testOverlay() { + // should support overlay + Transformation transformation = new Transformation().overlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); + // should not pass width/height to html if overlay + transformation = new Transformation().overlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); + } + + public void testUnderlay() { + Transformation transformation = new Transformation().underlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); + // should not pass width/height to html if underlay + transformation = new Transformation().underlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); + } + + public void testFetchFormat() { + // should support format for fetch urls + String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/logo.png"); + assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/logo.png", result); + } + + public void testEffect() { + // should support effect + Transformation transformation = new Transformation().effect("sepia"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); + } + + public void testEffectWithParam() { + // should support effect with param + Transformation transformation = new Transformation().effect("sepia", 10); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); + } + + public void testDensity() { + // should support density + Transformation transformation = new Transformation().density(150); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); + } + + public void testPage() { + // should support page + Transformation transformation = new Transformation().page(5); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); + } + + public void testBorder() { + // should support border + Transformation transformation = new Transformation().border(5, "black"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); + transformation = new Transformation().border(5, "#ffaabbdd"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); + transformation = new Transformation().border("1px_solid_blue"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); + } + + public void testFlags() { + // should support flags + Transformation transformation = new Transformation().flags("abc"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); + transformation = new Transformation().flags("abc", "def"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); + } + + public void testImageTag() { + Transformation transformation = new Transformation().width(100).height(101).crop("crop"); + String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals( + "my image", + result); + } + + public void testFolders() { + // should add version if public_id contains / + String result = cloudinary.url().generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); + result = cloudinary.url().version(123).generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); + } + + public void testFoldersWithVersion() { + // should not add version if public_id contains version already + String result = cloudinary.url().generate("v1234/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); + } + + public void testShorten() { + // should allow to shorted image/upload urls + String result = cloudinary.url().shorten(true).generate("test"); + assertEquals("http://res.cloudinary.com/test123/iu/test", result); + } + + @SuppressWarnings("unchecked") + public void testEscapePublicId() { + // should escape public_ids + Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + for (Map.Entry entry : tests.entrySet()) { + String result = cloudinary.url().generate(entry.getKey()); + assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); + } + } + + public void testRecommendedIdentifierFormat() { + String imageIdentifier = "image:upload:dfhjghjkdisudgfds7iyf.jpg"; + String[] components = imageIdentifier.split(":"); + + String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); + assertEquals("http://res.cloudinary.com/test123/image/upload/dfhjghjkdisudgfds7iyf.jpg", url); + + String rawIdentifier = "raw:upload:cguysfdsfuydsfyuds31.doc"; + components = rawIdentifier.split(":"); + + url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); + assertEquals("http://res.cloudinary.com/test123/raw/upload/cguysfdsfuydsfyuds31.doc", url); + } + + public void testSignedUrl() { + // should correctly sign a url + String expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"; + String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) + .generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"; + actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; + actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + } + +} diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java new file mode 100644 index 00000000..0d2c04f2 --- /dev/null +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -0,0 +1,306 @@ +package com.cloudinary.test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONArray; +import org.json.JSONObject; + +import android.test.InstrumentationTestCase; +import android.util.Log; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.android.Utils; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; + +public class UploaderTest extends InstrumentationTestCase { + + private Cloudinary cloudinary; + private static boolean first = true; + + public void setUp() throws Exception { + this.cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getInstrumentation().getContext())); + if (first) { + first = false; + if (cloudinary.getStringConfig("api_secret") == null) { + Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); + } + } + } + + protected InputStream getAssetStream(String filename) throws IOException { + return getInstrumentation().getContext().getAssets().open(filename); + } + + public void testUpload() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("colors", true))); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + assertNotNull(result.get("colors")); + assertNotNull(result.get("predominant")); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUnsignedUpload() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream("images/logo.png"), "sample_preset_dhfjhriu", + ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadUrl() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadDataUri() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject( + cloudinary + .uploader() + .upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", + ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 16L); + assertEquals(result.getLong("height"), 16L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadExternalSignature() throws Exception { + String apiSecret = cloudinary.getStringConfig("api_secret"); + if (apiSecret == null) + return; + Map config = new HashMap(); + config.put("api_key", cloudinary.getStringConfig("api_key")); + config.put("cloud_name", cloudinary.getStringConfig("cloud_name")); + + Map params = new HashMap(); + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); + Cloudinary emptyCloudinary = new Cloudinary(config); + JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream("images/logo.png"), params)); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testRename() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.emptyMap())); + + cloudinary.uploader().rename(result.getString("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); + + JSONObject result2 = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/favicon.ico"), ObjectUtils.emptyMap())); + boolean error_found = false; + try { + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); + } catch (Exception e) { + error_found = true; + } + assertTrue(error_found); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); + } + + public void testExplicit() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().explicit("cloudinary", + ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name"))); + String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png") + .version(result.get("version")).generate("cloudinary"); + assertEquals(result.getJSONArray("eager").getJSONObject(0).get("url"), url); + } + + public void testEager() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + cloudinary.uploader().upload(getAssetStream("images/logo.png"), + ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + } + + public void testHeaders() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", new String[] { "Link: 1" })); + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + } + + public void testText() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().text("hello world", ObjectUtils.emptyMap())); + assertTrue(result.getInt("width") > 1); + assertTrue(result.getInt("height") > 1); + } + + public void testSprite() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + JSONObject result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap())); + assertEquals(2, result.getJSONObject("image_infos").length()); + result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100"))); + assertTrue((result.getString("css_url")).contains("w_100")); + result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", + ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg"))); + assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); + } + + public void testMulti() throws Exception { + if (cloudinary.getStringConfig("api_secret") == null) + return; + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + JSONObject result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap())); + assertTrue((result.getString("url")).endsWith(".gif")); + result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100"))); + assertTrue((result.getString("url")).contains("w_100")); + result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf"))); + assertTrue((result.getString("url")).contains("w_111")); + assertTrue((result.getString("url")).endsWith(".pdf")); + } + + public void testUniqueFilename() throws Exception { + + File f = new File(getInstrumentation().getContext().getCacheDir() + "/logo.png"); + + InputStream is = getAssetStream("images/logo.png"); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + + FileOutputStream fos = new FileOutputStream(f); + fos.write(buffer); + fos.close(); + + JSONObject result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true))); + assertTrue(result.getString("public_id").matches("logo_[a-z0-9]{6}")); + result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true, "unique_filename", false))); + assertEquals(result.getString("public_id"), "logo"); + } + + public void testFaceCoordinates() throws Exception { + // should allow sending face coordinates + Coordinates coordinates = new Coordinates(); + Rectangle rect1 = new Rectangle(121, 31, 231, 182); + Rectangle rect2 = new Rectangle(120, 30, 229, 270); + coordinates.addRect(rect1); + coordinates.addRect(rect2); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); + JSONArray resultFaces = result.getJSONArray("faces"); + assertEquals(2, resultFaces.length()); + + JSONArray resultCoordinates = resultFaces.getJSONArray(0); + + assertEquals(rect1.x, resultCoordinates.getInt(0)); + assertEquals(rect1.y, resultCoordinates.getInt(1)); + assertEquals(rect1.width, resultCoordinates.getInt(2)); + assertEquals(rect1.height, resultCoordinates.getInt(3)); + + resultCoordinates = resultFaces.getJSONArray(1); + + assertEquals(rect2.x, resultCoordinates.getInt(0)); + assertEquals(rect2.y, resultCoordinates.getInt(1)); + assertEquals(rect2.width, resultCoordinates.getInt(2)); + assertEquals(rect2.height, resultCoordinates.getInt(3)); + + } + + public void testContext() throws Exception { + // should allow sending context + @SuppressWarnings("rawtypes") + Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("context", context)); + } + + public void testModerationRequest() throws Exception { + // should support requesting manual moderation + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("moderation", "manual"))); + assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); + assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); + } + + public void testRawConvertRequest() { + // should support requesting raw conversion + try { + cloudinary.uploader().upload(getAssetStream("docx.docx"), ObjectUtils.asMap("raw_convert", "illegal", "resource_type", "raw")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testCategorizationRequest() { + // should support requesting categorization + try { + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testDetectionRequest() { + // should support requesting detection + try { + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testAutoTaggingRequest() { + // should support requesting auto tagging + + try { + cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("auto_tagging", 0.5f)); + } catch (Exception e) { + Log.i("DANIEL",e.getMessage()); + for (int i = 0; i < e.getStackTrace().length; i++) { + StackTraceElement x = e.getStackTrace()[i]; + Log.i("DANIEL",x.getClassName()+"."+x.getMethodName()+" "+x.getLineNumber()); + } + + + assertTrue(e.getMessage().matches("^Must use(.*)")); + } + } +} diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 3eafd04c..33310aba 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -1,7 +1,6 @@ + 4.0.0 - - UTF-8 diff --git a/cloudinary-android/project.properties b/cloudinary-android/project.properties new file mode 100644 index 00000000..484dab07 --- /dev/null +++ b/cloudinary-android/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 +android.library=true diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java new file mode 100644 index 00000000..211473bb --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java @@ -0,0 +1,17 @@ +package com.cloudinary.android; + +import java.util.Map; + +import com.cloudinary.Api.HttpMethod; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.strategies.AbstractApiStrategy; + +public class ApiStrategy extends AbstractApiStrategy { + + @SuppressWarnings("rawtypes") + @Override + public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + throw new Exception("not implemented"); + } + +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java new file mode 100644 index 00000000..51cc8f63 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -0,0 +1,125 @@ +package com.cloudinary.android; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * This utility class provides an abstraction layer for sending multipart HTTP + * POST requests to a web server. + * + * @author www.codejava.net + * @author Cloudinary + */ +public class MultipartUtility { + private final String boundary; + private static final String LINE_FEED = "\r\n"; + private HttpURLConnection httpConn; + private String charset; + private OutputStream outputStream; + private PrintWriter writer; + + public final static String VERSION = "1.0.2"; + public final static String USER_AGENT = "cld-android-" + VERSION; + + + /** + * This constructor initializes a new HTTP POST request with content type is + * set to multipart/form-data + * + * @param requestURL + * @param charset + * @throws IOException + */ + public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { + this.charset = charset; + this.boundary = boundary; + + URL url = new URL(requestURL); + httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setDoOutput(true); // indicates POST method + httpConn.setDoInput(true); + httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + httpConn.setRequestProperty("User-Agent", USER_AGENT); + outputStream = httpConn.getOutputStream(); + writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); + } + + /** + * Adds a form field to the request + * + * @param name + * field name + * @param value + * field value + */ + public void addFormField(String name, String value) { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); + writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); + writer.append(LINE_FEED); + writer.append(value).append(LINE_FEED); + writer.flush(); + } + + /** + * Adds a upload file section to the request + * + * @param fieldName + * name attribute in + * @param uploadFile + * a File to be uploaded + * @throws IOException + */ + public void addFilePart(String fieldName, File uploadFile) throws IOException { + String fileName = uploadFile.getName(); + FileInputStream inputStream = new FileInputStream(uploadFile); + addFilePart(fieldName, inputStream, fileName); + } + + public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); + writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED); + writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); + writer.append(LINE_FEED); + writer.flush(); + + byte[] buffer = new byte[4096]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + inputStream.close(); + + writer.append(LINE_FEED); + writer.flush(); + } + + public void addFilePart(String fieldName, InputStream inputStream) throws IOException { + addFilePart(fieldName, inputStream, "file"); + } + + /** + * Completes the request and receives response from the server. + * + * @return a list of Strings as response in case the server returned status + * OK, otherwise an exception is thrown. + * @throws IOException + */ + public HttpURLConnection execute() throws IOException { + writer.append("--" + boundary + "--").append(LINE_FEED); + writer.close(); + + return httpConn; + } + +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 47401165..25d25bde 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -1,256 +1,61 @@ -package com.cloudinary; +package com.cloudinary.android; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; -import java.util.List; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; -import android.text.TextUtils; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class Uploader { - private final Cloudinary cloudinary; +public class UploaderStrategy extends AbstractUploaderStrategy { - public Uploader(Cloudinary cloudinary) { - this.cloudinary = cloudinary; - } - 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"}; - - public Map buildUploadParams(Map options) { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("public_id", (String) options.get("public_id")); - params.put("callback", (String) options.get("callback")); - params.put("format", (String) options.get("format")); - params.put("type", (String) options.get("type")); - for (String attr : BOOLEAN_UPLOAD_OPTIONS) { - Boolean value = Cloudinary.asBoolean(options.get(attr), null); - if (value != null) - params.put(attr, value.toString()); - } - params.put("notification_url", (String) options.get("notification_url")); - params.put("eager_notification_url", (String) options.get("eager_notification_url")); - params.put("proxy", (String) options.get("proxy")); - params.put("folder", (String) options.get("folder")); - params.put("allowed_formats", TextUtils.join(",", Cloudinary.asArray(options.get("allowed_formats")))); - params.put("moderation", options.get("moderation")); - params.put("upload_preset", (String) options.get("upload_preset")); - if (options.get("signature") == null) { - params.put("eager", buildEager((List) options.get("eager"))); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - Util.processWriteParameters(options, params); - } else { - params.put("eager", (String) options.get("eager")); - params.put("transformation", (String) options.get("transformation")); - params.put("headers", (String) options.get("headers")); - params.put("tags", (String) options.get("tags")); - params.put("face_coordinates", (String) options.get("face_coordinates")); - params.put("context", (String) options.get("context")); - params.put("ocr", (String) options.get("ocr")); - params.put("raw_convert", (String) options.get("raw_convert")); - params.put("categorization", (String) options.get("categorization")); - params.put("detection", (String) options.get("detection")); - params.put("similarity_search", (String) options.get("similarity_search")); - params.put("auto_tagging", (String) options.get("auto_tagging")); - } - - return params; - } - - public JSONObject upload(Object file, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = buildUploadParams(options); - return callApi("upload", params, options, file); - } - - public JSONObject unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - options = new HashMap(options); - options.put("upload_preset", uploadPreset); - options.put("unsigned", true); - return upload(file, options); - } - - public JSONObject destroy(String publicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("public_id", publicId); - params.put("invalidate", Cloudinary.asBoolean(options.get("invalidate"), false).toString()); - return callApi("destroy", params, options, null); - } - - public JSONObject rename(String fromPublicId, String toPublicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("overwrite", Cloudinary.asBoolean(options.get("overwrite"), false).toString()); - params.put("from_public_id", fromPublicId); - params.put("to_public_id", toPublicId); - return callApi("rename", params, options, null); - } - - public JSONObject explicit(String publicId, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("callback", (String) options.get("callback")); - params.put("type", (String) options.get("type")); - params.put("eager", buildEager((List) options.get("eager"))); - params.put("headers", Util.buildCustomHeaders(options.get("headers"))); - params.put("tags", TextUtils.join(",", Cloudinary.asArray(options.get("tags")))); - return callApi("explicit", params, options, null); - } - - public JSONObject generate_sprite(String tag, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transParam = options.get("transformation"); - Transformation transformation = null; - if (transParam instanceof Transformation) { - transformation = new Transformation((Transformation) transParam); - } else if (transParam instanceof String) { - transformation = new Transformation().rawTransformation((String) transParam); - } else { - transformation = new Transformation(); - } - String format = (String) options.get("format"); - if (format != null) { - transformation.fetchFormat(format); + @SuppressWarnings("rawtypes") + @Override + public Map callApi(String action, Map params, Map options, Object file) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); } - params.put("transformation", transformation.generate()); - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); - return callApi("sprite", params, options, null); - } + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - public JSONObject multi(String tag, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - params.put("async", Cloudinary.asBoolean(options.get("async"), false).toString()); - return callApi("multi", params, options, null); - } - - public JSONObject explode(String public_id, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("public_id", public_id); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - return callApi("explode", params, options, null); - } - - // options may include 'exclusive' (boolean) which causes clearing this tag - // from all other resources - public JSONObject addTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - boolean exclusive = Cloudinary.asBoolean(options.get("exclusive"), false); - String command = exclusive ? "set_exclusive" : "add"; - return callTagsApi(tag, command, publicIds, options); - } - - public JSONObject removeTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - return callTagsApi(tag, "remove", publicIds, options); - } - - public JSONObject replaceTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - return callTagsApi(tag, "replace", publicIds, options); - } - - public JSONObject callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("tag", tag); - params.put("command", command); - params.put("type", (String) options.get("type")); - params.put("public_ids", Arrays.asList(publicIds)); - return callApi("tags", params, options, null); - } - - private final static String[] TEXT_PARAMS = { "public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", - "font_style", "background", "opacity", "text_decoration" }; - - public JSONObject text(String text, Map options) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - Map params = new HashMap(); - params.put("text", text); - for (String param : TEXT_PARAMS) { - params.put(param, Cloudinary.asString(options.get(param))); - } - return callApi("text", params, options, null); - } - - public JSONObject callApi(String action, Map params, Map options, Object file) throws IOException { - if (options == null) options = Cloudinary.emptyMap(); - boolean returnError = Cloudinary.asBoolean(options.get("return_error"), false); - String apiKey = Cloudinary.asString(options.get("api_key"), this.cloudinary.config.apiKey); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().getStringConfig("api_key")); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); - if (Boolean.TRUE.equals(options.get("unsigned"))) { + if (Boolean.TRUE.equals(options.get("unsigned"))) { // Nothing to do - } else if (options.containsKey("signature") && options.containsKey("timestamp")) { + } else if (options.containsKey("signature") && options.containsKey("timestamp")) { params.put("timestamp", options.get("timestamp")); params.put("signature", options.get("signature")); params.put("api_key", apiKey); - } else { - String apiSecret = Cloudinary.asString(options.get("api_secret"), this.cloudinary.config.apiSecret); + } else { + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().getStringConfig("api_secret")); if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); + params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); params.put("api_key", apiKey); - } - - String apiUrl = cloudinary.cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", cloudinary.randomPublicId()); + } + String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId()); // Remove blank parameters for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof String || param.getValue() instanceof Integer) { - String value = Cloudinary.asString(param.getValue()); - if (!TextUtils.isEmpty(value)) { - multipart.addFormField(param.getKey(), value); - } - } else if (param.getValue() instanceof Collection) { + if (param.getValue() instanceof Collection) { for (Object value : (Collection) param.getValue()) { - multipart.addFormField(param.getKey()+"[]", Cloudinary.asString(value)); + multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); + } + } else { + if (StringUtils.isNotBlank(param.getValue())) { + multipart.addFormField(param.getKey(), param.getValue().toString()); } } } @@ -266,7 +71,7 @@ public JSONObject callApi(String action, Map params, Map options multipart.addFilePart("file", (InputStream) file); } HttpURLConnection connection = multipart.execute(); - int code; + int code; try { code = connection.getResponseCode(); } catch (IOException e) { @@ -277,7 +82,7 @@ public JSONObject callApi(String action, Map params, Map options throw e; } } - InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); + InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); String responseData = readFully(responseStream); connection.disconnect(); @@ -296,12 +101,12 @@ public JSONObject callApi(String action, Map params, Map options throw new RuntimeException(error.getString("message")); } } - return result; + return ObjectUtils.toMap(result); } catch (JSONException e) { throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); } } - + protected static String readFully(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; @@ -311,26 +116,4 @@ protected static String readFully(InputStream in) throws IOException { } return new String(baos.toByteArray()); } - - protected String buildEager(List transformations) { - if (transformations == null) { - return null; - } - List eager = new ArrayList(); - for (Transformation transformation : transformations) { - List single_eager = new ArrayList(); - String transformationString = transformation.generate(); - if (!TextUtils.isEmpty(transformationString)) { - single_eager.add(transformationString); - } - if (transformation instanceof EagerTransformation) { - EagerTransformation eagerTransformation = (EagerTransformation) transformation; - if (!TextUtils.isEmpty(eagerTransformation.getFormat())) { - single_eager.add(eagerTransformation.getFormat()); - } - } - eager.add(TextUtils.join("/", single_eager)); - } - return TextUtils.join("|", eager); - } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java new file mode 100644 index 00000000..4752d20f --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java @@ -0,0 +1,33 @@ +package com.cloudinary.android; + +import java.net.URISyntaxException; + +import android.net.Uri; + +import com.cloudinary.strategies.AbstractUrlBuilderStrategy; + +public class UrlBuilderStrategy extends AbstractUrlBuilderStrategy { + + private Uri.Builder builder; + + public UrlBuilderStrategy() throws URISyntaxException { + super(); + } + + @Override + public void addParam(String key, Object value) { + builder.appendQueryParameter(key, (String) value); + } + + @Override + public String url() { + return builder.toString(); + } + + @Override + public void initialize() throws Exception { + this.builder = Uri.parse(source).buildUpon(); + + } + +} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java b/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java new file mode 100644 index 00000000..28e9b956 --- /dev/null +++ b/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java @@ -0,0 +1,22 @@ +package com.cloudinary.android; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; + +public class Utils { + public static String cloudinaryUrlFromContext(Context context){ + String url=""; + try { + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo info = packageManager.getApplicationInfo( context.getPackageName(), PackageManager.GET_META_DATA); + if (info != null && info.metaData != null) { + url = (String) info.metaData.get("CLOUDINARY_URL"); + } + } catch (NameNotFoundException e) { + // No metadata found + } + return url; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 33db0494..71f9dc95 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -75,6 +75,7 @@ public static void registerUrlBuilderStrategy(String className){ private void loadStrategies() { uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); + if (uploaderStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); } @@ -143,9 +144,8 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) if (param.getValue() instanceof Collection) { params.add(param.getKey() + "=" + StringUtils.join((Collection) param.getValue(), ",")); } else { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - params.add(param.getKey() + "=" + value); + if (StringUtils.isNotBlank(param.getValue())) { + params.add(param.getKey() + "=" + param.getValue().toString()); } } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index f8d12f4f..ed9f3ed3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -30,7 +30,7 @@ public Map callApi(String action, Map params, Map options, Objec public Uploader(Cloudinary cloudinary,AbstractUploaderStrategy strategy) { this.cloudinary = cloudinary; this.strategy = strategy; - this.strategy.init(this); + strategy.init(this); } public Cloudinary cloudinary(){ @@ -56,7 +56,7 @@ public Map upload(Object file, Map options) throws IOException { Map params = buildUploadParams(options); return callApi("upload", params, options, file); } - + public Map uploadLargeRaw(Object file, Map options) throws IOException { return uploadLargeRaw(file, options, 20000000); } 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 b90e4ccf..29dcd37b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -74,6 +74,10 @@ public static String escapeHtml(String input) { return HtmlEscape.escape(input); } + public static boolean isNotBlank(Object input) { + if (input==null) return false; + return !isBlank(input.toString()); + } public static boolean isNotBlank(String input) { return !isBlank(input); } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index c0e1e084..0bf2e90c 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -25,7 +25,6 @@ public class CloudinaryTest { public void setUp() { this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); } - @Test public void testCloudName() { diff --git a/pom.xml b/pom.xml index 637917d5..75d4666a 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ cloudinary-core cloudinary-android + cloudinary-android-test cloudinary-taglib cloudinary-http42 From cf44766b760ae1805d7da178b8c6b55e1fac6386 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Thu, 18 Sep 2014 12:16:50 +0300 Subject: [PATCH 008/592] cloudinary credentials removed --- cloudinary-android-test/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-android-test/AndroidManifest.xml b/cloudinary-android-test/AndroidManifest.xml index d4fc81a5..3c47997d 100644 --- a/cloudinary-android-test/AndroidManifest.xml +++ b/cloudinary-android-test/AndroidManifest.xml @@ -14,7 +14,7 @@ - + \ No newline at end of file From f02ecbe7cafcf8b97ace59747b5b5f5638cf98f3 Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Tue, 14 Oct 2014 11:58:09 +0300 Subject: [PATCH 009/592] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0414a12f..7e2227ed 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary - 1.0.8 + 1.0.14 Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/tarball/master) From 9d327b420b5ad882fa43bdf3b4f15c8d7a39b2c6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 29 Oct 2014 16:17:56 +0200 Subject: [PATCH 010/592] merged config & builder --- .../com/cloudinary/test/CloudinaryTest.java | 14 +- .../com/cloudinary/test/UploaderTest.java | 40 +-- .../cloudinary/android/UploaderStrategy.java | 4 +- .../main/java/com/cloudinary/Cloudinary.java | 57 ++-- .../java/com/cloudinary/Configuration.java | 289 ++++++++++++++++++ .../main/java/com/cloudinary/Uploader.java | 2 +- .../src/main/java/com/cloudinary/Url.java | 228 ++++++++------ .../com/cloudinary/utils/ObjectUtils.java | 26 +- .../com/cloudinary/http42/ApiStrategy.java | 9 +- .../cloudinary/http42/UploaderStrategy.java | 12 +- .../java/com/cloudinary/test/ApiTest.java | 4 +- .../com/cloudinary/test/CloudinaryTest.java | 14 +- .../com/cloudinary/test/UploaderTest.java | 12 +- .../taglib/CloudinaryJsConfigTag.java | 26 +- .../taglib/CloudinaryUploadTag.java | 2 +- 15 files changed, 545 insertions(+), 194 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/Configuration.java diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java index 5c86578a..f26bba80 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java @@ -43,7 +43,7 @@ public void testSecureDistributionOverwrite() { public void testSecureDistibution() { // should take secure distribution from config if secure=TRUE - cloudinary.setConfig("secure_distribution", "config.secure.distribution.com"); + cloudinary.config.secureDistribution = "config.secure.distribution.com"; String result = cloudinary.url().secure(true).generate("test"); assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); } @@ -51,8 +51,8 @@ public void testSecureDistibution() { public void testSecureAkamai() { // should default to akamai if secure is given with private_cdn and no // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn",true); + cloudinary.config.secure = true; + cloudinary.config.privateCdn = true ; String result = cloudinary.url().generate("test"); assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); } @@ -60,16 +60,16 @@ public void testSecureAkamai() { public void testSecureNonAkamai() { // should not add cloud_name if private_cdn and secure non akamai // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn",true); - cloudinary.setConfig("secure_distribution", "something.cloudfront.net"); + cloudinary.config.secure = true; + cloudinary.config.privateCdn = true; + cloudinary.config.secureDistribution = "something.cloudfront.net"; String result = cloudinary.url().generate("test"); assertEquals("https://something.cloudfront.net/image/upload/test", result); } public void testHttpPrivateCdn() { // should not add cloud_name if private_cdn and not secure - cloudinary.setConfig("private_cdn",true); + cloudinary.config.privateCdn = true ; String result = cloudinary.url().generate("test"); assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); } diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 0d2c04f2..e6ba8dce 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -30,7 +30,7 @@ public void setUp() throws Exception { this.cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getInstrumentation().getContext())); if (first) { first = false; - if (cloudinary.getStringConfig("api_secret") == null) { + if (cloudinary.config.apiSecret == null) { Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); } } @@ -41,7 +41,7 @@ protected InputStream getAssetStream(String filename) throws IOException { } public void testUpload() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("colors", true))); assertEquals(result.getLong("width"), 241L); @@ -51,12 +51,12 @@ public void testUpload() throws Exception { Map to_sign = new HashMap(); to_sign.put("public_id", result.getString("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } public void testUnsignedUpload() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream("images/logo.png"), "sample_preset_dhfjhriu", ObjectUtils.emptyMap())); @@ -65,12 +65,14 @@ public void testUnsignedUpload() throws Exception { Map to_sign = new HashMap(); to_sign.put("public_id", result.getString("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + Log.d("TestRunner",cloudinary.config.apiSecret); + + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } public void testUploadUrl() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap())); assertEquals(result.getLong("width"), 241L); @@ -78,12 +80,12 @@ public void testUploadUrl() throws Exception { Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } public void testUploadDataUri() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject( cloudinary @@ -95,17 +97,17 @@ public void testUploadDataUri() throws Exception { Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } public void testUploadExternalSignature() throws Exception { - String apiSecret = cloudinary.getStringConfig("api_secret"); + String apiSecret = cloudinary.config.apiSecret; if (apiSecret == null) return; Map config = new HashMap(); - config.put("api_key", cloudinary.getStringConfig("api_key")); - config.put("cloud_name", cloudinary.getStringConfig("cloud_name")); + config.put("api_key", cloudinary.config.apiKey); + config.put("cloud_name", cloudinary.config.cloudName); Map params = new HashMap(); params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); @@ -122,7 +124,7 @@ public void testUploadExternalSignature() throws Exception { } public void testRename() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.emptyMap())); @@ -140,7 +142,7 @@ public void testRename() throws Exception { } public void testExplicit() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name"))); @@ -150,21 +152,21 @@ public void testExplicit() throws Exception { } public void testEager() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } public void testHeaders() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", new String[] { "Link: 1" })); cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } public void testText() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; JSONObject result = new JSONObject(cloudinary.uploader().text("hello world", ObjectUtils.emptyMap())); assertTrue(result.getInt("width") > 1); @@ -172,7 +174,7 @@ public void testText() throws Exception { } public void testSprite() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); @@ -186,7 +188,7 @@ public void testSprite() throws Exception { } public void testMulti() throws Exception { - if (cloudinary.getStringConfig("api_secret") == null) + if (cloudinary.config.apiSecret == null) return; cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 25d25bde..c84dafb1 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -26,7 +26,7 @@ public Map callApi(String action, Map params, Map options, Objec } boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().getStringConfig("api_key")); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); @@ -37,7 +37,7 @@ public Map callApi(String action, Map params, Map options, Objec params.put("signature", options.get("signature")); params.put("api_key", apiKey); } else { - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().getStringConfig("api_secret")); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 71f9dc95..cfbe723f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -18,9 +18,9 @@ import org.apache.http.conn.ClientConnectionManager; import com.cloudinary.strategies.AbstractApiStrategy; -import com.cloudinary.strategies.StrategyLoader; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.AbstractUrlBuilderStrategy; +import com.cloudinary.strategies.StrategyLoader; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -39,7 +39,7 @@ public class Cloudinary { public final static String VERSION = "1.0.14"; public final static String USER_AGENT = "cld-java-" + VERSION; - private final Map config = new HashMap(); + public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; private AbstractUrlBuilderStrategy urlBuilderStrategy; @@ -93,20 +93,22 @@ private void loadStrategies() { public Cloudinary(Map config) { loadStrategies(); - this.config.putAll(config); + this.config = new Configuration(config); } public Cloudinary(String cloudinaryUrl) { loadStrategies(); - initFromUrl(cloudinaryUrl); + this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); } public Cloudinary() { loadStrategies(); String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); if (cloudinaryUrl != null) { - initFromUrl(cloudinaryUrl); + this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + }else { + this.config = new Configuration(); } } @@ -117,8 +119,8 @@ public Url url() { public String cloudinaryApiUrl(String action, Map options) { String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), - ObjectUtils.asString(this.config.get("upload_prefix"), "https://api.cloudinary.com")); - String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.get("cloud_name"))); + ObjectUtils.asString(this.config.uploadPrefix, "https://api.cloudinary.com")); + String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.cloudName)); if (cloud_name == null) throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); @@ -161,10 +163,10 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) } public void signRequest(Map params, Map options) { - String apiKey = ObjectUtils.asString(options.get("api_key"), this.getStringConfig("api_key")); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.config.apiKey); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.getStringConfig("api_secret")); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.config.apiSecret); if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); Util.clearEmpty(params); @@ -208,42 +210,31 @@ public String zipDownload(String tag, Map options) throws Except return builder.url(); } - protected void initFromUrl(String cloudinaryUrl) { + protected Map parseConfigUrl(String cloudinaryUrl) { + Map params = new HashMap(); URI cloudinaryUri = URI.create(cloudinaryUrl); - setConfig("cloud_name", cloudinaryUri.getHost()); - String[] creds = cloudinaryUri.getUserInfo().split(":"); - setConfig("api_key", creds[0]); - setConfig("api_secret", creds[1]); - setConfig("private_cdn", StringUtils.isNotBlank(cloudinaryUri.getPath())); - setConfig("secure_distribution", cloudinaryUri.getPath()); + params.put("cloud_name", cloudinaryUri.getHost()); + if (cloudinaryUri.getUserInfo() != null) { + String[] creds = cloudinaryUri.getUserInfo().split(":"); + params.put("api_key", creds[0]); + params.put("api_secret", creds[1]); + } + params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); + params.put("secure_distribution", cloudinaryUri.getPath()); if (cloudinaryUri.getQuery() != null) { for (String param : cloudinaryUri.getQuery().split("&")) { String[] keyValue = param.split("="); try { - setConfig(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unexpected exception", e); } } } + return params; } - public boolean getBooleanConfig(String key, boolean default_value) { - return ObjectUtils.asBoolean(this.config.get(key), default_value); - } - - public String getStringConfig(String key, String default_value) { - return ObjectUtils.asString(this.config.get(key), default_value); - } - - public String getStringConfig(String key) { - return ObjectUtils.asString(this.config.get(key)); - } - - public void setConfig(String key, Object value) { - this.config.put(key, value); - } - + public Cloudinary withConnectionManager(ClientConnectionManager connectionManager) { this.connectionManager = connectionManager; return this; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java new file mode 100644 index 00000000..1670b242 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -0,0 +1,289 @@ +package com.cloudinary; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.Map; + +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +/** +* Configuration object for a {@link Cloudinary} instance +*/ +public class Configuration { + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; + public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; + 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.0.2"; + public final static String USER_AGENT = "cld-android-" + VERSION; + + public String cloudName; + public String apiKey; + public String apiSecret; + public String secureDistribution; + public String cname; + public String uploadPrefix; + public boolean secure; + public boolean privateCdn; + public boolean cdnSubdomain; + public boolean shorten; + public String callback; + public String proxyHost; + public int proxyPort; + public Configuration(){ + } + + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort) { + this.cloudName = cloudName; + this.apiKey = apiKey; + this.apiSecret = apiSecret; + this.secureDistribution = secureDistribution; + this.cname = cname; + this.uploadPrefix = uploadPrefix; + this.secure = secure; + this.privateCdn = privateCdn; + this.cdnSubdomain = cdnSubdomain; + this.shorten = shorten; + this.callback = callback; + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + } + + + @SuppressWarnings("rawtypes") + public Configuration(Map config) { + update(config); + } + + @SuppressWarnings("rawtypes") + public void update(Map config) { + this.cloudName = (String) config.get("cloud_name"); + this.apiKey = (String) config.get("api_key"); + 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.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); + this.uploadPrefix = (String) config.get("upload_prefix"); + this.callback = (String) config.get("callback"); + this.proxyHost = (String) config.get("proxy_host"); + this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); + } + + + + public Configuration(Configuration other) { + this.cloudName = other.cloudName; + this.apiKey = other.apiKey; + this.apiSecret = other.apiSecret; + this.secureDistribution = other.secureDistribution; + this.cname = other.cname; + this.uploadPrefix = other.uploadPrefix; + this.secure = other.secure; + this.privateCdn = other.privateCdn; + this.cdnSubdomain = other.cdnSubdomain; + this.shorten = other.shorten; + this.callback = other.callback; + } + + + /** + * Create a new Configuration from an existing one + * @param other + * @return + */ + public static Configuration from(Configuration other) { + return new Builder().from(other).build(); + } + + /** + * Create a Configuration from a cloudinary url + * + * Example url: cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7 + * + * @param cloudinaryUrl configuration url + * @return + */ + public static Configuration from(String cloudinaryUrl) { + return from(parseConfigUrl(cloudinaryUrl)); + } + + + private static Configuration parseConfigUrl(String cloudinaryUrl) { + Builder builder = new Builder(); + + URI cloudinaryUri = URI.create(cloudinaryUrl); + builder.setCloudName(cloudinaryUri.getHost()); + if (cloudinaryUri.getUserInfo() != null) { + String[] creds = cloudinaryUri.getUserInfo().split(":"); + builder.setApiKey(creds[0]); + builder.setApiSecret(creds[1]); + } + builder.setPrivateCdn(!StringUtils.isEmpty(cloudinaryUri.getPath())); + builder.setSecureDistribution(cloudinaryUri.getPath()); + if (cloudinaryUri.getQuery() != null) { + for (String param : cloudinaryUri.getQuery().split("&")) { + String[] keyValue = param.split("="); + String val = null; + try { +// params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + val = URLDecoder.decode(keyValue[1], "ASCII"); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Error decoding cloudinaryUrl", e); + } + + String key = keyValue[0]; + if (key.equals("cname")){ + builder.setCname(val); + }else if (key.equals("upload_prefix")){ + builder.setUploadPrefix(val); + }else if (key.equals("secure")){ + builder.setSecure(ObjectUtils.asBoolean(val, false)); + }else if (key.equals("cdn_subdomain")){ + builder.setCdnSubdomain(ObjectUtils.asBoolean(val, false)); + }else if (key.equals("shorten")){ + builder.setShorten(ObjectUtils.asBoolean(val, false)); + }else { +// Log.w("Cloudinary", "ignoring invalid parameter " + val); + } + + } + } + return builder.build(); + } + + + /** + * Build a new {@link Configuration} + */ + public static class Builder { + private String cloudName; + private String apiKey; + private String apiSecret; + private String secureDistribution; + private String cname; + private String uploadPrefix; + private boolean secure; + private boolean privateCdn; + private boolean cdnSubdomain; + private boolean shorten; + private String callback; + private String proxyHost; + private int proxyPort; + + /** + * Creates a {@link Configuration} with the arguments supplied to this builder + */ + public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort); } + + /** + * The unique name of your cloud at Cloudinary + * You can find your cloud name in the Account Details section in the dashboard of Cloudinary Management Console. + */ + public Builder setCloudName(String cloudName) { + this.cloudName = cloudName; + return this; + } + + /** + * API Key + * You can find API Key in the Account Details section in the dashboard of Cloudinary Management Console. + */ + public Builder setApiKey(String apiKey) { + this.apiKey = apiKey; + return this; + } + + /** + * API Secret + * You can find API Secret in the Account Details section in the dashboard of Cloudinary Management Console. + */ + public Builder setApiSecret(String apiSecret) { + this.apiSecret = apiSecret; + return this; + } + + /** + * The domain name of the CDN distribution to use for building HTTPS URLs. + * Relevant only for Advanced plan's users that have a private CDN distribution. + */ + public Builder setSecureDistribution(String secureDistribution) { + this.secureDistribution = secureDistribution; + return this; + } + + /** + * Custom domain name to use for building HTTP URLs. + * Relevant only for Advanced plan's users that have a private CDN distribution and a custom CNAME. + */ + public Builder setCname(String cname) { + this.cname = cname; + return this; + } + + /** + * Force HTTPS URLs of images even if embedded in non-secure HTTP pages. + */ + public Builder setSecure(boolean secure) { + this.secure = secure; + return this; + } + + /** + * Should be set to true for Advanced plan's users that have a private CDN distribution. + */ + public Builder setPrivateCdn(boolean privateCdn) { + this.privateCdn = privateCdn; + return this; + } + + /** + * Whether to automatically build URLs with multiple CDN sub-domains. + */ + public Builder setCdnSubdomain(boolean cdnSubdomain) { + this.cdnSubdomain = cdnSubdomain; + return this; + } + + public Builder setShorten(boolean shorten) { + this.shorten = shorten; + return this; + } + + public Builder setCallback(String callback) { + this.callback = callback; + return this; + } + + public Builder setUploadPrefix(String uploadPrefix) { + this.uploadPrefix = uploadPrefix; + return this; + } + + /** + * Initialize builder from existing {@link Configuration} + * @param other + * @return + */ + public Builder from(Configuration other) { + this.cloudName = other.cloudName; + this.apiKey = other.apiKey; + this.apiSecret = other.apiSecret; + this.secureDistribution = other.secureDistribution; + this.cname = other.cname; + this.uploadPrefix = other.uploadPrefix; + this.secure = other.secure; + this.privateCdn = other.privateCdn; + this.cdnSubdomain = other.cdnSubdomain; + this.shorten = other.shorten; + this.callback = other.callback; + + return this; + } + + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index ed9f3ed3..2ac4d373 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -285,7 +285,7 @@ public String uploadTagParams(Map options) { options.put("resource_type", "auto"); } - String callback = ObjectUtils.asString(options.get("callback"), this.cloudinary.getStringConfig("callback")); + String callback = ObjectUtils.asString(options.get("callback"), this.cloudinary.config.callback); if (callback == null) { throw new IllegalArgumentException("Must supply callback"); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 082bcc7a..1a6429d4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -4,8 +4,11 @@ import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.CRC32; import com.cloudinary.utils.Base64Coder; @@ -13,33 +16,64 @@ import com.cloudinary.utils.StringUtils; public class Url { - String cloudName; - boolean secure; - boolean privateCdn; - String secureDistribution; - boolean cdnSubdomain; + private final Configuration config; boolean shorten; - boolean signUrl; - String cname; + String publicId = null; String type = "upload"; String resourceType = "image"; String format = null; String version = null; - String source = null; - String apiSecret = null; Transformation transformation = null; + boolean signUrl; + String source = null; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; + public Url(Cloudinary cloudinary) { - this.cloudName = cloudinary.getStringConfig("cloud_name"); - this.secureDistribution = cloudinary.getStringConfig("secure_distribution"); - this.cname = cloudinary.getStringConfig("cname"); - this.secure = cloudinary.getBooleanConfig("secure", false); - this.privateCdn = cloudinary.getBooleanConfig("private_cdn", false); - this.cdnSubdomain = cloudinary.getBooleanConfig("cdn_subdomain", false); - this.shorten = cloudinary.getBooleanConfig("shorten", false); - this.signUrl = cloudinary.getBooleanConfig("sign_url", false); - this.apiSecret = cloudinary.getStringConfig("api_secret"); + this.config = new Configuration(cloudinary.config); + } + + private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); + + /** + * Parses a cloudinary identifier of the form: + * [/][/][v/][.][#] + */ + public Url fromIdentifier(String identifier) { + Matcher matcher = identifierPattern.matcher(identifier); + if (!matcher.matches()) { + throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); + } + + String resourceType = matcher.group(1); + if (resourceType != null) { + resourceType(resourceType); + } + + String type = matcher.group(2); + if (type != null) { + type(type); + } + + String version = matcher.group(3); + if (version != null) { + version(version); + } + + String publicId = matcher.group(4); + if (publicId != null) { + publicId(publicId); + } + + String format = matcher.group(5); + if (format != null) { + format(format); + } + + // Signature (group 6) is not used + + return this; } public Url type(String type) { @@ -47,7 +81,6 @@ public Url type(String type) { return this; } - @Deprecated public Url resourcType(String resourceType) { return resourceType(resourceType); } @@ -57,23 +90,28 @@ public Url resourceType(String resourceType) { return this; } + public Url publicId(Object publicId) { + this.publicId = ObjectUtils.asString(publicId); + return this; + } + public Url format(String format) { this.format = format; return this; } public Url cloudName(String cloudName) { - this.cloudName = cloudName; + this.config.cloudName = cloudName; return this; } public Url secureDistribution(String secureDistribution) { - this.secureDistribution = secureDistribution; + this.config.secureDistribution = secureDistribution; return this; } public Url cname(String cname) { - this.cname = cname; + this.config.cname = cname; return this; } @@ -88,39 +126,22 @@ public Url transformation(Transformation transformation) { } public Url secure(boolean secure) { - this.secure = secure; + this.config.secure = secure; return this; } public Url privateCdn(boolean privateCdn) { - this.privateCdn = privateCdn; + this.config.privateCdn = privateCdn; return this; } public Url cdnSubdomain(boolean cdnSubdomain) { - this.cdnSubdomain = cdnSubdomain; + this.config.cdnSubdomain = cdnSubdomain; return this; } public Url shorten(boolean shorten) { - this.shorten = shorten; - return this; - } - - public Url source(String source) { - this.source = source; - return this; - } - - public Url source(StoredFile source) { - if (source.getResourceType() != null) - this.resourceType = source.getResourceType(); - if (source.getType() != null) - this.type = source.getType(); - if (source.getVersion() != null) - this.version = source.getVersion().toString(); - this.format = source.getFormat(); - this.source = source.getPublicId(); + this.config.shorten = shorten; return this; } @@ -135,26 +156,29 @@ public Url signed(boolean signUrl) { return this; } - public String generate(String source) { - this.source = source; - return this.generate(); + public String generate() { + return generate(null); } - public String generate() { - if (type.equals("fetch") && StringUtils.isNotBlank(format)) { + public String generate(String source) { + if (type.equals("fetch") && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); this.format = null; } String transformationStr = transformation().generate(); - if (StringUtils.isBlank(this.cloudName)) { + if (StringUtils.isEmpty(this.config.cloudName)) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); } - if (source == null) - return null; + if (source == null) { + if (publicId == null) { + return null; + } + source = publicId; + } String original_source = source; - if (source.toLowerCase().matches("^https?:/.*")) { + if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { if ("upload".equals(type) || "asset".equals(type)) { return original_source; } @@ -169,33 +193,35 @@ public String generate() { source = source + "." + format; } String prefix; - boolean sharedDomain = !privateCdn; - if (secure) { - if (StringUtils.isBlank(secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(secureDistribution)) { - secureDistribution = privateCdn ? cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + boolean sharedDomain = !config.privateCdn; + if (config.secure) { + if (StringUtils.isEmpty(config.secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(config.secureDistribution)) { + config.secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; } - sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(secureDistribution); - prefix = "https://" + secureDistribution; + sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(config.secureDistribution); + prefix = "https://" + config.secureDistribution; } else { CRC32 crc32 = new CRC32(); crc32.update(source.getBytes()); - String subdomain = cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; - String host = cname != null ? cname : (privateCdn ? cloudName + "-" : "") + "res.cloudinary.com"; + String subdomain = config.cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; + String host = config.cname != null ? config.cname : (config.privateCdn ? config.cloudName + "-" : "") + "res.cloudinary.com"; prefix = "http://" + subdomain + host; } if (sharedDomain) - prefix = prefix + "/" + cloudName; + prefix = prefix + "/" + config.cloudName; - if (shorten && resourceType.equals("image") && type.equals("upload")) { + if (config.shorten && resourceType.equals("image") && type.equals("upload")) { resourceType = "iu"; type = ""; } - if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isBlank(version)) { + if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { version = "1"; } - if (version != null) + if (version == null) + version = ""; + else version = "v" + version; String rest = StringUtils.join(new String[] { transformationStr, version, source }, "/"); @@ -208,7 +234,7 @@ public String generate() { } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unexpected exception", e); } - byte[] digest = md.digest((rest + apiSecret).getBytes()); + byte[] digest = md.digest((rest + this.config.apiSecret).getBytes()); String signature = Base64Coder.encodeURLSafeString(digest); rest = "s--" + signature.substring(0, 8) + "--/" + rest; } @@ -216,40 +242,17 @@ public String generate() { return StringUtils.join(new String[] { prefix, resourceType, type, rest }, "/").replaceAll("([^:])\\/+", "$1/"); } - public String generateSpriteCss(String source) { - this.type = "sprite"; - if (!source.endsWith(".css")) - this.format = "css"; - return generate(source); - } - @SuppressWarnings("unchecked") public String imageTag(String source) { return imageTag(source, ObjectUtils.emptyMap()); } - public String imageTag(String source, Map attributes) { - this.source = source; - return imageTag(attributes); - } - - @SuppressWarnings("unchecked") - public String imageTag() { - return imageTag(ObjectUtils.emptyMap()); - } - - @SuppressWarnings("unchecked") - public String imageTag(StoredFile source) { - return imageTag(source, ObjectUtils.emptyMap()); - } - - public String imageTag(StoredFile source, Map attributes) { - source(source); - return imageTag(attributes); + public String imageTag(Map attributes) { + return imageTag("", attributes); } - public String imageTag(Map attributes) { - String url = generate(); + public String imageTag(String source, Map attributes) { + String url = generate(source); attributes = new TreeMap(attributes); // Make sure they // are ordered. if (transformation().getHtmlHeight() != null) @@ -282,4 +285,45 @@ public String imageTag(Map attributes) { builder.append("/>"); return builder.toString(); } + +// public String imageTag(String source, Map attributes) { +// String url = generate(source); +// attributes = new TreeMap(attributes); // Make sure they +// // are ordered. +// if (transformation().getHtmlHeight() != null) +// attributes.put("height", transformation().getHtmlHeight()); +// if (transformation().getHtmlWidth() != null) +// attributes.put("width", transformation().getHtmlWidth()); +// StringBuilder builder = new StringBuilder(); +// builder.append(" attr : attributes.entrySet()) { +// builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); +// } +// builder.append("/>"); +// return builder.toString(); +// } + + public String generateSpriteCss(String source) { + this.type = "sprite"; + if (!source.endsWith(".css")) + this.format = "css"; + return generate(source); + } + + public Url source(String source) { + this.source = source; + return this; + } + + public Url source(StoredFile source) { + if (source.getResourceType() != null) + this.resourceType = source.getResourceType(); + if (source.getType() != null) + this.type = source.getType(); + if (source.getVersion() != null) + this.version = source.getVersion().toString(); + this.format = source.getFormat(); + this.source = source.getPublicId(); + return this; + } } 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 e641dedf..b705d684 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -135,16 +135,26 @@ private static Object fromJson(Object json) throws JSONException { return toList((JSONArray) json); } else { return json; - } + } } - + @SuppressWarnings({ "rawtypes", "unchecked" }) public static List toList(JSONArray array) throws JSONException { - List list = new ArrayList(); - for (int i = 0; i < array.length(); i++) { - list.add(fromJson(array.get(i))); - } - return list; - } + List list = new ArrayList(); + for (int i = 0; i < array.length(); i++) { + list.add(fromJson(array.get(i))); + } + return list; + } + + public static Integer asInteger(Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Integer) { + return (Integer) value; + } else { + return Integer.parseInt(value.toString()); + } + } } 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 c32843c0..0cdbda43 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -34,14 +34,15 @@ public class ApiStrategy extends com.cloudinary.strategies.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"), this.api.cloudinary.getStringConfig("upload_prefix", "https://api.cloudinary.com")); - String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.getStringConfig("cloud_name")); + + 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.getStringConfig("api_key")); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.getStringConfig("api_secret")); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 85f2afc5..c9eb4d0f 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -6,9 +6,11 @@ import java.util.Collection; import java.util.Map; +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.params.ConnRoutePNames; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.ByteArrayBody; @@ -30,10 +32,10 @@ public class UploaderStrategy extends AbstractUploaderStrategy { @Override public Map callApi(String action, Map params, Map options, Object file) throws IOException { // initialize options if passed as null - if (options == null){ + if (options == null) { options = ObjectUtils.emptyMap(); } - + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { @@ -46,6 +48,12 @@ public Map callApi(String action, Map params, Map options, Objec HttpClient client = new DefaultHttpClient(uploader.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", Cloudinary.USER_AGENT); diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 9fc5a5e6..1f91c95d 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -40,7 +40,7 @@ public class ApiTest { @BeforeClass public static void setUpClass() throws IOException { Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.getStringConfig("api_secret") == null) { + if (cloudinary.config.apiSecret == null) { System.err.println("Please setup environment for Upload test to run"); return; } @@ -78,7 +78,7 @@ public static void setUpClass() throws IOException { @Before public void setUp() { this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.getStringConfig("api_secret")); + assumeNotNull(cloudinary.config.apiSecret); this.api = cloudinary.api(); } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index 0bf2e90c..c09edf38 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -57,7 +57,7 @@ public void testSecureDistributionOverwrite() { @Test public void testSecureDistibution() { // should take secure distribution from config if secure=TRUE - cloudinary.setConfig("secure_distribution", "config.secure.distribution.com"); + cloudinary.config.secureDistribution = "config.secure.distribution.com"; String result = cloudinary.url().secure(true).generate("test"); assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); } @@ -66,8 +66,8 @@ public void testSecureDistibution() { public void testSecureAkamai() { // should default to akamai if secure is given with private_cdn and no // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn", true); + cloudinary.config.secure = true; + cloudinary.config.privateCdn =true; String result = cloudinary.url().generate("test"); assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); } @@ -76,9 +76,9 @@ public void testSecureAkamai() { public void testSecureNonAkamai() { // should not add cloud_name if private_cdn and secure non akamai // secure_distribution - cloudinary.setConfig("secure", true); - cloudinary.setConfig("private_cdn", true); - cloudinary.setConfig("secure_distribution", "something.cloudfront.net"); + cloudinary.config.secure = true; + cloudinary.config.privateCdn =true; + cloudinary.config.secureDistribution = "something.cloudfront.net"; String result = cloudinary.url().generate("test"); assertEquals("https://something.cloudfront.net/image/upload/test", result); } @@ -86,7 +86,7 @@ public void testSecureNonAkamai() { @Test public void testHttpPrivateCdn() { // should not add cloud_name if private_cdn and not secure - cloudinary.setConfig("private_cdn", true); + cloudinary.config.privateCdn =true; String result = cloudinary.url().generate("test"); assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 73618ccc..998d5ff7 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -29,7 +29,7 @@ public class UploaderTest { @BeforeClass public static void setUpClass() { Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.getStringConfig("api_secret") == null) { + if (cloudinary.config.apiSecret == null) { System.err.println("Please setup environment for Upload test to run"); } } @@ -37,7 +37,7 @@ public static void setUpClass() { @Before public void setUp() { this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.getStringConfig("api_secret")); + assumeNotNull(cloudinary.config.apiSecret); } @Test @@ -50,7 +50,7 @@ public void testUpload() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } @@ -62,7 +62,7 @@ public void testUploadUrl() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } @@ -74,7 +74,7 @@ public void testUploadDataUri() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.getStringConfig("api_secret")); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } @@ -109,7 +109,7 @@ public void testExplicit() throws IOException { Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); - String cloudName = cloudinary.getStringConfig("cloud_name"); + String cloudName = cloudinary.config.cloudName; assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java index e181aa30..ae4b319a 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java @@ -6,6 +6,7 @@ import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.SimpleTagSupport; + import java.io.IOException; public class CloudinaryJsConfigTag extends SimpleTagSupport { @@ -18,16 +19,21 @@ public void doTag() throws JspException, IOException { out.println(""); } + + private void print(JspWriter out, String key,Object value) throws IOException { + out.println(key + ": \""+value + "\","); + + } } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java index f4defc5f..51cc526e 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java @@ -427,7 +427,7 @@ protected String uploadTag(Uploader uploader, Map options, Map htmlOptions) { private void buildCallbackUrl(Map options) { String callback = (String) options.get("callback"); - if (callback == null || callback.isEmpty()) callback = Singleton.getCloudinary().getStringConfig("callback"); + if (callback == null || callback.isEmpty()) callback = Singleton.getCloudinary().config.callback; if (callback == null || callback.isEmpty()) callback = "/cloudinary_cors.html"; if (!callback.matches("^https?://")) { PageContext context = (PageContext) getJspContext(); From 6f19e3f5dd13c7351d7dae1b45a072ef6fec75bb Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 5 Nov 2014 12:13:34 +0200 Subject: [PATCH 011/592] Remove httpclient dependencies from cloudinary-core. Use main version in both http42 and android versions. Remove getRawResponse from ApiResponse --- .../{ => src/main}/AndroidManifest.xml | 0 .../{ => src/main}/assets/docx.docx | Bin .../{ => src/main}/assets/images/favicon.ico | Bin .../{ => src/main}/assets/images/logo.png | Bin .../{ => src/main}/res/drawable-hdpi/icon.png | Bin .../{ => src/main}/res/drawable-ldpi/icon.png | Bin .../{ => src/main}/res/drawable-mdpi/icon.png | Bin .../{ => src/main}/res/layout/main.xml | 0 .../{ => src/main}/res/values/strings.xml | 0 .../cloudinary/android/MultipartUtility.java | 5 +- .../src/main/java/com/cloudinary/Api.java | 9 --- .../main/java/com/cloudinary/Cloudinary.java | 16 +----- .../java/com/cloudinary/Configuration.java | 2 + .../main/java/com/cloudinary/Uploader.java | 7 --- .../java/com/cloudinary/api/ApiResponse.java | 3 - .../cloudinary/strategies/StrategyLoader.java | 52 ------------------ cloudinary-http42/pom.xml | 10 ++++ .../com/cloudinary/http42/ApiStrategy.java | 7 ++- .../cloudinary/http42/UploaderStrategy.java | 4 +- .../com/cloudinary/http42}/api/Response.java | 4 +- pom.xml | 37 ------------- .../controllers/PhotoController.java | 7 ++- 22 files changed, 33 insertions(+), 130 deletions(-) rename cloudinary-android-test/{ => src/main}/AndroidManifest.xml (100%) rename cloudinary-android-test/{ => src/main}/assets/docx.docx (100%) rename cloudinary-android-test/{ => src/main}/assets/images/favicon.ico (100%) rename cloudinary-android-test/{ => src/main}/assets/images/logo.png (100%) rename cloudinary-android-test/{ => src/main}/res/drawable-hdpi/icon.png (100%) rename cloudinary-android-test/{ => src/main}/res/drawable-ldpi/icon.png (100%) rename cloudinary-android-test/{ => src/main}/res/drawable-mdpi/icon.png (100%) rename cloudinary-android-test/{ => src/main}/res/layout/main.xml (100%) rename cloudinary-android-test/{ => src/main}/res/values/strings.xml (100%) rename {cloudinary-core/src/main/java/com/cloudinary => cloudinary-http42/src/main/java/com/cloudinary/http42}/api/Response.java (94%) diff --git a/cloudinary-android-test/AndroidManifest.xml b/cloudinary-android-test/src/main/AndroidManifest.xml similarity index 100% rename from cloudinary-android-test/AndroidManifest.xml rename to cloudinary-android-test/src/main/AndroidManifest.xml diff --git a/cloudinary-android-test/assets/docx.docx b/cloudinary-android-test/src/main/assets/docx.docx similarity index 100% rename from cloudinary-android-test/assets/docx.docx rename to cloudinary-android-test/src/main/assets/docx.docx diff --git a/cloudinary-android-test/assets/images/favicon.ico b/cloudinary-android-test/src/main/assets/images/favicon.ico similarity index 100% rename from cloudinary-android-test/assets/images/favicon.ico rename to cloudinary-android-test/src/main/assets/images/favicon.ico diff --git a/cloudinary-android-test/assets/images/logo.png b/cloudinary-android-test/src/main/assets/images/logo.png similarity index 100% rename from cloudinary-android-test/assets/images/logo.png rename to cloudinary-android-test/src/main/assets/images/logo.png diff --git a/cloudinary-android-test/res/drawable-hdpi/icon.png b/cloudinary-android-test/src/main/res/drawable-hdpi/icon.png similarity index 100% rename from cloudinary-android-test/res/drawable-hdpi/icon.png rename to cloudinary-android-test/src/main/res/drawable-hdpi/icon.png diff --git a/cloudinary-android-test/res/drawable-ldpi/icon.png b/cloudinary-android-test/src/main/res/drawable-ldpi/icon.png similarity index 100% rename from cloudinary-android-test/res/drawable-ldpi/icon.png rename to cloudinary-android-test/src/main/res/drawable-ldpi/icon.png diff --git a/cloudinary-android-test/res/drawable-mdpi/icon.png b/cloudinary-android-test/src/main/res/drawable-mdpi/icon.png similarity index 100% rename from cloudinary-android-test/res/drawable-mdpi/icon.png rename to cloudinary-android-test/src/main/res/drawable-mdpi/icon.png diff --git a/cloudinary-android-test/res/layout/main.xml b/cloudinary-android-test/src/main/res/layout/main.xml similarity index 100% rename from cloudinary-android-test/res/layout/main.xml rename to cloudinary-android-test/src/main/res/layout/main.xml diff --git a/cloudinary-android-test/res/values/strings.xml b/cloudinary-android-test/src/main/res/values/strings.xml similarity index 100% rename from cloudinary-android-test/res/values/strings.xml rename to cloudinary-android-test/src/main/res/values/strings.xml diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 51cc8f63..51614414 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -11,6 +11,8 @@ import java.net.URL; import java.net.URLConnection; +import com.cloudinary.Cloudinary; + /** * This utility class provides an abstraction layer for sending multipart HTTP * POST requests to a web server. @@ -26,8 +28,7 @@ public class MultipartUtility { private OutputStream outputStream; private PrintWriter writer; - public final static String VERSION = "1.0.2"; - public final static String USER_AGENT = "cld-android-" + VERSION; + public final static String USER_AGENT = "cld-android-" + Cloudinary.VERSION; /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index c06043c9..892456dd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -6,8 +6,6 @@ import java.util.List; import java.util.Map; -import org.apache.http.conn.ClientConnectionManager; - import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; import com.cloudinary.api.exceptions.AlreadyExists; @@ -35,7 +33,6 @@ public enum HttpMethod { GET, POST, PUT, DELETE } } public final Cloudinary cloudinary; - public ClientConnectionManager connectionManager = null; private AbstractApiStrategy strategy; protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { @@ -230,10 +227,4 @@ public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); } - public Api withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; - } - - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index cfbe723f..7d8bdd59 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -14,9 +14,6 @@ import java.util.Map; import java.util.TreeMap; -//import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.ClientConnectionManager; - import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.AbstractUrlBuilderStrategy; @@ -36,22 +33,21 @@ 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.0.14"; + public final static String VERSION = "1.0.15"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; private AbstractUrlBuilderStrategy urlBuilderStrategy; - protected ClientConnectionManager connectionManager = null; public Uploader uploader(){ - return new Uploader(this,uploaderStrategy).withConnectionManager(connectionManager); + return new Uploader(this,uploaderStrategy); }; public Api api(){ - return new Api(this,apiStrategy).withConnectionManager(connectionManager); + return new Api(this,apiStrategy); }; public static void registerUploaderStrategy(String className){ @@ -234,10 +230,4 @@ protected Map parseConfigUrl(String cloudinaryUrl) { return params; } - - public Cloudinary withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; - } - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 1670b242..db9dd325 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -4,6 +4,7 @@ import java.net.URI; import java.net.URLDecoder; import java.util.Map; +import java.util.HashMap; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -32,6 +33,7 @@ public class Configuration { public String callback; public String proxyHost; public int proxyPort; + public Map properties = new HashMap(); public Configuration(){ } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 2ac4d373..128ccc4a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; -import org.apache.http.conn.ClientConnectionManager; import org.json.JSONObject; import com.cloudinary.strategies.AbstractUploaderStrategy; @@ -23,7 +22,6 @@ public Map callApi(String action, Map params, Map options, Objec return strategy.callApi(action,params,options,file); } - public ClientConnectionManager connectionManager = null; private Cloudinary cloudinary; private AbstractUploaderStrategy strategy; @@ -272,11 +270,6 @@ public void signRequestParams(Map params, Map options) { cloudinary.signRequest(params, options); } - public Uploader withConnectionManager(ClientConnectionManager connectionManager) { - this.connectionManager = connectionManager; - return this; - } - public String uploadTagParams(Map options) { if (options == null) options = new HashMap(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java index 8353ad1e..9edb8a82 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java @@ -2,11 +2,8 @@ import java.util.Map; -import org.apache.http.HttpResponse; - @SuppressWarnings("rawtypes") public interface ApiResponse extends Map { - HttpResponse getRawHttpResponse(); Map rateLimits() throws java.text.ParseException; RateLimit apiRateLimit() throws java.text.ParseException; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java index ab69008c..ff2ad907 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java @@ -41,57 +41,5 @@ public static T find(List strategies) { public boolean exists(List strategies) { return find(strategies) != null; } - - @Deprecated - public static ArrayList getClassNamesFromPackage(String packageName) throws IOException, URISyntaxException { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - URL packageURL; - ArrayList names = new ArrayList(); - - - packageName = packageName.replace(".", "/"); - packageURL = classLoader.getResource(packageName); - - if (packageURL.getProtocol().equals("jar")) { - String jarFileName; - JarFile jf; - Enumeration jarEntries; - String entryName; - - // build jar file name, then loop through zipped entries - jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8"); - jarFileName = jarFileName.substring(5, jarFileName.indexOf("!")); - System.out.println(">" + jarFileName); - jf = new JarFile(jarFileName); - jarEntries = jf.entries(); - while (jarEntries.hasMoreElements()) { - entryName = jarEntries.nextElement().getName(); - if (entryName.startsWith(packageName) && entryName.length() > packageName.length() + 5) { - entryName = entryName.substring(packageName.length(), entryName.lastIndexOf('.')); - names.add(entryName); - } - } - - // loop through files in classpath - } else { - URI uri = new URI(packageURL.toString()); - File folder = new File(uri.getPath()); - // won't work with path which contains blank (%20) - // File folder = new File(packageURL.getFile()); - File[] contenuti = folder.listFiles(); - System.out.println("files:"); - System.out.println(StringUtils.join(contenuti, "\n")); - System.out.println("-- -- --"); - String entryName; - for (File actual : contenuti) { - entryName = actual.getName(); - System.out.println(entryName); - entryName = entryName.substring(0, entryName.lastIndexOf('.')); - names.add(entryName); - } - } - return names; - } - } diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index ab7dffa6..9e5244ba 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -22,6 +22,16 @@ commons-lang3 3.1 + + org.apache.httpcomponents + httpclient + 4.2.1 + + + org.apache.httpcomponents + httpcore + 4.2.1 + org.apache.httpcomponents httpmime 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 0cdbda43..cd0b1a96 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -13,6 +13,7 @@ import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ClientConnectionManager; import org.apache.http.impl.client.DefaultHttpClient; import org.json.simple.JSONValue; import org.json.simple.parser.ParseException; @@ -21,8 +22,8 @@ import com.cloudinary.Api.HttpMethod; import com.cloudinary.Cloudinary; import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.Response; import com.cloudinary.api.exceptions.GeneralError; +import com.cloudinary.http42.api.Response; import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -60,7 +61,9 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options, Objec String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); - HttpClient client = new DefaultHttpClient(uploader.connectionManager); + 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) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/Response.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java similarity index 94% rename from cloudinary-core/src/main/java/com/cloudinary/api/Response.java rename to cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java index a0351ee4..c7ccc41b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/Response.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java @@ -1,4 +1,4 @@ -package com.cloudinary.api; +package com.cloudinary.http42.api; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -10,6 +10,8 @@ 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") diff --git a/pom.xml b/pom.xml index 75d4666a..f86003b5 100644 --- a/pom.xml +++ b/pom.xml @@ -97,43 +97,6 @@ - - - - - - - - - - - - - - - - - - - - - - - org.apache.httpcomponents - httpclient - 4.2.1 - - - org.apache.httpcomponents - httpcore - 4.2.1 - - - - - - - junit junit 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 fd71cb6f..d35a2a1d 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 @@ -27,7 +27,7 @@ @Controller @RequestMapping("/") public class PhotoController { - private final static GAEConnectionManager connectoinManager = new GAEConnectionManager(); + private final static GAEConnectionManager connectionManager = new GAEConnectionManager(); @RequestMapping(value = "/", method = RequestMethod.GET) public String listPhotos(ModelMap model) { DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); @@ -47,8 +47,9 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult validator.validate(photoUpload, result); Map uploadResult = null; - if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { - uploadResult = Singleton.getCloudinary().uploader().withConnectionManager(connectoinManager).upload(photoUpload.getFile().getBytes(), + if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { + Singleton.getCloudinary().config.properties.put("connectionManager", connectionManager); + uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), Cloudinary.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); From 1c753acc05472c8571c916557bd0235a9db191a6 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 5 Nov 2014 12:21:14 +0200 Subject: [PATCH 012/592] Git ignore cloudinary-android-test/src/main/AndroidManifest.xml. Fix tag lib dependency --- .gitignore | 4 +++- .../main/{AndroidManifest.xml => AndroidManifest.xml.sample} | 0 cloudinary-taglib/pom.xml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) rename cloudinary-android-test/src/main/{AndroidManifest.xml => AndroidManifest.xml.sample} (100%) diff --git a/.gitignore b/.gitignore index ea7ec3d7..c930c5ba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ test-output/ *.iml -appengine-web.xml \ No newline at end of file +appengine-web.xml + +cloudinary-android-test/src/main/AndroidManifest.xml diff --git a/cloudinary-android-test/src/main/AndroidManifest.xml b/cloudinary-android-test/src/main/AndroidManifest.xml.sample similarity index 100% rename from cloudinary-android-test/src/main/AndroidManifest.xml rename to cloudinary-android-test/src/main/AndroidManifest.xml.sample diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index b3d8cca8..eb5f52db 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -15,7 +15,7 @@ com.cloudinary - cloudinary-http42 + cloudinary ${project.version} From 501e237813e1271d7ed155fd8e2c2dd21ad7a283 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Sat, 8 Nov 2014 23:21:24 +0200 Subject: [PATCH 013/592] add support for signed urls in tag helpers (image and url) --- .../java/com/cloudinary/taglib/CloudinaryImageTag.java | 10 ++++++++++ .../main/java/com/cloudinary/taglib/CloudinaryUrl.java | 10 ++++++++++ .../src/main/resources/META-INF/cloudinary.tld | 10 ++++++++++ samples/photo_album/pom.xml | 2 +- .../photo_album/src/main/webapp/WEB-INF/pages/pre.jsp | 2 +- 5 files changed, 32 insertions(+), 2 deletions(-) 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 ec36673d..01e1320e 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java @@ -42,6 +42,7 @@ public class CloudinaryImageTag extends SimpleTagSupport implements DynamicAttri private Boolean secure = null; private Boolean cdnSubdomain = null; + private Boolean signed = null; private String namedTransformation = null; @@ -87,6 +88,7 @@ public void doTag() throws JspException, IOException { url.secure(true); } if (cdnSubdomain != null) url.cdnSubdomain(cdnSubdomain.booleanValue()); + if (signed != null) url.signed(signed.booleanValue()); out.println(url.imageTag(attributes)); } @@ -181,6 +183,14 @@ public void setCdnSubdomain(Boolean cdnSubdomain) { this.cdnSubdomain = cdnSubdomain; } + public Boolean getSigned() { + return signed; + } + + public void setSigned(Boolean signed) { + this.signed = signed; + } + public String getNamed() { return namedTransformation; } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java index 93eb12ce..19b21982 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java @@ -31,6 +31,7 @@ public class CloudinaryUrl extends SimpleTagSupport implements DynamicAttributes private Boolean secure = null; private Boolean cdnSubdomain = null; + private Boolean signed = null; private String namedTransformation = null; @@ -64,6 +65,7 @@ public void doTag() throws JspException, IOException { url.secure(true); } if (cdnSubdomain != null) url.cdnSubdomain(cdnSubdomain.booleanValue()); + if (signed != null) url.signed(signed.booleanValue()); out.println(url.generate()); } @@ -133,6 +135,14 @@ public void setCdnSubdomain(Boolean cdnSubdomain) { this.cdnSubdomain = cdnSubdomain; } + public Boolean getSigned() { + return signed; + } + + public void setSigned(Boolean signed) { + this.signed = signed; + } + public String getNamed() { return namedTransformation; } diff --git a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld index 457f8133..4cecbdce 100644 --- a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld +++ b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld @@ -340,6 +340,11 @@ cdnSubdomain false true + + + signed + false + true named @@ -392,6 +397,11 @@ false true + + signed + false + true + named false diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 6e523c7c..62cb1ad4 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,7 +23,7 @@ com.cloudinary cloudinary-taglib - 1.0.14 + 1.0.15-SNAPSHOT org.springframework diff --git a/samples/photo_album/src/main/webapp/WEB-INF/pages/pre.jsp b/samples/photo_album/src/main/webapp/WEB-INF/pages/pre.jsp index 8b78072f..c219fce2 100644 --- a/samples/photo_album/src/main/webapp/WEB-INF/pages/pre.jsp +++ b/samples/photo_album/src/main/webapp/WEB-INF/pages/pre.jsp @@ -12,7 +12,7 @@

\ No newline at end of file From a6d3a4a59640944235cb50f56ca5986b9ebdffa1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 11 Nov 2014 15:29:09 +0200 Subject: [PATCH 014/592] updated documentation , fixed sample projects --- README.md | 15 +-- cloudinary-android-test/pom.xml | 1 + .../com/cloudinary/test/UploaderTest.java | 3 - cloudinary-android/README.md | 113 ++++++++++++++++++ cloudinary-android/pom.xml | 2 +- pom.xml | 2 +- .../controllers/PhotoController.java | 9 +- .../controllers/PhotoController.java | 4 +- 8 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 cloudinary-android/README.md diff --git a/README.md b/README.md index 7e2227ed..25d26966 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,23 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** This Java library is intended mainly for Web applications. For **Android** integration, there is a dedicated library with a similar interface: https://github.com/cloudinary/cloudinary_android +**Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/cloudinary-android ## Getting started guide ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](http://cloudinary.com/documentation/java_integration#getting_started_guide)**. ## Setup ###################################################################### -The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: +The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml : com.cloudinary - cloudinary + cloudinary-http42 1.0.14 -Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/tarball/master) -and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/pom.xml) for library dependencies. +Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/tarball/master) +and see [pom.xml](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/blob/master/pom.xml) for library dependencies. ## Try it right away @@ -156,8 +156,8 @@ Returns an html input field for direct image upload, to be used in conjunction w Usage: - Map options = Cloudinary.asMap("resource_type", "auto"); - Map htmlOptions = Cloudinary.asMap("alt", "sample"); + Map options = ObjectUtils.asMap("resource_type", "auto"); + Map htmlOptions = ObjectUtils.asMap("alt", "sample"); String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. @@ -182,3 +182,4 @@ Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary) ## License ####################################################################### Released under the MIT license. + diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index ca2229dd..f2bc33c6 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -51,6 +51,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin + 4.0.0-rc.2 true diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index e6ba8dce..38f63f73 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -66,7 +66,6 @@ public void testUnsignedUpload() throws Exception { to_sign.put("public_id", result.getString("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); Log.d("TestRunner",cloudinary.config.apiSecret); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } @@ -295,10 +294,8 @@ public void testAutoTaggingRequest() { try { cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("auto_tagging", 0.5f)); } catch (Exception e) { - Log.i("DANIEL",e.getMessage()); for (int i = 0; i < e.getStackTrace().length; i++) { StackTraceElement x = e.getStackTrace()[i]; - Log.i("DANIEL",x.getClassName()+"."+x.getMethodName()+" "+x.getLineNumber()); } diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md new file mode 100644 index 00000000..1d18c3a9 --- /dev/null +++ b/cloudinary-android/README.md @@ -0,0 +1,113 @@ +Cloudinary +========== + +Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. + +Easily 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. + +Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. + +Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. + +For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. + +## Manual Setup ###################################################################### +Download cloudinary-core-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-core-1.0.15.jar) and cloudinary-android-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-android-1.0.15.jar) +and put them in your libs folder. + +## Maven Integration ###################################################################### +The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: + + + com.cloudinary + cloudinary-android + 1.0.15 + + + +## Try it right away + +Sign up for a [free account](https://cloudinary.com/users/register/free) so you can try out image transformations and seamless image delivery through CDN. + +*Note: Replace `demo` in all the following examples with your Cloudinary's `cloud name`.* + +Accessing an uploaded image with the `sample` public ID through a CDN: + + http://res.cloudinary.com/demo/image/upload/sample.jpg + +![Sample](https://res.cloudinary.com/demo/image/upload/w_0.4/sample.jpg "Sample") + +Generating a 150x100 version of the `sample` image and downloading it through a CDN: + + http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg + +![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") + +Converting to a 150x100 PNG with rounded corners of 20 pixels: + + http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png + +![Sample 150x150 Rounded PNG](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png "Sample 150x150 Rounded PNG") + +For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). + +Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: + + http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg + +![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") + +For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. + + +## Usage + +### Configuration + +Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. +Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. +See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. + +Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, +by when initializing the Cloudinary object, or by using the CLOUDINARY_URL meta-data property. + +The entry point of the library is the Cloudinary object. + +Here's an example of setting the configuration parameters programatically: + + Map config = new HashMap(); + config.put("cloud_name", "n07t21i7"); + config.put("api_key", "123456789012345"); + config.put("api_secret", "abcdeghijklmnopqrstuvwxyz12"); + Cloudinary cloudinary = new Cloudinary(config); + +Another example of setting the configuration parameters by providing the CLOUDINARY_URL value to the constructor: + + Cloudinary cloudinary = new Cloudinary("cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7"); + +Giving the context will allow Cloudinary to configure from the application's meta-data. + + Cloudinary cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getContext())); + +Then add a meta-data property to your application section in the AndroidManifest.xml + + + ... + + ... + + + + +### Embedding and transforming images + +Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: + +The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: + + cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") + +... + diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 33310aba..f30902d9 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -42,7 +42,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin - 3.9.0-rc.1 + 4.0.0-rc.2 19 diff --git a/pom.xml b/pom.xml index f86003b5..c6f695cd 100644 --- a/pom.xml +++ b/pom.xml @@ -16,9 +16,9 @@ cloudinary-core cloudinary-android - cloudinary-android-test cloudinary-taglib cloudinary-http42 + cloudinary-android-test diff --git a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java index 0da6ad84..16fefc3d 100644 --- a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java +++ b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java @@ -6,6 +6,7 @@ import cloudinary.repositories.PhotoRepository; import com.cloudinary.Cloudinary; +import com.cloudinary.utils.ObjectUtils; import com.cloudinary.Singleton; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +39,7 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult Map uploadResult = null; if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), - Cloudinary.asMap("resource_type", "auto")); + ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); photoUpload.setVersion((Long) uploadResult.get("version")); photoUpload.setSignature((String) uploadResult.get("signature")); @@ -79,10 +80,10 @@ public String directUnsignedUploadPhotoForm(ModelMap model) throws Exception { model.addAttribute("photoUpload", new PhotoUpload()); model.addAttribute("unsigned", true); Cloudinary cld = Singleton.getCloudinary(); - String preset = "sample_" + cld.apiSignRequest(Cloudinary.asMap("api_key", cld.getStringConfig("api_key")), cld.getStringConfig("api_secret")).substring(0, 10); + String preset = "sample_" + cld.apiSignRequest(ObjectUtils.asMap("api_key", cld.config.apiKey), cld.config.apiSecret).substring(0, 10); model.addAttribute("preset", preset); try { - Singleton.getCloudinary().api().createUploadPreset(Cloudinary.asMap( + Singleton.getCloudinary().api().createUploadPreset(ObjectUtils.asMap( "name", preset, "unsigned", true, "folder", "preset_folder")); @@ -90,4 +91,4 @@ public String directUnsignedUploadPhotoForm(ModelMap model) throws Exception { } return "direct_upload_form"; } -} \ No newline at end of file +} 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 d35a2a1d..4dbfa915 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 @@ -3,6 +3,7 @@ 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; import org.springframework.ui.ModelMap; @@ -27,6 +28,7 @@ @Controller @RequestMapping("/") public class PhotoController { + private final static GAEConnectionManager connectionManager = new GAEConnectionManager(); @RequestMapping(value = "/", method = RequestMethod.GET) public String listPhotos(ModelMap model) { @@ -50,7 +52,7 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { Singleton.getCloudinary().config.properties.put("connectionManager", connectionManager); uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), - Cloudinary.asMap("resource_type", "auto")); + ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); photoUpload.setVersion((Long) uploadResult.get("version")); From 8c0ba232a51338e453074ce14a92d3148cb36150 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Mon, 17 Nov 2014 10:40:18 +0200 Subject: [PATCH 015/592] added deprecated asMap method to Cloudinary (support old api) --- .../src/main/java/com/cloudinary/Cloudinary.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 7d8bdd59..3d5c5e14 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -229,5 +229,9 @@ protected Map parseConfigUrl(String cloudinaryUrl) { } return params; } - + + @Deprecated + public static Map asMap(Object... values) { + return ObjectUtils.asMap(values); + } } From 52290ce1fcf7c6fe27ad5f62e44f95afa53f0673 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Mon, 17 Nov 2014 11:03:55 +0200 Subject: [PATCH 016/592] promoted minor version (1.0.x -> 1.1.x) & fixed documentation external links --- cloudinary-android-test/pom.xml | 8 ++--- cloudinary-android/README.md | 33 +++++++++---------- cloudinary-android/pom.xml | 4 +-- cloudinary-core/pom.xml | 2 +- .../main/java/com/cloudinary/Cloudinary.java | 26 +++++++-------- cloudinary-http42/pom.xml | 3 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 10 +++--- samples/photo_album/pom.xml | 2 +- samples/photo_album_gae/pom.xml | 6 ++-- 10 files changed, 46 insertions(+), 50 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f2bc33c6..e1ce605b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT com.cloudinary cloudinary-android-test - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT apk Cloudinary Android Test Project @@ -31,13 +31,13 @@ com.cloudinary cloudinary jar - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT com.cloudinary cloudinary-android jar - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT junit diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 1d18c3a9..67d33bd4 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -1,20 +1,20 @@ Cloudinary ========== -Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. +Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. -Easily 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. +Easily 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. Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. -Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. +Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-core-1.0.15.jar) and cloudinary-android-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-android-1.0.15.jar) +Download cloudinary-core-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-core-1.1.0.jar) and cloudinary-android-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-android-1.1.0.jar) and put them in your libs folder. ## Maven Integration ###################################################################### @@ -23,7 +23,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.0.15 + 1.1.0 @@ -45,7 +45,7 @@ Generating a 150x100 version of the `sample` image and downloading it through a ![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") -Converting to a 150x100 PNG with rounded corners of 20 pixels: +Converting to a 150x100 PNG with rounded corners of 20 pixels: http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png @@ -54,26 +54,26 @@ Converting to a 150x100 PNG with rounded corners of 20 pixels: For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: - + http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg - + ![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") -For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. +For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. ## Usage ### Configuration -Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. -Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. +Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. +Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. -Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, +Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, by when initializing the Cloudinary object, or by using the CLOUDINARY_URL meta-data property. -The entry point of the library is the Cloudinary object. +The entry point of the library is the Cloudinary object. Here's an example of setting the configuration parameters programatically: @@ -100,7 +100,7 @@ Then add a meta-data property to your application section in the AndroidManifest - + ### Embedding and transforming images Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: @@ -110,4 +110,3 @@ The following example generates the url for accessing an uploaded `sample` image cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") ... - diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index f30902d9..5bfcaa6f 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-android @@ -63,5 +63,3 @@ - - diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index a31d9454..8c4e6fcc 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3d5c5e14..e96b4458 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -23,17 +23,17 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) public class Cloudinary { - + private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList("com.cloudinary.android.UploaderStrategy","com.cloudinary.http42.UploaderStrategy","com.cloudinary.http43.UploaderStrategy")); private static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy" )); private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UrlBuilderStrategy", "com.cloudinary.http42.UrlBuilderStrategy", "com.cloudinary.http43.UrlBuilderStrategy" )); - + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; 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.0.15"; + public final static String VERSION = "1.1.0"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; @@ -43,7 +43,7 @@ public class Cloudinary { public Uploader uploader(){ return new Uploader(this,uploaderStrategy); - + }; public Api api(){ @@ -54,33 +54,33 @@ public static void registerUploaderStrategy(String className){ if (!UPLOAD_STRATEGIES.contains(className)){ UPLOAD_STRATEGIES.add(className); } - + } - + public static void registerAPIStrategy(String className){ if (!API_STRATEGIES.contains(className)){ API_STRATEGIES.add(className); } } - + public static void registerUrlBuilderStrategy(String className){ if (!URLBUILDER_STRATEGIES.contains(className)){ URLBUILDER_STRATEGIES.add(className); } } - + private void loadStrategies() { uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); - + if (uploaderStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); } - + apiStrategy= StrategyLoader.find(API_STRATEGIES); if (apiStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); } - + urlBuilderStrategy= StrategyLoader.find(URLBUILDER_STRATEGIES); if (urlBuilderStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(URLBUILDER_STRATEGIES, ",") + "]"); @@ -179,7 +179,7 @@ public String privateDownload(String publicId, String format, Map param : params.entrySet()) { builder.addParam(param.getKey(), param.getValue().toString()); } @@ -229,7 +229,7 @@ protected Map parseConfigUrl(String cloudinaryUrl) { } return params; } - + @Deprecated public static Map asMap(Object... values) { return ObjectUtils.asMap(values); diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 9e5244ba..654e976e 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-http42 @@ -45,4 +45,3 @@ - diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index eb5f52db..ac76a27a 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index c6f695cd..2c4de779 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -22,10 +22,10 @@ - Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. - Easily 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. + Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. + Easily 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. http://github.com/cloudinary/cloudinary_java diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 6e523c7c..93283b53 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,7 +23,7 @@ com.cloudinary cloudinary-taglib - 1.0.14 + 1.1.0 org.springframework diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index bfdbe2ef..4dbfe92a 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -38,7 +38,7 @@ com.cloudinary cloudinary-taglib - 1.0.13 + 1.1.0 org.springframework @@ -121,7 +121,7 @@ 1.9.0 test - + com.google.appengine appengine-testing @@ -140,7 +140,7 @@ hibernate-validator-annotation-processor 4.1.0.Final - + gmultipart gmultipart From 87250ccafb64c9a2fc6e3ab1fe67af2764e9d6f3 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 11 Nov 2014 15:29:09 +0200 Subject: [PATCH 017/592] updated documentation , fixed sample projects --- README.md | 15 +-- cloudinary-android-test/pom.xml | 1 + .../com/cloudinary/test/UploaderTest.java | 3 - cloudinary-android/README.md | 113 ++++++++++++++++++ cloudinary-android/pom.xml | 2 +- pom.xml | 2 +- .../controllers/PhotoController.java | 9 +- .../controllers/PhotoController.java | 4 +- 8 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 cloudinary-android/README.md diff --git a/README.md b/README.md index 7e2227ed..25d26966 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,23 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** This Java library is intended mainly for Web applications. For **Android** integration, there is a dedicated library with a similar interface: https://github.com/cloudinary/cloudinary_android +**Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/cloudinary-android ## Getting started guide ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](http://cloudinary.com/documentation/java_integration#getting_started_guide)**. ## Setup ###################################################################### -The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: +The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml : com.cloudinary - cloudinary + cloudinary-http42 1.0.14 -Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/tarball/master) -and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/pom.xml) for library dependencies. +Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/tarball/master) +and see [pom.xml](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/blob/master/pom.xml) for library dependencies. ## Try it right away @@ -156,8 +156,8 @@ Returns an html input field for direct image upload, to be used in conjunction w Usage: - Map options = Cloudinary.asMap("resource_type", "auto"); - Map htmlOptions = Cloudinary.asMap("alt", "sample"); + Map options = ObjectUtils.asMap("resource_type", "auto"); + Map htmlOptions = ObjectUtils.asMap("alt", "sample"); String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. @@ -182,3 +182,4 @@ Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary) ## License ####################################################################### Released under the MIT license. + diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index ca2229dd..f2bc33c6 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -51,6 +51,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin + 4.0.0-rc.2 true diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index e6ba8dce..38f63f73 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -66,7 +66,6 @@ public void testUnsignedUpload() throws Exception { to_sign.put("public_id", result.getString("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); Log.d("TestRunner",cloudinary.config.apiSecret); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } @@ -295,10 +294,8 @@ public void testAutoTaggingRequest() { try { cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("auto_tagging", 0.5f)); } catch (Exception e) { - Log.i("DANIEL",e.getMessage()); for (int i = 0; i < e.getStackTrace().length; i++) { StackTraceElement x = e.getStackTrace()[i]; - Log.i("DANIEL",x.getClassName()+"."+x.getMethodName()+" "+x.getLineNumber()); } diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md new file mode 100644 index 00000000..1d18c3a9 --- /dev/null +++ b/cloudinary-android/README.md @@ -0,0 +1,113 @@ +Cloudinary +========== + +Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. + +Easily 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. + +Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. + +Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. + +For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. + +## Manual Setup ###################################################################### +Download cloudinary-core-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-core-1.0.15.jar) and cloudinary-android-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-android-1.0.15.jar) +and put them in your libs folder. + +## Maven Integration ###################################################################### +The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: + + + com.cloudinary + cloudinary-android + 1.0.15 + + + +## Try it right away + +Sign up for a [free account](https://cloudinary.com/users/register/free) so you can try out image transformations and seamless image delivery through CDN. + +*Note: Replace `demo` in all the following examples with your Cloudinary's `cloud name`.* + +Accessing an uploaded image with the `sample` public ID through a CDN: + + http://res.cloudinary.com/demo/image/upload/sample.jpg + +![Sample](https://res.cloudinary.com/demo/image/upload/w_0.4/sample.jpg "Sample") + +Generating a 150x100 version of the `sample` image and downloading it through a CDN: + + http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg + +![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") + +Converting to a 150x100 PNG with rounded corners of 20 pixels: + + http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png + +![Sample 150x150 Rounded PNG](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png "Sample 150x150 Rounded PNG") + +For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). + +Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: + + http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg + +![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") + +For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. + + +## Usage + +### Configuration + +Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. +Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. +See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. + +Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, +by when initializing the Cloudinary object, or by using the CLOUDINARY_URL meta-data property. + +The entry point of the library is the Cloudinary object. + +Here's an example of setting the configuration parameters programatically: + + Map config = new HashMap(); + config.put("cloud_name", "n07t21i7"); + config.put("api_key", "123456789012345"); + config.put("api_secret", "abcdeghijklmnopqrstuvwxyz12"); + Cloudinary cloudinary = new Cloudinary(config); + +Another example of setting the configuration parameters by providing the CLOUDINARY_URL value to the constructor: + + Cloudinary cloudinary = new Cloudinary("cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7"); + +Giving the context will allow Cloudinary to configure from the application's meta-data. + + Cloudinary cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getContext())); + +Then add a meta-data property to your application section in the AndroidManifest.xml + + + ... + + ... + + + + +### Embedding and transforming images + +Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: + +The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: + + cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") + +... + diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 33310aba..f30902d9 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -42,7 +42,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin - 3.9.0-rc.1 + 4.0.0-rc.2 19 diff --git a/pom.xml b/pom.xml index f86003b5..c6f695cd 100644 --- a/pom.xml +++ b/pom.xml @@ -16,9 +16,9 @@ cloudinary-core cloudinary-android - cloudinary-android-test cloudinary-taglib cloudinary-http42 + cloudinary-android-test diff --git a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java index 0da6ad84..16fefc3d 100644 --- a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java +++ b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java @@ -6,6 +6,7 @@ import cloudinary.repositories.PhotoRepository; import com.cloudinary.Cloudinary; +import com.cloudinary.utils.ObjectUtils; import com.cloudinary.Singleton; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +39,7 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult Map uploadResult = null; if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), - Cloudinary.asMap("resource_type", "auto")); + ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); photoUpload.setVersion((Long) uploadResult.get("version")); photoUpload.setSignature((String) uploadResult.get("signature")); @@ -79,10 +80,10 @@ public String directUnsignedUploadPhotoForm(ModelMap model) throws Exception { model.addAttribute("photoUpload", new PhotoUpload()); model.addAttribute("unsigned", true); Cloudinary cld = Singleton.getCloudinary(); - String preset = "sample_" + cld.apiSignRequest(Cloudinary.asMap("api_key", cld.getStringConfig("api_key")), cld.getStringConfig("api_secret")).substring(0, 10); + String preset = "sample_" + cld.apiSignRequest(ObjectUtils.asMap("api_key", cld.config.apiKey), cld.config.apiSecret).substring(0, 10); model.addAttribute("preset", preset); try { - Singleton.getCloudinary().api().createUploadPreset(Cloudinary.asMap( + Singleton.getCloudinary().api().createUploadPreset(ObjectUtils.asMap( "name", preset, "unsigned", true, "folder", "preset_folder")); @@ -90,4 +91,4 @@ public String directUnsignedUploadPhotoForm(ModelMap model) throws Exception { } return "direct_upload_form"; } -} \ No newline at end of file +} 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 d35a2a1d..4dbfa915 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 @@ -3,6 +3,7 @@ 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; import org.springframework.ui.ModelMap; @@ -27,6 +28,7 @@ @Controller @RequestMapping("/") public class PhotoController { + private final static GAEConnectionManager connectionManager = new GAEConnectionManager(); @RequestMapping(value = "/", method = RequestMethod.GET) public String listPhotos(ModelMap model) { @@ -50,7 +52,7 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { Singleton.getCloudinary().config.properties.put("connectionManager", connectionManager); uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), - Cloudinary.asMap("resource_type", "auto")); + ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); photoUpload.setVersion((Long) uploadResult.get("version")); From d437f016ac202847a80794e62fa9951d98ffa222 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Mon, 17 Nov 2014 10:40:18 +0200 Subject: [PATCH 018/592] added deprecated asMap method to Cloudinary (support old api) --- .../src/main/java/com/cloudinary/Cloudinary.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 7d8bdd59..3d5c5e14 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -229,5 +229,9 @@ protected Map parseConfigUrl(String cloudinaryUrl) { } return params; } - + + @Deprecated + public static Map asMap(Object... values) { + return ObjectUtils.asMap(values); + } } From 1772f58d9244613fc6fbb2e6b7c4f05aea83bd07 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Mon, 17 Nov 2014 11:03:55 +0200 Subject: [PATCH 019/592] promoted minor version (1.0.x -> 1.1.x) & fixed documentation external links --- cloudinary-android-test/pom.xml | 8 ++--- cloudinary-android/README.md | 33 +++++++++---------- cloudinary-android/pom.xml | 4 +-- cloudinary-core/pom.xml | 2 +- .../main/java/com/cloudinary/Cloudinary.java | 26 +++++++-------- cloudinary-http42/pom.xml | 3 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 10 +++--- samples/photo_album/pom.xml | 2 +- samples/photo_album_gae/pom.xml | 6 ++-- 10 files changed, 46 insertions(+), 50 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f2bc33c6..e1ce605b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT com.cloudinary cloudinary-android-test - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT apk Cloudinary Android Test Project @@ -31,13 +31,13 @@ com.cloudinary cloudinary jar - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT com.cloudinary cloudinary-android jar - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT junit diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 1d18c3a9..67d33bd4 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -1,20 +1,20 @@ Cloudinary ========== -Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. +Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. -Easily 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. +Easily 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. Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. -Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. +Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-core-1.0.15.jar) and cloudinary-android-1.0.15.jar from [here](http://res.cloudinary.com/cloudinary/raw/upload/cloudinary-android-1.0.15.jar) +Download cloudinary-core-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-core-1.1.0.jar) and cloudinary-android-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-android-1.1.0.jar) and put them in your libs folder. ## Maven Integration ###################################################################### @@ -23,7 +23,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.0.15 + 1.1.0 @@ -45,7 +45,7 @@ Generating a 150x100 version of the `sample` image and downloading it through a ![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") -Converting to a 150x100 PNG with rounded corners of 20 pixels: +Converting to a 150x100 PNG with rounded corners of 20 pixels: http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png @@ -54,26 +54,26 @@ Converting to a 150x100 PNG with rounded corners of 20 pixels: For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: - + http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg - + ![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") -For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. +For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. ## Usage ### Configuration -Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. -Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. +Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. +Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. -Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, +Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, by when initializing the Cloudinary object, or by using the CLOUDINARY_URL meta-data property. -The entry point of the library is the Cloudinary object. +The entry point of the library is the Cloudinary object. Here's an example of setting the configuration parameters programatically: @@ -100,7 +100,7 @@ Then add a meta-data property to your application section in the AndroidManifest - + ### Embedding and transforming images Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: @@ -110,4 +110,3 @@ The following example generates the url for accessing an uploaded `sample` image cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") ... - diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index f30902d9..5bfcaa6f 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-android @@ -63,5 +63,3 @@ - - diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index a31d9454..8c4e6fcc 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3d5c5e14..e96b4458 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -23,17 +23,17 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) public class Cloudinary { - + private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList("com.cloudinary.android.UploaderStrategy","com.cloudinary.http42.UploaderStrategy","com.cloudinary.http43.UploaderStrategy")); private static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy" )); private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UrlBuilderStrategy", "com.cloudinary.http42.UrlBuilderStrategy", "com.cloudinary.http43.UrlBuilderStrategy" )); - + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; 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.0.15"; + public final static String VERSION = "1.1.0"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; @@ -43,7 +43,7 @@ public class Cloudinary { public Uploader uploader(){ return new Uploader(this,uploaderStrategy); - + }; public Api api(){ @@ -54,33 +54,33 @@ public static void registerUploaderStrategy(String className){ if (!UPLOAD_STRATEGIES.contains(className)){ UPLOAD_STRATEGIES.add(className); } - + } - + public static void registerAPIStrategy(String className){ if (!API_STRATEGIES.contains(className)){ API_STRATEGIES.add(className); } } - + public static void registerUrlBuilderStrategy(String className){ if (!URLBUILDER_STRATEGIES.contains(className)){ URLBUILDER_STRATEGIES.add(className); } } - + private void loadStrategies() { uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); - + if (uploaderStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); } - + apiStrategy= StrategyLoader.find(API_STRATEGIES); if (apiStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); } - + urlBuilderStrategy= StrategyLoader.find(URLBUILDER_STRATEGIES); if (urlBuilderStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(URLBUILDER_STRATEGIES, ",") + "]"); @@ -179,7 +179,7 @@ public String privateDownload(String publicId, String format, Map param : params.entrySet()) { builder.addParam(param.getKey(), param.getValue().toString()); } @@ -229,7 +229,7 @@ protected Map parseConfigUrl(String cloudinaryUrl) { } return params; } - + @Deprecated public static Map asMap(Object... values) { return ObjectUtils.asMap(values); diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 9e5244ba..654e976e 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-http42 @@ -45,4 +45,3 @@ - diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index eb5f52db..ac76a27a 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index c6f695cd..2c4de779 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.0.15-SNAPSHOT + 1.1.0-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -22,10 +22,10 @@ - Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. - Easily 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. + Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. + Easily 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. http://github.com/cloudinary/cloudinary_java diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 6e523c7c..93283b53 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,7 +23,7 @@ com.cloudinary cloudinary-taglib - 1.0.14 + 1.1.0 org.springframework diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index bfdbe2ef..4dbfe92a 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -38,7 +38,7 @@ com.cloudinary cloudinary-taglib - 1.0.13 + 1.1.0 org.springframework @@ -121,7 +121,7 @@ 1.9.0 test - + com.google.appengine appengine-testing @@ -140,7 +140,7 @@ hibernate-validator-annotation-processor 4.1.0.Final - + gmultipart gmultipart From 3685b965aea918b354fd331199457d796f6b3485 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 17 Nov 2014 11:13:27 +0200 Subject: [PATCH 020/592] Fix modules artifactId --- cloudinary-core/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 8c4e6fcc..39d91fda 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -7,7 +7,7 @@ 1.1.0-SNAPSHOT - cloudinary + cloudinary-core jar diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index ac76a27a..4c3738ca 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -15,7 +15,7 @@ com.cloudinary - cloudinary + cloudinary-core ${project.version} From 8a91cf809f7a0f9641da910f8c9a9c864bf1cd19 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 17 Nov 2014 14:21:12 +0200 Subject: [PATCH 021/592] Fix documentation. Fix dependencies --- CHANGES.txt | 3 +++ README.md | 4 +++- cloudinary-android-test/pom.xml | 2 +- cloudinary-android/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 29c03669..14c25052 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,2 +1,5 @@ 1.0.11 - 2014-03-04 - new update method. add listing by moderation kind and status. add moderation status in listing. add moderation flag in upload. add moderation_status in update. add ocr, raw_conversion, categorization, detection, similarity_search and auto_tagging parameters in update and upload. add support for uploading large raw files 1.0.12 - 2014-03-04 - Fix handling of Booleans in uploader API +1.0.13 - 2014-04-29 - Allow passing ClientConnectionManager. Add support for opacity. Support upload_presets. Support unsigned uploads. Support start_at for resource listing. Support phash for upload and resource details. Support rate limit header in Api calls. +1.0.14 - 2014-07-29 - Add background_removal. Support return_delete_token in upload/update params. Support responsive and hidpi. Support custom coordinates. +1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. \ No newline at end of file diff --git a/README.md b/README.md index 25d26966..753d4936 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. +**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. + **Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/cloudinary-android ## Getting started guide @@ -25,7 +27,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-http42 - 1.0.14 + 1.1.0 Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/tarball/master) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index e1ce605b..8ed9e5e4 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -29,7 +29,7 @@ com.cloudinary - cloudinary + cloudinary-core jar 1.1.0-SNAPSHOT diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 5bfcaa6f..ca922841 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -32,7 +32,7 @@ com.cloudinary - cloudinary + cloudinary-core ${project.version} diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 654e976e..30352ed3 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -14,7 +14,7 @@ com.cloudinary - cloudinary + cloudinary-core ${project.version} From dd94391bc10c88fccc2a21d09f716f463062a37a Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 17 Nov 2014 14:22:51 +0200 Subject: [PATCH 022/592] Update CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 14c25052..8d034d9b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,4 +2,4 @@ 1.0.12 - 2014-03-04 - Fix handling of Booleans in uploader API 1.0.13 - 2014-04-29 - Allow passing ClientConnectionManager. Add support for opacity. Support upload_presets. Support unsigned uploads. Support start_at for resource listing. Support phash for upload and resource details. Support rate limit header in Api calls. 1.0.14 - 2014-07-29 - Add background_removal. Support return_delete_token in upload/update params. Support responsive and hidpi. Support custom coordinates. -1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. \ No newline at end of file +1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) \ No newline at end of file From 9587c5aa7226f065faf456a9ae3a08f311ce8077 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Mon, 17 Nov 2014 18:13:19 +0200 Subject: [PATCH 023/592] - changed org.json to org.cloudinary.json due to Android optimization issues . - removed dependency on SimpleJSON from tablib --- cloudinary-android-test/pom.xml | 19 +- .../com/cloudinary/test/UploaderTest.java | 5 +- cloudinary-android/pom.xml | 10 +- .../cloudinary/android/UploaderStrategy.java | 4 +- cloudinary-core/pom.xml | 2 +- .../main/java/com/cloudinary/Uploader.java | 2 +- .../com/cloudinary/utils/ObjectUtils.java | 7 +- .../java/org/cloudinary/json/JSONArray.java | 972 +++++++++++++++++ .../{ => cloudinary}/json/JSONException.java | 2 +- .../org/{ => cloudinary}/json/JSONObject.java | 2 +- .../org/{ => cloudinary}/json/JSONString.java | 2 +- .../{ => cloudinary}/json/JSONTokener.java | 2 +- .../src/main/java/org/json/JSONArray.java | 977 ------------------ cloudinary-http42/pom.xml | 7 +- .../com/cloudinary/http42/ApiStrategy.java | 12 +- .../cloudinary/http42/UploaderStrategy.java | 11 +- .../java/com/cloudinary/test/ApiTest.java | 951 ++++++++--------- .../com/cloudinary/test/UploaderTest.java | 137 +-- cloudinary-taglib/pom.xml | 2 +- 19 files changed, 1542 insertions(+), 1584 deletions(-) create mode 100644 cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java rename cloudinary-core/src/main/java/org/{ => cloudinary}/json/JSONException.java (97%) rename cloudinary-core/src/main/java/org/{ => cloudinary}/json/JSONObject.java (99%) rename cloudinary-core/src/main/java/org/{ => cloudinary}/json/JSONString.java (95%) rename cloudinary-core/src/main/java/org/{ => cloudinary}/json/JSONTokener.java (99%) delete mode 100644 cloudinary-core/src/main/java/org/json/JSONArray.java diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index e1ce605b..a958532e 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -15,6 +15,12 @@ Cloudinary Android Test Project + + com.cloudinary + cloudinary-android + jar + 1.1.0-SNAPSHOT + com.google.android android @@ -27,18 +33,7 @@ 2.3.1 provided - - com.cloudinary - cloudinary - jar - 1.1.0-SNAPSHOT - - - com.cloudinary - cloudinary-android - jar - 1.1.0-SNAPSHOT - + junit junit diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 38f63f73..5490c8cb 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -8,8 +8,8 @@ import java.util.HashMap; import java.util.Map; -import org.json.JSONArray; -import org.json.JSONObject; +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; import android.test.InstrumentationTestCase; import android.util.Log; @@ -23,6 +23,7 @@ public class UploaderTest extends InstrumentationTestCase { + private Cloudinary cloudinary; private static boolean first = true; diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 5bfcaa6f..5c44c0f4 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -18,6 +18,11 @@ Cloudinary Android Library + + com.cloudinary + cloudinary-core + ${project.version} + com.google.android android @@ -30,11 +35,6 @@ 2.3.1 provided - - com.cloudinary - cloudinary - ${project.version} - diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index c84dafb1..925e0a74 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -8,8 +8,8 @@ import java.util.Collection; import java.util.Map; -import org.json.JSONException; -import org.json.JSONObject; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.utils.ObjectUtils; diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 8c4e6fcc..39d91fda 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -7,7 +7,7 @@ 1.1.0-SNAPSHOT - cloudinary + cloudinary-core jar diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 128ccc4a..522084ec 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Map; -import org.json.JSONObject; +import org.cloudinary.json.JSONObject; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.utils.ObjectUtils; 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 b705d684..515a3620 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -9,9 +9,10 @@ import java.util.List; import java.util.Map; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; + public class ObjectUtils { diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java new file mode 100644 index 00000000..f8a3367b --- /dev/null +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java @@ -0,0 +1,972 @@ +package org.cloudinary.json; + +/* + Copyright (c) 2002 JSON.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + The Software shall be used for Good, not Evil. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there is , + *  (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, and + * if they do not contain any of these characters: + * { } [ ] / \ : , # and if they do not look like numbers and if + * they are not the reserved words true, false, or + * null.
  • + *
+ * + * @author JSON.org + * @version 2014-05-03 + */ +public class JSONArray { + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * + * @param x + * A JSONTokener + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() != ']') { + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + /** + * Construct a JSONArray from a source JSON text. + * + * @param source + * A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException + * If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection + * A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = new ArrayList(); + if (collection != null) { + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + this.myArrayList.add(JSONObject.wrap(iter.next())); + } + } + } + + /** + * Construct a JSONArray from an array + * + * @throws JSONException + * If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + } + + /** + * Get the object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException + * If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" + * and "false" are converted to boolean. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException + * If there is no value for the index or if the value is not + * convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a boolean."); + } + + /** + * Get the double value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public double getDouble(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).doubleValue() : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the int value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).intValue() : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException + * If there is no value for the index. or if the value is not a + * JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index + * subscript + * @return A JSONObject value. + * @throws JSONException + * If there is no value for the index or if the value is not a + * JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); + } + + /** + * Get the long value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException + * If the key is not found or if the value cannot be converted + * to a number. + */ + public long getLong(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).longValue() : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the string associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException + * If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String) object; + } + throw new JSONException("JSONArray[" + index + "] not a string."); + } + + /** + * Determine if the value is null. + * + * @param index + * The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. Warning: + * This method assumes that the data structure is acyclical. + * + * @param separator + * A string that will be inserted between the elements. + * @return a string. + * @throws JSONException + * If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index + * The index must be between 0 and length() - 1. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index + * The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * subscript + * @param defaultValue + * The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return this.getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional int value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return this.getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional JSONArray associated with an index. + * + * @param index + * subscript + * @return A JSONArray value, or null if the index has no value, or if the + * value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = this.opt(index); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if + * the key is not found, or null if the index has no value, or if the value + * is not a JSONObject. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = this.opt(index); + return o instanceof JSONObject ? (JSONObject) o : null; + } + + /** + * Get the optional long value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return this.getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value is not a + * string and is not null, then it is coverted to a string. + * + * @param index + * The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is + * returned if the key is not found. + * + * @param index + * The index must be between 0 and length() - 1. + * @param defaultValue + * The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object.toString(); + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value + * A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + this.put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param value + * A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + this.put(new JSONArray(value)); + return this; + } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value + * A double value. + * @throws JSONException + * if the value is not finite. + * @return this. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + this.put(d); + return this; + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value + * An int value. + * @return this. + */ + public JSONArray put(int value) { + this.put(new Integer(value)); + return this; + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value + * A long value. + * @return this. + */ + public JSONArray put(long value) { + this.put(new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject which + * is produced from a Map. + * + * @param value + * A Map value. + * @return this. + */ + public JSONArray put(Map value) { + this.put(new JSONObject(value)); + return this; + } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value + * An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * A boolean value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param index + * The subscript. + * @param value + * A Collection value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + this.put(index, new JSONArray(value)); + return this; + } + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A double value. + * @return this. + * @throws JSONException + * If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + this.put(index, new Double(value)); + return this; + } + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * An int value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + this.put(index, new Integer(value)); + return this; + } + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index + * The subscript. + * @param value + * A long value. + * @return this. + * @throws JSONException + * If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + this.put(index, new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index + * The subscript. + * @param value + * The Map value. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value)); + return this; + } + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index + * The subscript. + * @param value + * The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException + * If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + this.myArrayList.set(index, value); + } else { + while (index != this.length()) { + this.put(JSONObject.NULL); + } + this.put(value); + } + return this; + } + + /** + * Remove an index and close the hole. + * + * @param index + * The index of the element to be removed. + * @return The value that was associated with the index, or null if there + * was no value. + */ + public Object remove(int index) { + return index >= 0 && index < this.length() ? this.myArrayList.remove(index) : null; + } + + /** + * Determine if two JSONArrays are similar. They must contain similar + * sequences. + * + * @param other + * The other JSONArray + * @return true if they are equal + */ + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray) other).length()) { + return false; + } + for (int i = 0; i < len; i += 1) { + Object valueThis = this.get(i); + Object valueOther = ((JSONArray) other).get(i); + if (valueThis instanceof JSONObject) { + if (!((JSONObject) valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray) valueThis).similar(valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } + + /** + * Produce a JSONObject by combining a JSONArray of names with the values of + * this JSONArray. + * + * @param names + * A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException + * If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || this.length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary + * whitespace is added. If it is not possible to produce a syntactically + * correct JSON text then null will be returned instead. This could occur if + * the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable representation of the + * array. + */ + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method + * assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0).toString(); + } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The indention of the top level. + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + try { + boolean commanate = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newindent); + JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } + + public ArrayList toList(Class type) { + ArrayList listdata = new ArrayList(); + for (int i = 0; i < this.length(); i++) { + listdata.add((T)this.get(i)); + } + return listdata; + } + +} diff --git a/cloudinary-core/src/main/java/org/json/JSONException.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java similarity index 97% rename from cloudinary-core/src/main/java/org/json/JSONException.java rename to cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java index 6fef5194..6eb1b6b4 100644 --- a/cloudinary-core/src/main/java/org/json/JSONException.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java @@ -1,4 +1,4 @@ -package org.json; +package org.cloudinary.json; /** * The JSONException is thrown by the JSON.org classes when things are amiss. diff --git a/cloudinary-core/src/main/java/org/json/JSONObject.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java similarity index 99% rename from cloudinary-core/src/main/java/org/json/JSONObject.java rename to cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java index b7a21592..05b4a5c6 100644 --- a/cloudinary-core/src/main/java/org/json/JSONObject.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java @@ -1,4 +1,4 @@ -package org.json; +package org.cloudinary.json; /* Copyright (c) 2002 JSON.org diff --git a/cloudinary-core/src/main/java/org/json/JSONString.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java similarity index 95% rename from cloudinary-core/src/main/java/org/json/JSONString.java rename to cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java index 1f2d77dd..dfa3ff15 100644 --- a/cloudinary-core/src/main/java/org/json/JSONString.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java @@ -1,4 +1,4 @@ -package org.json; +package org.cloudinary.json; /** * The JSONString interface allows a toJSONString() * method so that a class can change the behavior of diff --git a/cloudinary-core/src/main/java/org/json/JSONTokener.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java similarity index 99% rename from cloudinary-core/src/main/java/org/json/JSONTokener.java rename to cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java index 32548ed9..fe53fa9a 100644 --- a/cloudinary-core/src/main/java/org/json/JSONTokener.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java @@ -1,4 +1,4 @@ -package org.json; +package org.cloudinary.json; import java.io.BufferedReader; import java.io.IOException; diff --git a/cloudinary-core/src/main/java/org/json/JSONArray.java b/cloudinary-core/src/main/java/org/json/JSONArray.java deleted file mode 100644 index 3f05548d..00000000 --- a/cloudinary-core/src/main/java/org/json/JSONArray.java +++ /dev/null @@ -1,977 +0,0 @@ -package org.json; - -/* - Copyright (c) 2002 JSON.org - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - The Software shall be used for Good, not Evil. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; - -/** - * A JSONArray is an ordered sequence of values. Its external text form is a - * string wrapped in square brackets with commas separating the values. The - * internal form is an object having get and opt - * methods for accessing the values by index, and put methods for - * adding or replacing values. The values can be any of these types: - * Boolean, JSONArray, JSONObject, - * Number, String, or the - * JSONObject.NULL object. - *

- * The constructor can convert a JSON text into a Java object. The - * toString method converts to JSON text. - *

- * A get method returns a value if one can be found, and throws an - * exception if one cannot be found. An opt method returns a - * default value instead of throwing an exception, and so is useful for - * obtaining optional values. - *

- * The generic get() and opt() methods return an - * object which you can cast or query for type. There are also typed - * get and opt methods that do type checking and type - * coercion for you. - *

- * The texts produced by the toString methods strictly conform to - * JSON syntax rules. The constructors are more forgiving in the texts they will - * accept: - *

    - *
  • An extra , (comma) may appear just - * before the closing bracket.
  • - *
  • The null value will be inserted when there is , - *  (comma) elision.
  • - *
  • Strings may be quoted with ' (single - * quote).
  • - *
  • Strings do not need to be quoted at all if they do not begin with a quote - * or single quote, and if they do not contain leading or trailing spaces, and - * if they do not contain any of these characters: - * { } [ ] / \ : , # and if they do not look like numbers and - * if they are not the reserved words true, false, or - * null.
  • - *
- * - * @author JSON.org - * @version 2014-05-03 - */ -public class JSONArray { - - /** - * The arrayList where the JSONArray's properties are kept. - */ - private final ArrayList myArrayList; - - /** - * Construct an empty JSONArray. - */ - public JSONArray() { - this.myArrayList = new ArrayList(); - } - - /** - * Construct a JSONArray from a JSONTokener. - * - * @param x - * A JSONTokener - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(JSONTokener x) throws JSONException { - this(); - if (x.nextClean() != '[') { - throw x.syntaxError("A JSONArray text must start with '['"); - } - if (x.nextClean() != ']') { - x.back(); - for (;;) { - if (x.nextClean() == ',') { - x.back(); - this.myArrayList.add(JSONObject.NULL); - } else { - x.back(); - this.myArrayList.add(x.nextValue()); - } - switch (x.nextClean()) { - case ',': - if (x.nextClean() == ']') { - return; - } - x.back(); - break; - case ']': - return; - default: - throw x.syntaxError("Expected a ',' or ']'"); - } - } - } - } - - /** - * Construct a JSONArray from a source JSON text. - * - * @param source - * A string that begins with [ (left - * bracket) and ends with ] - *  (right bracket). - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(String source) throws JSONException { - this(new JSONTokener(source)); - } - - /** - * Construct a JSONArray from a Collection. - * - * @param collection - * A Collection. - */ - public JSONArray(Collection collection) { - this.myArrayList = new ArrayList(); - if (collection != null) { - Iterator iter = collection.iterator(); - while (iter.hasNext()) { - this.myArrayList.add(JSONObject.wrap(iter.next())); - } - } - } - - /** - * Construct a JSONArray from an array - * - * @throws JSONException - * If not an array. - */ - public JSONArray(Object array) throws JSONException { - this(); - if (array.getClass().isArray()) { - int length = Array.getLength(array); - for (int i = 0; i < length; i += 1) { - this.put(JSONObject.wrap(Array.get(array, i))); - } - } else { - throw new JSONException( - "JSONArray initial value should be a string or collection or array."); - } - } - - /** - * Get the object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object value. - * @throws JSONException - * If there is no value for the index. - */ - public Object get(int index) throws JSONException { - Object object = this.opt(index); - if (object == null) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - return object; - } - - /** - * Get the boolean value associated with an index. The string values "true" - * and "false" are converted to boolean. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - * @throws JSONException - * If there is no value for the index or if the value is not - * convertible to boolean. - */ - public boolean getBoolean(int index) throws JSONException { - Object object = this.get(index); - if (object.equals(Boolean.FALSE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { - return false; - } else if (object.equals(Boolean.TRUE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { - return true; - } - throw new JSONException("JSONArray[" + index + "] is not a boolean."); - } - - /** - * Get the double value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public double getDouble(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).doubleValue() - : Double.parseDouble((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the int value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value is not a number. - */ - public int getInt(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).intValue() - : Integer.parseInt((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the JSONArray associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONArray value. - * @throws JSONException - * If there is no value for the index. or if the value is not a - * JSONArray - */ - public JSONArray getJSONArray(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONArray) { - return (JSONArray) object; - } - throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); - } - - /** - * Get the JSONObject associated with an index. - * - * @param index - * subscript - * @return A JSONObject value. - * @throws JSONException - * If there is no value for the index or if the value is not a - * JSONObject - */ - public JSONObject getJSONObject(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONObject) { - return (JSONObject) object; - } - throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); - } - - /** - * Get the long value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public long getLong(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).longValue() - : Long.parseLong((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the string associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A string value. - * @throws JSONException - * If there is no string value for the index. - */ - public String getString(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof String) { - return (String) object; - } - throw new JSONException("JSONArray[" + index + "] not a string."); - } - - /** - * Determine if the value is null. - * - * @param index - * The index must be between 0 and length() - 1. - * @return true if the value at the index is null, or if there is no value. - */ - public boolean isNull(int index) { - return JSONObject.NULL.equals(this.opt(index)); - } - - /** - * Make a string from the contents of this JSONArray. The - * separator string is inserted between each element. Warning: - * This method assumes that the data structure is acyclical. - * - * @param separator - * A string that will be inserted between the elements. - * @return a string. - * @throws JSONException - * If the array contains an invalid number. - */ - public String join(String separator) throws JSONException { - int len = this.length(); - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < len; i += 1) { - if (i > 0) { - sb.append(separator); - } - sb.append(JSONObject.valueToString(this.myArrayList.get(i))); - } - return sb.toString(); - } - - /** - * Get the number of elements in the JSONArray, included nulls. - * - * @return The length (or size). - */ - public int length() { - return this.myArrayList.size(); - } - - /** - * Get the optional object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object value, or null if there is no object at that index. - */ - public Object opt(int index) { - return (index < 0 || index >= this.length()) ? null : this.myArrayList - .get(index); - } - - /** - * Get the optional boolean value associated with an index. It returns false - * if there is no value at that index, or if the value is not Boolean.TRUE - * or the String "true". - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - */ - public boolean optBoolean(int index) { - return this.optBoolean(index, false); - } - - /** - * Get the optional boolean value associated with an index. It returns the - * defaultValue if there is no value at that index or if it is not a Boolean - * or the String "true" or "false" (case insensitive). - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * A boolean default. - * @return The truth. - */ - public boolean optBoolean(int index, boolean defaultValue) { - try { - return this.getBoolean(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional double value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public double optDouble(int index) { - return this.optDouble(index, Double.NaN); - } - - /** - * Get the optional double value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * subscript - * @param defaultValue - * The default value. - * @return The value. - */ - public double optDouble(int index, double defaultValue) { - try { - return this.getDouble(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional int value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public int optInt(int index) { - return this.optInt(index, 0); - } - - /** - * Get the optional int value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public int optInt(int index, int defaultValue) { - try { - return this.getInt(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional JSONArray associated with an index. - * - * @param index - * subscript - * @return A JSONArray value, or null if the index has no value, or if the - * value is not a JSONArray. - */ - public JSONArray optJSONArray(int index) { - Object o = this.opt(index); - return o instanceof JSONArray ? (JSONArray) o : null; - } - - /** - * Get the optional JSONObject associated with an index. Null is returned if - * the key is not found, or null if the index has no value, or if the value - * is not a JSONObject. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONObject value. - */ - public JSONObject optJSONObject(int index) { - Object o = this.opt(index); - return o instanceof JSONObject ? (JSONObject) o : null; - } - - /** - * Get the optional long value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public long optLong(int index) { - return this.optLong(index, 0); - } - - /** - * Get the optional long value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public long optLong(int index, long defaultValue) { - try { - return this.getLong(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional string value associated with an index. It returns an - * empty string if there is no value at that index. If the value is not a - * string and is not null, then it is coverted to a string. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A String value. - */ - public String optString(int index) { - return this.optString(index, ""); - } - - /** - * Get the optional string associated with an index. The defaultValue is - * returned if the key is not found. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return A String value. - */ - public String optString(int index, String defaultValue) { - Object object = this.opt(index); - return JSONObject.NULL.equals(object) ? defaultValue : object - .toString(); - } - - /** - * Append a boolean value. This increases the array's length by one. - * - * @param value - * A boolean value. - * @return this. - */ - public JSONArray put(boolean value) { - this.put(value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param value - * A Collection value. - * @return this. - */ - public JSONArray put(Collection value) { - this.put(new JSONArray(value)); - return this; - } - - /** - * Append a double value. This increases the array's length by one. - * - * @param value - * A double value. - * @throws JSONException - * if the value is not finite. - * @return this. - */ - public JSONArray put(double value) throws JSONException { - Double d = new Double(value); - JSONObject.testValidity(d); - this.put(d); - return this; - } - - /** - * Append an int value. This increases the array's length by one. - * - * @param value - * An int value. - * @return this. - */ - public JSONArray put(int value) { - this.put(new Integer(value)); - return this; - } - - /** - * Append an long value. This increases the array's length by one. - * - * @param value - * A long value. - * @return this. - */ - public JSONArray put(long value) { - this.put(new Long(value)); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject which - * is produced from a Map. - * - * @param value - * A Map value. - * @return this. - */ - public JSONArray put(Map value) { - this.put(new JSONObject(value)); - return this; - } - - /** - * Append an object value. This increases the array's length by one. - * - * @param value - * An object value. The value should be a Boolean, Double, - * Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. - * @return this. - */ - public JSONArray put(Object value) { - this.myArrayList.add(value); - return this; - } - - /** - * Put or replace a boolean value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * A boolean value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, boolean value) throws JSONException { - this.put(index, value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param index - * The subscript. - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is not finite. - */ - public JSONArray put(int index, Collection value) throws JSONException { - this.put(index, new JSONArray(value)); - return this; - } - - /** - * Put or replace a double value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A double value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is not finite. - */ - public JSONArray put(int index, double value) throws JSONException { - this.put(index, new Double(value)); - return this; - } - - /** - * Put or replace an int value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * An int value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, int value) throws JSONException { - this.put(index, new Integer(value)); - return this; - } - - /** - * Put or replace a long value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A long value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, long value) throws JSONException { - this.put(index, new Long(value)); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject that - * is produced from a Map. - * - * @param index - * The subscript. - * @param value - * The Map value. - * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. - */ - public JSONArray put(int index, Map value) throws JSONException { - this.put(index, new JSONObject(value)); - return this; - } - - /** - * Put or replace an object value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * The value to put into the array. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. - */ - public JSONArray put(int index, Object value) throws JSONException { - JSONObject.testValidity(value); - if (index < 0) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - if (index < this.length()) { - this.myArrayList.set(index, value); - } else { - while (index != this.length()) { - this.put(JSONObject.NULL); - } - this.put(value); - } - return this; - } - - /** - * Remove an index and close the hole. - * - * @param index - * The index of the element to be removed. - * @return The value that was associated with the index, or null if there - * was no value. - */ - public Object remove(int index) { - return index >= 0 && index < this.length() - ? this.myArrayList.remove(index) - : null; - } - - /** - * Determine if two JSONArrays are similar. - * They must contain similar sequences. - * - * @param other The other JSONArray - * @return true if they are equal - */ - public boolean similar(Object other) { - if (!(other instanceof JSONArray)) { - return false; - } - int len = this.length(); - if (len != ((JSONArray)other).length()) { - return false; - } - for (int i = 0; i < len; i += 1) { - Object valueThis = this.get(i); - Object valueOther = ((JSONArray)other).get(i); - if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { - return false; - } - } else if (!valueThis.equals(valueOther)) { - return false; - } - } - return true; - } - - /** - * Produce a JSONObject by combining a JSONArray of names with the values of - * this JSONArray. - * - * @param names - * A JSONArray containing a list of key strings. These will be - * paired with the values. - * @return A JSONObject, or null if there are no names or if this JSONArray - * has no values. - * @throws JSONException - * If any of the names are null. - */ - public JSONObject toJSONObject(JSONArray names) throws JSONException { - if (names == null || names.length() == 0 || this.length() == 0) { - return null; - } - JSONObject jo = new JSONObject(); - for (int i = 0; i < names.length(); i += 1) { - jo.put(names.getString(i), this.opt(i)); - } - return jo; - } - - /** - * Make a JSON text of this JSONArray. For compactness, no unnecessary - * whitespace is added. If it is not possible to produce a syntactically - * correct JSON text then null will be returned instead. This could occur if - * the array contains an invalid number. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return a printable, displayable, transmittable representation of the - * array. - */ - public String toString() { - try { - return this.toString(0); - } catch (Exception e) { - return null; - } - } - - /** - * Make a prettyprinted JSON text of this JSONArray. Warning: This method - * assumes that the data structure is acyclical. - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @return a printable, displayable, transmittable representation of the - * object, beginning with [ (left - * bracket) and ending with ] - *  (right bracket). - * @throws JSONException - */ - public String toString(int indentFactor) throws JSONException { - StringWriter sw = new StringWriter(); - synchronized (sw.getBuffer()) { - return this.write(sw, indentFactor, 0).toString(); - } - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return The writer. - * @throws JSONException - */ - public Writer write(Writer writer) throws JSONException { - return this.write(writer, 0, 0); - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @param indent - * The indention of the top level. - * @return The writer. - * @throws JSONException - */ - Writer write(Writer writer, int indentFactor, int indent) - throws JSONException { - try { - boolean commanate = false; - int length = this.length(); - writer.write('['); - - if (length == 1) { - JSONObject.writeValue(writer, this.myArrayList.get(0), - indentFactor, indent); - } else if (length != 0) { - final int newindent = indent + indentFactor; - - for (int i = 0; i < length; i += 1) { - if (commanate) { - writer.write(','); - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, newindent); - JSONObject.writeValue(writer, this.myArrayList.get(i), - indentFactor, newindent); - commanate = true; - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, indent); - } - writer.write(']'); - return writer; - } catch (IOException e) { - throw new JSONException(e); - } - } -} diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 654e976e..217f5c09 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -14,7 +14,7 @@ com.cloudinary - cloudinary + cloudinary-core ${project.version} @@ -37,11 +37,6 @@ httpmime 4.2.1 - - com.googlecode.json-simple - json-simple - 1.1.1 - 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 cd0b1a96..2537e4e0 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -15,8 +15,8 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.impl.client.DefaultHttpClient; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; import com.cloudinary.Api; import com.cloudinary.Api.HttpMethod; @@ -94,12 +94,14 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options, Objec } Map result; + try { - result = (Map) JSONValue.parseWithException(responseData); - } catch (ParseException e) { + JSONObject responseJSON = new JSONObject(responseData); + result= ObjectUtils.toMap(responseJSON); + } catch (JSONException e) { throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); } + if (result.containsKey("error")) { Map error = (Map) result.get("error"); if (returnError) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 1f91c95d..768b8211 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -9,6 +9,7 @@ import static org.junit.Assume.assumeNotNull; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -30,468 +31,469 @@ import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.utils.ObjectUtils; -@SuppressWarnings({"rawtypes", "unchecked"}) +@SuppressWarnings({ "rawtypes", "unchecked" }) public class ApiTest { - private Cloudinary cloudinary; - private Api api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); - - @BeforeClass - public static void setUpClass() throws IOException { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - return; - } - Api api = cloudinary.api(); - try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try{api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap());}catch (Exception e) {} - try{api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap());}catch (Exception e) {} - Map options = ObjectUtils.asMap( - "public_id", "api_test", - "tags", new String[]{"api_test_tag", uniqueTag}, - "context", "key=value", - "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - options.put("public_id", "api_test1"); - cloudinary.uploader().upload("src/test/resources/logo.png", options); - } - - @Before - public void setUp() { - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.config.apiSecret); - this.api = cloudinary.api(); - } - - public Map findByAttr(List elements, String attr, Object value) { - for (Map element : elements) { - if (value.equals(element.get(attr))) { - return element; - } - } - return null; - } - - @Test - public void test01ResourceTypes() throws Exception { - // should allow listing resource_types - Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); - } - - @Test - public void test02Resources() throws Exception { - // should allow listing resources - Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } - - @Test - public void test03ResourcesCursor() throws Exception { - // should allow listing resources with cursor - Map options = new HashMap(); - options.put("max_results", 1); - Map result = api.resources(options); - List resources = (List) result.get("resources"); - assertNotNull(resources); - assertEquals(1, resources.size()); - assertNotNull(result.get("next_cursor")); - - options.put("next_cursor", result.get("next_cursor")); - Map result2 = api.resources(options); - List resources2 = (List) result2.get("resources"); - assertNotNull(resources2); - assertEquals(resources2.size(), 1); - assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void test04ResourcesByType() throws Exception { - // should allow listing resources by type - Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - } - - @Test - public void test05ResourcesByPrefix() throws Exception { - // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void testResourcesListingDirection() throws Exception { - // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); - List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); - List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); - } - - @Ignore - public void testResourcesListingStartAt() throws Exception { - // should allow listing resources by start date - make sure your clock is set correctly!!! - Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); + private Cloudinary cloudinary; + private Api api; + private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + return; + } + Api api = cloudinary.api(); + try { + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", + Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + options.put("public_id", "api_test1"); + cloudinary.uploader().upload("src/test/resources/logo.png", options); + } + + @Before + public void setUp() { + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + } + + public Map findByAttr(List elements, String attr, Object value) { + for (Map element : elements) { + if (value.equals(element.get(attr))) { + return element; + } + } + return null; + } + + @Test + public void test01ResourceTypes() throws Exception { + // should allow listing resource_types + Map result = api.resourceTypes(ObjectUtils.emptyMap()); + assertContains("image", (Collection) result.get("resource_types")); + } + + @Test + public void test02Resources() throws Exception { + // should allow listing resources + Map result = api.resources(ObjectUtils.emptyMap()); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Test + public void test03ResourcesCursor() throws Exception { + // should allow listing resources with cursor + Map options = new HashMap(); + options.put("max_results", 1); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + assertEquals(1, resources.size()); + assertNotNull(result.get("next_cursor")); + + options.put("next_cursor", result.get("next_cursor")); + Map result2 = api.resources(options); + List resources2 = (List) result2.get("resources"); + assertNotNull(resources2); + assertEquals(resources2.size(), 1); + assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void test04ResourcesByType() throws Exception { + // should allow listing resources by type + Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + } + + @Test + public void test05ResourcesByPrefix() throws Exception { + // should allow listing resources by prefix + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void testResourcesListingDirection() throws Exception { + // should allow listing resources in both directions + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + List resources = (List) result.get("resources"); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + List resourcesDesc = (List) result.get("resources"); + Collections.reverse(resources); + assertEquals(resources, resourcesDesc); + } + + @Ignore + public void testResourcesListingStartAt() throws Exception { + // should allow listing resources by start date - make sure your clock + // is set correctly!!! Thread.sleep(2000L); - Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); - List resources = (List) listResources.get("resources"); - assertEquals(response.get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void testResourcesByPublicIds() throws Exception { - // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test06ResourcesTag() throws Exception { - // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); - assertNotNull(resource); - List resources = (List) result.get("resources"); - boolean found = false; - for (Map r: resources) { - org.json.simple.JSONArray tags = (org.json.simple.JSONArray) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test07ResourceMetadata() throws Exception { - // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); - assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); - assertEquals(resource.get("bytes"), 3381L); - assertEquals(((List) resource.get("derived")).size(), 1); - } - - @Test - public void test08DeleteDerived() throws Exception { - // should allow deleting derived resource - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap( - "public_id", "api_test3", - "eager", Collections.singletonList(new Transformation().width(101).crop("scale")) - )); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - List derived = (List) resource.get("derived"); - assertEquals(derived.size(), 1); - String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - derived = (List) resource.get("derived"); - assertEquals(derived.size(), 0); - } - - @Test(expected = NotFound.class) - public void test09DeleteResources() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByPrefix() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByTags() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); - } - - @Test - public void test10Tags() throws Exception { - // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - } - - @Test - public void test11TagsPrefix() throws Exception { - // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); - tags = (List) result.get("tags"); - assertEquals(0, tags.size()); - } - - @Test - public void test12Transformations() throws Exception { - // should allow listing transformations - Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); - - assertNotNull(transformation); - assertTrue((Boolean) transformation.get("used")); - } - - @Test - public void test13TransformationMetadata() throws Exception { - // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100) - .generate()); - } - - @Test - public void test14TransformationUpdate() throws Exception { - // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), false); - } - - @Test - public void test15TransformationCreate() throws Exception { - // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102) - .generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test15aTransformationUnsafeUpdate() throws Exception { - // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103) - .generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test16aTransformationDelete() throws Exception { - // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test - public void test17aTransformationDeleteImplicit() throws Exception { - // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ - @Test(expected = NotFound.class) - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - @Test - public void test18Usage() throws Exception { - // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); - assertNotNull(result.get("last_updated")); - } - - @Test - public void test19Ping() throws Exception { - // should support ping API call - Map result = api.ping(ObjectUtils.emptyMap()); - assertEquals(result.get("status"), "ok"); - } - - // This test must be last because it deletes (potentially) all dependent transformations which some tests rely on. - // Add @Test if you really want to test it - This test deletes derived resources! - public void testDeleteAllResources() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((org.json.simple.JSONArray) result.get("derived")).size()); - api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); - //assertEquals(0, ((org.json.simple.JSONArray) result.get("derived")).size()); - } - - - @Test - public void testManualModeration() throws Exception { - // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation","manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); - } - + java.util.Date startAt = new java.util.Date(); + Thread.sleep(2000L); + Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); + assertEquals(response.get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void testResourcesByPublicIds() throws Exception { + // should allow listing resources by public ids + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test06ResourcesTag() throws Exception { + // should allow listing resources by tag + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); + assertNotNull(resource); + List resources = (List) result.get("resources"); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test07ResourceMetadata() throws Exception { + // should allow get resource metadata + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + assertNotNull(resource); + assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("bytes"), 3381); + assertEquals(((List) resource.get("derived")).size(), 1); + } + + @Test + public void test08DeleteDerived() throws Exception { + // should allow deleting derived resource + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = (List) resource.get("derived"); + assertEquals(derived.size(), 1); + String derived_resource_id = (String) derived.get(0).get("id"); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = (List) resource.get("derived"); + assertEquals(derived.size(), 0); + } + + @Test(expected = NotFound.class) + public void test09DeleteResources() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByPrefix() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByTags() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); + } + + @Test + public void test10Tags() throws Exception { + // should allow listing tags + Map result = api.tags(ObjectUtils.emptyMap()); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + } + + @Test + public void test11TagsPrefix() throws Exception { + // should allow listing tag by prefix + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); + tags = (List) result.get("tags"); + assertEquals(0, tags.size()); + } + + @Test + public void test12Transformations() throws Exception { + // should allow listing transformations + Map result = api.transformations(ObjectUtils.emptyMap()); + Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + + assertNotNull(transformation); + assertTrue((Boolean) transformation.get("used")); + } + + @Test + public void test13TransformationMetadata() throws Exception { + // should allow getting transformation metadata + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); + } + + @Test + public void test14TransformationUpdate() throws Exception { + // should allow updating transformation allowed_for_strict + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), false); + } + + @Test + public void test15TransformationCreate() throws Exception { + // should allow creating named transformation + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test15aTransformationUnsafeUpdate() throws Exception { + // should allow unsafe update of named transformation + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), + ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test16aTransformationDelete() throws Exception { + // should allow deleting named transformation + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test16bTransformationDelete() throws Exception { + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test + public void test17aTransformationDeleteImplicit() throws Exception { + // should allow deleting implicit transformation + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + /** + * @throws Exception + * @expectedException \Cloudinary\Api\NotFound + */ + @Test(expected = NotFound.class) + public void test17bTransformationDeleteImplicit() throws Exception { + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + @Test + public void test18Usage() throws Exception { + // should support usage API call + Map result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + @Test + public void test19Ping() throws Exception { + // should support ping API call + Map result = api.ping(ObjectUtils.emptyMap()); + assertEquals(result.get("status"), "ok"); + } + + // This test must be last because it deletes (potentially) all dependent + // transformations which some tests rely on. + // Add @Test if you really want to test it - This test deletes derived + // resources! + public void testDeleteAllResources() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload("src/test/resources/logo.png", + ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); + // assertEquals(0, ((org.cloudinary.json.JSONArray) + // result.get("derived")).size()); + } + + @Test + public void testManualModeration() throws Exception { + // should support setting manual moderation status + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); + } + @Test public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("ocr", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - + @Test public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("raw_convert", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - + @Test public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("categorization", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - + @Test public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("detection", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - + @Test public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload( - "src/test/resources/logo.png", ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), - ObjectUtils.asMap("similarity_search", "illegal")); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); assertTrue(e.getMessage().matches("^Illegal value(.*)")); } } - + @Test public void testUpdateCustomCoordinates() throws IOException, Exception { - //should update custom coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - long[] expected = new long[]{121L,31L,110L,151L}; - Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } - } - + // should update custom coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + int[] expected = new int[] { 121, 31, 110, 151}; + ArrayList actual = (ArrayList) ((ArrayList)((Map) result.get("coordinates")).get("custom")).get(0); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual.get(i)); + } + } + @Test public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response header + // should support reporting the current API limits found in the response + // header ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); assertNotNull(result1.apiRateLimit()); @@ -502,24 +504,18 @@ public void testApiLimits() throws Exception { assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); } - + @Test public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset3", "folder", "folder3")); - org.json.simple.JSONArray presets = (org.json.simple.JSONArray) api - .uploadPresets(ObjectUtils.emptyMap()).get("presets"); - assertEquals(((Map) presets.get(0)).get("name"), - "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), - "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), - "api_test_upload_preset"); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); + + ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); + assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); + assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); + assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); @@ -532,21 +528,18 @@ public void testGetUploadPreset() throws Exception { Map context = ObjectUtils.asMap("a", "b", "c", "d"); Transformation transformation = new Transformation(); transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, - "folder", "folder", "transformation", transformation, "tags", - tags, "context", context)); + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", + context)); 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"); - Map outTransformation = (Map) ((org.json.simple.JSONArray) settings - .get("transformation")).get(0); - assertEquals(outTransformation.get("width"), 100L); + Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); + assertEquals(outTransformation.get("width"), 100); assertEquals(outTransformation.get("crop"), "scale"); - Object[] outTags = ((org.json.simple.JSONArray) settings.get("tags")) - .toArray(); + Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); assertArrayEquals(tags, outTags); Map outContext = (Map) settings.get("context"); assertEquals(context, outContext); @@ -555,8 +548,7 @@ public void testGetUploadPreset() throws Exception { @Test public void testDeleteUploadPreset() throws Exception { // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", - "api_test_upload_preset4", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); boolean error = false; @@ -571,13 +563,10 @@ public void testDeleteUploadPreset() throws Exception { @Test public void testUpdateUploadPreset() throws Exception { // should allow updating upload_presets - String name = api - .createUploadPreset(ObjectUtils.asMap("folder", "folder")) - .get("name").toString(); + 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)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); preset = api.uploadPreset(name, ObjectUtils.emptyMap()); @@ -586,49 +575,29 @@ public void testUpdateUploadPreset() throws Exception { assertEquals(settings, preset.get("settings")); api.deleteUploadPreset(name, ObjectUtils.emptyMap()); } - + @Test public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload( - "src/test/resources/logo.png", - ObjectUtils.asMap("moderation", "manual")); - api.update((String) result1.get("public_id"), - ObjectUtils.asMap("moderation_status", "approved")); - api.update((String) result2.get("public_id"), - ObjectUtils.asMap("moderation_status", "rejected")); - Map approved = api.resourcesByModeration("manual", "approved", - ObjectUtils.asMap("max_results", 1000)); - Map rejected = api.resourcesByModeration("manual", "rejected", - ObjectUtils.asMap("max_results", 1000)); - Map pending = api.resourcesByModeration("manual", "pending", - ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), - "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), - "public_id", (String) result2.get("public_id"))); - } - + Map result1 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); + Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); + Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); + Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); + assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); + } + // For this test to work, "Auto-create folders" should be enabled in the // Upload Settings. // Uncomment @Test if you really want to test it. @@ -637,18 +606,14 @@ public void testFolderApi() throws Exception { // should allow deleting all resources cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", - ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("name")); + assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", - ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", - ((Map) ((org.json.simple.JSONArray) result.get("folders")).get(1)).get("path")); + assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); try { api.subFolders("test_folder", null); } catch (Exception e) { @@ -656,8 +621,8 @@ public void testFolderApi() throws Exception { } api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); } - + private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } + assertTrue(list.contains(object)); + } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 998d5ff7..2333995b 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -6,6 +6,7 @@ import static org.junit.Assume.assumeNotNull; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -25,7 +26,7 @@ public class UploaderTest { private Cloudinary cloudinary; - + @BeforeClass public static void setUpClass() { Cloudinary cloudinary = new Cloudinary(); @@ -43,8 +44,8 @@ public void setUp() { @Test public void testUpload() throws IOException { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); - assertEquals(result.get("width"), 241L); - assertEquals(result.get("height"), 51L); + assertEquals(result.get("width"), 241); + assertEquals(result.get("height"), 51); assertNotNull(result.get("colors")); assertNotNull(result.get("predominant")); Map to_sign = new HashMap(); @@ -53,12 +54,12 @@ public void testUpload() throws IOException { String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } - + @Test public void testUploadUrl() throws IOException { Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 241L); - assertEquals(result.get("height"), 51L); + assertEquals(result.get("width"), 241); + assertEquals(result.get("height"), 51); Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); @@ -69,8 +70,8 @@ public void testUploadUrl() throws IOException { @Test public void testUploadDataUri() throws IOException { Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 16L); - assertEquals(result.get("height"), 16L); + assertEquals(result.get("width"), 16); + assertEquals(result.get("height"), 16); Map to_sign = new HashMap(); to_sign.put("public_id", (String) result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); @@ -100,17 +101,17 @@ public void testRename() throws Exception { @Test public void testUniqueFilename() throws Exception { Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); - assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); + assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals((String) result.get("public_id"), "logo"); + assertEquals((String) result.get("public_id"), "logo"); } @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); + Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; - assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); + assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); } @Test @@ -127,10 +128,10 @@ public void testHeaders() throws IOException { @Test public void testText() throws IOException { Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); - assertTrue(((Long) result.get("width")) > 1); - assertTrue(((Long) result.get("height")) > 1); + assertTrue(((Integer) result.get("width")) > 1); + assertTrue(((Integer) result.get("height")) > 1); } - + @Test public void testImageUploadTag() { String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); @@ -147,7 +148,7 @@ public void testSprite() throws IOException { cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); - assertEquals(2, ((Map) result.get("image_infos")).size()); + assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); assertTrue(((String) result.get("css_url")).contains("w_100")); result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); @@ -175,31 +176,31 @@ public void testTags() throws Exception { String public_id2 = (String)result2.get("public_id"); cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); - List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); - tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); + tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); } - + @Test public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats - String[] formats = {"png"}; + String[] formats = {"png"}; Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); assertEquals(result.get("format"), "png"); } - + @Test public void testAllowedFormatsWithIllegalFormat() throws Exception { //should prevent non whitelisted formats from being uploaded if allowed_formats is specified boolean errorFound = false; - String[] formats = {"jpg"}; + String[] formats = {"jpg"}; try{ cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); } catch(Exception e) { @@ -207,15 +208,15 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { } assertTrue(errorFound); } - + @Test public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type - String[] formats = {"jpg"}; + String[] formats = {"jpg"}; Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); assertEquals("jpg", result.get("format")); } - + @Test public void testFaceCoordinates() throws Exception { //should allow sending face coordinates @@ -225,62 +226,62 @@ public void testFaceCoordinates() throws Exception { coordinates.addRect(rect1); coordinates.addRect(rect2); Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); - org.json.simple.JSONArray resultFaces = (org.json.simple.JSONArray) result.get("faces"); + ArrayList resultFaces = ((ArrayList) result.get("faces")); assertEquals(2, resultFaces.size()); - - Object[] resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); - - assertEquals((long)rect1.x, resultCoordinates[0]); - assertEquals((long)rect1.y, resultCoordinates[1]); - assertEquals((long)rect1.width, resultCoordinates[2]); - assertEquals((long)rect1.height, resultCoordinates[3]); - - resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(1)).toArray(); - - assertEquals((long)rect2.x, resultCoordinates[0]); - assertEquals((long)rect2.y, resultCoordinates[1]); - assertEquals((long)rect2.width, resultCoordinates[2]); - assertEquals((long)rect2.height, resultCoordinates[3]); - + + Object[] resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect1.x, resultCoordinates[0]); + assertEquals(rect1.y, resultCoordinates[1]); + assertEquals(rect1.width, resultCoordinates[2]); + assertEquals(rect1.height, resultCoordinates[3]); + + resultCoordinates =((ArrayList)resultFaces.get(1)).toArray(); + + assertEquals(rect2.x, resultCoordinates[0]); + assertEquals(rect2.y, resultCoordinates[1]); + assertEquals(rect2.width, resultCoordinates[2]); + assertEquals(rect2.height, resultCoordinates[3]); + Coordinates differentCoordinates = new Coordinates(); Rectangle rect3 = new Rectangle(122,32,111,152); differentCoordinates.addRect(rect3); cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); - - resultFaces = (org.json.simple.JSONArray) info.get("faces"); + + resultFaces = (ArrayList) info.get("faces"); assertEquals(1, resultFaces.size()); - resultCoordinates = ((org.json.simple.JSONArray) resultFaces.get(0)).toArray(); + resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect3.x, resultCoordinates[0]); + assertEquals(rect3.y, resultCoordinates[1]); + assertEquals(rect3.width, resultCoordinates[2]); + assertEquals(rect3.height, resultCoordinates[3]); - assertEquals((long)rect3.x, resultCoordinates[0]); - assertEquals((long)rect3.y, resultCoordinates[1]); - assertEquals((long)rect3.width, resultCoordinates[2]); - assertEquals((long)rect3.height, resultCoordinates[3]); - } - + @Test public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - long[] expected = new long[]{121L,31L,110L,151L}; - Object[] actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + int[] expected = new int[]{121,31,110,151}; + Object[] actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++){ assertEquals(expected[i], actual[i]); } - + coordinates = new Coordinates(new int[]{122,32,110,152}); cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - expected = new long[]{122L,32L,110L,152L}; - actual = ((org.json.simple.JSONArray)((org.json.simple.JSONArray)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + expected = new int[]{122,32,110,152}; + actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++){ assertEquals(expected[i], actual[i]); } } - + @Test public void testContext() throws Exception { //should allow sending context @@ -293,7 +294,7 @@ public void testContext() throws Exception { info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); } - + @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation @@ -301,8 +302,8 @@ public void testModerationRequest() throws Exception { assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); } - - + + @Test public void testRawConvertRequest() { //should support requesting raw conversion @@ -312,7 +313,7 @@ public void testRawConvertRequest() { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } - + @Test public void testCategorizationRequest() { //should support requesting categorization @@ -322,7 +323,7 @@ public void testCategorizationRequest() { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } - + @Test public void testDetectionRequest() { //should support requesting detection @@ -332,7 +333,7 @@ public void testDetectionRequest() { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } - + @Test public void testAutoTaggingRequest() { //should support requesting auto tagging @@ -342,15 +343,15 @@ public void testAutoTaggingRequest() { assertTrue(e.getMessage().matches("^Must use(.*)")); } } - + @Test public void testUploadLargeRawFiles() throws Exception { // support uploading large raw files Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); - assertEquals(new java.io.File("src/test/resources/docx.docx").length(), response.get("bytes")); + assertEquals((int)(new java.io.File("src/test/resources/docx.docx").length()), response.get("bytes")); assertEquals(Boolean.TRUE, response.get("done")); } - + @Test public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets @@ -359,5 +360,5 @@ public void testUnsignedUpload() throws Exception { assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } - + } diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index ac76a27a..c3b3f557 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -15,7 +15,7 @@ com.cloudinary - cloudinary + cloudinary-http42 ${project.version} From 076393eb63bf8a7fa083834e97fd9887098a55d2 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 18 Nov 2014 11:23:02 +0200 Subject: [PATCH 024/592] Remove redundant depndencies --- cloudinary-android-test/pom.xml | 6 ------ cloudinary-android/pom.xml | 6 ------ 2 files changed, 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 8ed9e5e4..589b10f8 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -27,12 +27,6 @@ 2.3.1 provided - - com.cloudinary - cloudinary-core - jar - 1.1.0-SNAPSHOT - com.cloudinary cloudinary-android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index ca922841..6b66b567 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -24,12 +24,6 @@ 4.1.1.4 provided - - com.google.android - android-test - 2.3.1 - provided - com.cloudinary cloudinary-core From b991aadf81ba26dacc60a62f3135adff5c3b57b1 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 18 Nov 2014 11:33:16 +0200 Subject: [PATCH 025/592] [maven-release-plugin] prepare release cloudinary-parent-1.1.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 04ccba6f..2d61bd4b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 com.cloudinary cloudinary-android-test - 1.1.0-SNAPSHOT + 1.1.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.0-SNAPSHOT + 1.1.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 9146163e..7da27d73 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 39d91fda..faea1774 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 217f5c09..1943c516 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 4c3738ca..e0a9d3b1 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 cloudinary-taglib diff --git a/pom.xml b/pom.xml index 2c4de779..ee620bf9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.0-SNAPSHOT + 1.1.0 pom Cloudinary Java Client Library Parent Project From 8b191a4b35d352c681731ff64c28dc2662d5b4f0 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 18 Nov 2014 11:33:24 +0200 Subject: [PATCH 026/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 2d61bd4b..d1bc905c 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.1.0 + 1.1.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.0 + 1.1.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 7da27d73..e15e5599 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index faea1774..3d2cc451 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 1943c516..efe8452e 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index e0a9d3b1..1d8a3314 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index ee620bf9..b8602d2a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.0 + 1.1.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project From 17520ade0895aba494fc128d0e4fbd47c28474b0 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 18 Nov 2014 19:08:08 +0200 Subject: [PATCH 027/592] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 753d4936..e7be4ac7 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For Java, Cloudinary provides a library for simplifying the integration even fur ## Setup ###################################################################### -The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml : +The cloudinary_java library is available in [Maven Central](https://repo1.maven.org/maven2/com/cloudinary/). To use it, add the following dependency to your pom.xml : com.cloudinary @@ -30,7 +30,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o 1.1.0 -Alternatively, download cloudinary_java from [here](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/tarball/master) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.1.0/cloudinary-core-1.1.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http42/1.1.0/cloudinary-http42-1.1.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/blob/master/pom.xml) for library dependencies. ## Try it right away From 842eb7a84152263b9a605a6d27ac1db0a220b045 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 26 Nov 2014 17:14:37 +0200 Subject: [PATCH 028/592] Update README.md --- cloudinary-android/README.md | 109 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 67d33bd4..46e77d34 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -109,4 +109,111 @@ The following example generates the url for accessing an uploaded `sample` image cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") -... +Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: + + cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg") + +You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. + +Embedding a Facebook profile to match your graphic design is very simple: + + cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg") + +Same goes for Twitter: + + cloudinary.url().type("twitter_name").generate("billclinton.jpg") + +### Upload + +Assuming you have your Cloudinary configuration parameters defined (`cloud_name`, `api_key`, `api_secret`), uploading to Cloudinary is very simple. + +The following example uploads a local JPG available as an InputStream to the cloud: + + cloudinary.uploader().upload(inputStream, Cloudinary.emptyMap()) + +The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: + + cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg") + + http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg + +You can also specify your own public ID: + + cloudinary.uploader().upload("http://www.example.com/image.jpg", Cloudinary.asMap("public_id", "sample_remote")) + + cloudinary.url().generate("sample_remote.jpg") + + http://res.cloudinary.com/demo/image/upload/sample_remote.jpg + +### Safe mobile uploading + +Android applications might prefer to avoid keeping the sensitive `api_secret` on the mobile device. It is recommended to generate the upload authentication signature on the server side. +This way the `api_secret` is stored only on the much safer server-side. + +Cloudinary's Android SDK allows providing server-generated signature and any additional parameters that were generated on the server side (instead of signing using `api_secret` locally). + +The following example intializes Cloudinary without any authentication parameters: + + Map config = new HashMap(); + config.put("cloud_name", "n07t21i7"); + Cloudinary mobileCloudinary = new Cloudinary(config); + +Alternatively replace your CLOUDINARY_URL meta-data property as follows: + + + +Your server can use any Cloudinary libraries (Ruby on Rails, PHP, Python & Django, Java, Perl, .Net, etc.) for generating the signature. The following JSON in an example of a response of an upload authorization request to your server: + + { + "signature": "sgjfdoigfjdgfdogidf9g87df98gfdb8f7d6gfdg7gfd8", + "public_id": "abdbasdasda76asd7sa789", + "timestamp": 1346925631, + "api_key": "123456789012345" + } + +The following code uploads an image to Cloudinary with the parameters generated safely on the server side (e.g., from a JSON as in the example above): + + cloudinary.uploader().upload(inputStream, Cloudinary.asMap("public_id", publicId, "signature", signature, "timestamp", timestamp, "api_key", api_key)) + +You might want to reference uploaded Cloudinary images and raw files using an identifier string of the following format: + + resource_type:type:identifier.format + +The following example generates a Cloudinary URL based on an idenfier of the format mentioned above: + + String imageIdentifier = "image:upload:dfhjghjkdisudgfds7iyf.jpg"; + String[] components = imageIdentifier.split(":"); + + String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); + + // http://res.cloudinary.com/n07t21i7/image/upload/dfhjghjkdisudgfds7iyf.jpg + +Same can work for raw file uploads: + + String rawIdentifier = "raw:upload:cguysfdsfuydsfyuds31.doc"; + String[] components = rawIdentifier.split(":"); + + String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); + + // http://res.cloudinary.com/n07t21i7/raw/upload/cguysfdsfuydsfyuds31.doc + +## Additional resources ########################################################## + +Additional resources are available at: + +* [Website](http://cloudinary.com) +* [Documentation](http://cloudinary.com/documentation) +* [Image transformations documentation](http://cloudinary.com/documentation/image_transformations) +* [Upload API documentation](http://cloudinary.com/documentation/upload_images) + +## Support + +You can [open an issue through GitHub](https://github.com/cloudinary/cloudinary_android/issues). + +Contact us at [support@cloudinary.com](mailto:support@cloudinary.com) + +Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary) + +## License ####################################################################### + +Released under the MIT license. From 324bd5806572ca535f017052d48222b42cc6b998 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 16 Dec 2014 17:54:59 +0200 Subject: [PATCH 029/592] updated Url.generate method (b4 tests) --- .../java/com/cloudinary/Configuration.java | 26 +- .../src/main/java/com/cloudinary/Url.java | 239 +++++++++++++----- .../src/main/java/com/cloudinary/Util.java | 4 +- .../cloudinary/strategies/StrategyLoader.java | 12 - .../java/org/cloudinary/json/JSONArray.java | 1 + .../java/com/cloudinary/test/ApiTest.java | 7 + .../com/cloudinary/test/CloudinaryTest.java | 10 +- .../com/cloudinary/test/UploaderTest.java | 5 + .../taglib/CloudinaryJsConfigTag.java | 9 +- .../taglib/CloudinaryJsIncludeTag.java | 2 +- .../taglib/CloudinaryUnsignedUploadTag.java | 1 + .../taglib/CloudinaryUploadTag.java | 4 +- 12 files changed, 227 insertions(+), 93 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index db9dd325..79065626 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -3,8 +3,8 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; -import java.util.Map; import java.util.HashMap; +import java.util.Map; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -34,10 +34,11 @@ public class Configuration { public String proxyHost; public int proxyPort; public Map properties = new HashMap(); + public Boolean secureCdnSubdomain; public Configuration(){ } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort) { + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort,Boolean secureCdnSubdomain) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -51,6 +52,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.callback = callback; this.proxyHost = proxyHost; this.proxyPort = proxyPort; + this.secureCdnSubdomain = secureCdnSubdomain; } @@ -74,6 +76,7 @@ public void update(Map config) { this.callback = (String) config.get("callback"); this.proxyHost = (String) config.get("proxy_host"); this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); + this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); } @@ -90,6 +93,9 @@ public Configuration(Configuration other) { this.cdnSubdomain = other.cdnSubdomain; this.shorten = other.shorten; this.callback = other.callback; + this.proxyHost = other.proxyHost; + this.proxyPort = other.proxyPort; + this.secureCdnSubdomain = other.secureCdnSubdomain; } @@ -176,11 +182,13 @@ public static class Builder { private String callback; private String proxyHost; private int proxyPort; + private Boolean secureCdnSubdomain; + /** * Creates a {@link Configuration} with the arguments supplied to this builder */ - public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort); } + public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain); } /** * The unique name of your cloud at Cloudinary @@ -243,6 +251,12 @@ public Builder setPrivateCdn(boolean privateCdn) { return this; } + public Builder setSecureCdnSubdomain(Boolean secureCdnSubdomain) { + this.secureCdnSubdomain = secureCdnSubdomain; + return this; + } + + /** * Whether to automatically build URLs with multiple CDN sub-domains. */ @@ -265,7 +279,7 @@ public Builder setUploadPrefix(String uploadPrefix) { this.uploadPrefix = uploadPrefix; return this; } - + /** * Initialize builder from existing {@link Configuration} * @param other @@ -283,7 +297,9 @@ public Builder from(Configuration other) { this.cdnSubdomain = other.cdnSubdomain; this.shorten = other.shorten; this.callback = other.callback; - + this.proxyHost = other.proxyHost; + this.proxyPort = other.proxyPort; + this.secureCdnSubdomain = other.secureCdnSubdomain; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 1a6429d4..331dcce8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -17,18 +17,19 @@ public class Url { private final Configuration config; - boolean shorten; String publicId = null; - String type = "upload"; + String type = null; String resourceType = "image"; String format = null; String version = null; Transformation transformation = null; boolean signUrl; String source = null; + private String urlSuffix; + private boolean useRootPath; + private String sourceToSign; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - public Url(Cloudinary cloudinary) { this.config = new Configuration(cloudinary.config); } @@ -110,6 +111,21 @@ public Url secureDistribution(String secureDistribution) { return this; } + public Url secureCdnSubdomain(boolean secureCdnSubdomain) { + this.config.secureCdnSubdomain = secureCdnSubdomain; + return this; + } + + public Url urlSuffix(String urlSuffix) { + this.urlSuffix = urlSuffix; + return this; + } + + public Url useRootPath(boolean useRootPath) { + this.useRootPath = useRootPath; + return this; + } + public Url cname(String cname) { this.config.cname = cname; return this; @@ -161,85 +177,193 @@ public String generate() { } public String generate(String source) { - if (type.equals("fetch") && !StringUtils.isEmpty(format)) { - transformation().fetchFormat(format); - this.format = null; - } - String transformationStr = transformation().generate(); + if (StringUtils.isEmpty(this.config.cloudName)) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); } + if (!this.config.privateCdn) { + if (StringUtils.isNotBlank(urlSuffix)) { + throw new RuntimeException("URL Suffix only supported in private CDN"); + } + if (useRootPath) { + throw new RuntimeException("Root path only supported in private CDN"); + } + } + + + if (source == null) { if (publicId == null) { return null; } source = publicId; } - String original_source = source; - + + + if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { - if ("upload".equals(type) || "asset".equals(type)) { - return original_source; + if (StringUtils.isEmpty(type) || "asset".equals(type) ) { + return source; + } + } + + if (type!=null && type.equals("fetch") && !StringUtils.isEmpty(format)) { + transformation().fetchFormat(format); + this.format = null; + } + String transformationStr = transformation().generate(); + String signature = ""; + + + String[] finalizedSource = finalizeSource(source,format,urlSuffix); + source = finalizedSource[0]; + sourceToSign = finalizedSource[1]; + + + if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { + version = "1"; + } + + if (version == null) + version = ""; + else + version = "v" + version; + + if (signUrl) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); } + + String toSign = StringUtils.join(new String[] { transformationStr, sourceToSign }, "/"); + toSign = toSign.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); + + + + byte[] digest = md.digest((toSign + this.config.apiSecret).getBytes()); + signature = Base64Coder.encodeURLSafeString(digest); + signature = "s--" + signature.substring(0, 8) + "--/" ; + } + + String finalResourceType = finalizeResourceType(resourceType,type,urlSuffix,useRootPath,config.shorten); + String prefix = unsignedDownloadUrlPrefix(source,config.cloudName,config.privateCdn,config.cdnSubdomain,config.secureCdnSubdomain,config.cname,config.secure,config.secureDistribution); + + return StringUtils.join(new String[] { prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + } + + private String[] finalizeSource(String source, String format, String urlSuffix) { + String[] result = new String[2]; + source = source.replaceAll("([^:])//", "\1/"); + + if (source.toLowerCase().matches("^https?:/")) { source = SmartUrlEncoder.encode(source); + sourceToSign = source; } else { try { source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } - if (format != null) + sourceToSign = source; + if (StringUtils.isNotBlank(urlSuffix)) { + if (urlSuffix.matches("([\\./]))")) { + throw new RuntimeException("url_suffix should not include . or /"); + } + source = source + "/" + urlSuffix; + } + if (StringUtils.isNotBlank(format)) { source = source + "." + format; - } - String prefix; - boolean sharedDomain = !config.privateCdn; - if (config.secure) { - if (StringUtils.isEmpty(config.secureDistribution) || Cloudinary.OLD_AKAMAI_SHARED_CDN.equals(config.secureDistribution)) { - config.secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + sourceToSign = sourceToSign + "." + format; } - sharedDomain = sharedDomain || Cloudinary.SHARED_CDN.equals(config.secureDistribution); - prefix = "https://" + config.secureDistribution; - } else { - CRC32 crc32 = new CRC32(); - crc32.update(source.getBytes()); - String subdomain = config.cdnSubdomain ? "a" + ((crc32.getValue() % 5 + 5) % 5 + 1) + "." : ""; - String host = config.cname != null ? config.cname : (config.privateCdn ? config.cloudName + "-" : "") + "res.cloudinary.com"; - prefix = "http://" + subdomain + host; } - if (sharedDomain) - prefix = prefix + "/" + config.cloudName; + result[0] = source; + result[1] = sourceToSign; + return result; + } - if (config.shorten && resourceType.equals("image") && type.equals("upload")) { + public String finalizeResourceType(String resourceType, String type, String urlSuffix, boolean useRootPath, boolean shorten) { + if (type == null) { + type = "upload"; + } + if (!StringUtils.isBlank(urlSuffix)) { + if (resourceType.equals("image") && type.equals("upload")) { + resourceType = "images"; + type = null; + } else if (resourceType.equals("raw") && type.equals("upload")) { + resourceType = "files"; + type = null; + } else { + throw new RuntimeException("URL Suffix only supported for image/upload and raw/upload"); + } + } + if (useRootPath) { + if ((resourceType.equals("image") && type.equals("upload")) || (resourceType.equals("images") && StringUtils.isBlank(type))) { + resourceType = null; + type = null; + } else { + throw new RuntimeException("Root path only supported for image/upload"); + } + } + if (shorten && resourceType.equals("image") && type.equals("upload")) { resourceType = "iu"; - type = ""; + type = null; + } + String result = resourceType; + if (type!=null){ + result+="/"+type; } + return result; + } - if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { - version = "1"; + public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean privateCdn, boolean cdnSubdomain, Boolean secureCdnSubdomain, String cname, boolean secure, String secureDistribution) { + if (this.config.cloudName.startsWith("/")) { + return "/res" + this.config.cloudName; } + boolean sharedDomain = !this.config.privateCdn; - if (version == null) - version = ""; - else - version = "v" + version; + String prefix; - String rest = StringUtils.join(new String[] { transformationStr, version, source }, "/"); - rest = rest.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); + if (this.config.secure) { + if (StringUtils.isEmpty(this.config.secureDistribution) || this.config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { + secureDistribution = this.config.privateCdn ? this.config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + } + if (!sharedDomain) { + sharedDomain = (secureDistribution == Cloudinary.SHARED_CDN); + } - if (signUrl) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); + if (secureCdnSubdomain == null && sharedDomain) { + secureCdnSubdomain = this.config.cdnSubdomain; + } + + if (secureCdnSubdomain!=null && secureCdnSubdomain==true) { + secureDistribution = this.config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); } - byte[] digest = md.digest((rest + this.config.apiSecret).getBytes()); - String signature = Base64Coder.encodeURLSafeString(digest); - rest = "s--" + signature.substring(0, 8) + "--/" + rest; + + prefix = "https://" + secureDistribution; + } else if (StringUtils.isNotBlank(this.config.cname)) { + String subdomain = this.config.cdnSubdomain ? "a" + shard(source) + "." : ""; + prefix = "http://" + subdomain + this.config.cname; + } else { + String protocol = "http://"; + cloudName = this.config.privateCdn ? this.config.cloudName + "-" : ""; + String res = "res"; + String subdomain = this.config.cdnSubdomain ? "-" + shard(source) : ""; + String domain = ".cloudinary.com"; + prefix = StringUtils.join(new String[] { protocol, cloudName, res, subdomain, domain }, ""); + } + if (sharedDomain) { + prefix += "/" + this.config.cloudName; } + return prefix; + } - return StringUtils.join(new String[] { prefix, resourceType, type, rest }, "/").replaceAll("([^:])\\/+", "$1/"); + private String shard(String input) { + CRC32 crc32 = new CRC32(); + crc32.update(input.getBytes()); + return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); } @SuppressWarnings("unchecked") @@ -286,23 +410,6 @@ public String imageTag(String source, Map attributes) { return builder.toString(); } -// public String imageTag(String source, Map attributes) { -// String url = generate(source); -// attributes = new TreeMap(attributes); // Make sure they -// // are ordered. -// if (transformation().getHtmlHeight() != null) -// attributes.put("height", transformation().getHtmlHeight()); -// if (transformation().getHtmlWidth() != null) -// attributes.put("width", transformation().getHtmlWidth()); -// StringBuilder builder = new StringBuilder(); -// builder.append(" attr : attributes.entrySet()) { -// builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); -// } -// builder.append("/>"); -// return builder.toString(); -// } - public String generateSpriteCss(String source) { this.type = "sprite"; if (!source.endsWith(".css")) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index ad526711..02b97aa0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -42,6 +42,9 @@ public static final Map buildUploadParams(Map options) { params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); params.put("upload_preset", options.get("upload_preset")); + if (options.get("tags") != null) { + params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); + } processWriteParameters(options, params); return params; @@ -125,5 +128,4 @@ public static void clearEmpty(Map params) { } } - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java index ff2ad907..8194e389 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java @@ -1,18 +1,6 @@ package com.cloudinary.strategies; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Enumeration; import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import com.cloudinary.utils.StringUtils; public class StrategyLoader { diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java index f8a3367b..504e4117 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java @@ -961,6 +961,7 @@ Writer write(Writer writer, int indentFactor, int indent) throws JSONException { } } + @SuppressWarnings("unchecked") public ArrayList toList(Class type) { ArrayList listdata = new ArrayList(); for (int i = 0; i < this.length(); i++) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 768b8211..c3cd16ce 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -20,7 +20,9 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import com.cloudinary.Api; import com.cloudinary.Cloudinary; @@ -85,11 +87,16 @@ public static void setUpClass() throws IOException { cloudinary.uploader().upload("src/test/resources/logo.png", options); } + @Rule public TestName currentTest = new TestName(); + @Before public void setUp() { + System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); this.cloudinary = new Cloudinary(); assumeNotNull(cloudinary.config.apiSecret); this.api = cloudinary.api(); + + } public Map findByAttr(List elements, String attr, Object value) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index c09edf38..5de31d6f 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -11,7 +11,9 @@ import java.util.Map; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import com.cloudinary.Cloudinary; import com.cloudinary.Transformation; @@ -21,8 +23,11 @@ public class CloudinaryTest { private Cloudinary cloudinary; + @Rule public TestName currentTest = new TestName(); + @Before public void setUp() { + System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); } @@ -168,7 +173,6 @@ public void testType() { assertEquals("http://res.cloudinary.com/test123/image/facebook/test", result); } - @SuppressWarnings("deprecation") @Test public void testResourceType() { // should use resource_type from options @@ -440,12 +444,12 @@ public void testEscapePublicId() { @Test public void testSignedUrl() { // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"; + String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) .generate("image.jpg"); assertEquals(expected, actual); - expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"; + expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); assertEquals(expected, actual); diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 2333995b..d13284a1 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -14,7 +14,9 @@ import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; @@ -35,8 +37,11 @@ public static void setUpClass() { } } + @Rule public TestName currentTest = new TestName(); + @Before public void setUp() { + System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); this.cloudinary = new Cloudinary(); assumeNotNull(cloudinary.config.apiSecret); } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java index ae4b319a..e0b3652e 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java @@ -1,15 +1,16 @@ package com.cloudinary.taglib; -import com.cloudinary.Cloudinary; -import com.cloudinary.Singleton; +import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.SimpleTagSupport; -import java.io.IOException; +import com.cloudinary.Cloudinary; +import com.cloudinary.Singleton; public class CloudinaryJsConfigTag extends SimpleTagSupport { + @SuppressWarnings("unused") public void doTag() throws JspException, IOException { Cloudinary cloudinary = Singleton.getCloudinary(); if (cloudinary == null) { @@ -18,7 +19,7 @@ public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); out.println(""); } } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUnsignedUploadTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUnsignedUploadTag.java index ddd93e5f..4883f1d0 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUnsignedUploadTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUnsignedUploadTag.java @@ -10,6 +10,7 @@ public CloudinaryUnsignedUploadTag() { this.unsigned = true; } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected String uploadTag(Uploader uploader, Map options, Map htmlOptions) { return uploader.unsignedImageUploadTag(fieldName, uploadPreset, options, htmlOptions); } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java index 51cc526e..9157c9db 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java @@ -421,11 +421,13 @@ public void setUploadPreset(String uploadPreset) { this.uploadPreset = uploadPreset; } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected String uploadTag(Uploader uploader, Map options, Map htmlOptions) { return uploader.imageUploadTag(fieldName, options, htmlOptions); } - private void buildCallbackUrl(Map options) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void buildCallbackUrl(Map options) { String callback = (String) options.get("callback"); if (callback == null || callback.isEmpty()) callback = Singleton.getCloudinary().config.callback; if (callback == null || callback.isEmpty()) callback = "/cloudinary_cors.html"; From fa562f4cb1e6569cc6e68e96d3e75f0df9500836 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 17 Dec 2014 12:25:36 +0200 Subject: [PATCH 030/592] added tests --- .../src/main/java/com/cloudinary/Url.java | 21 +-- .../com/cloudinary/test/CloudinaryTest.java | 133 +++++++++++++++--- 2 files changed, 126 insertions(+), 28 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 331dcce8..fd0b20dd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -208,6 +208,15 @@ public String generate(String source) { } } + if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { + version = "1"; + } + + if (version == null) + version = ""; + else + version = "v" + version; + if (type!=null && type.equals("fetch") && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); this.format = null; @@ -221,14 +230,6 @@ public String generate(String source) { sourceToSign = finalizedSource[1]; - if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { - version = "1"; - } - - if (version == null) - version = ""; - else - version = "v" + version; if (signUrl) { MessageDigest md = null; @@ -258,7 +259,7 @@ private String[] finalizeSource(String source, String format, String urlSuffix) String[] result = new String[2]; source = source.replaceAll("([^:])//", "\1/"); - if (source.toLowerCase().matches("^https?:/")) { + if (source.toLowerCase().matches("^https?:/.*")) { source = SmartUrlEncoder.encode(source); sourceToSign = source; } else { @@ -269,7 +270,7 @@ private String[] finalizeSource(String source, String format, String urlSuffix) } sourceToSign = source; if (StringUtils.isNotBlank(urlSuffix)) { - if (urlSuffix.matches("([\\./]))")) { + if (urlSuffix.matches("\\w*[\\./]\\w*")) { throw new RuntimeException("url_suffix should not include . or /"); } source = source + "/" + urlSuffix; diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index 5de31d6f..91f57da6 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -9,6 +9,8 @@ import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.junit.Before; import org.junit.Rule; @@ -23,14 +25,15 @@ public class CloudinaryTest { private Cloudinary cloudinary; - @Rule public TestName currentTest = new TestName(); + @Rule + public TestName currentTest = new TestName(); @Before public void setUp() { - System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); } - + @Test public void testCloudName() { // should use cloud_name from config @@ -71,8 +74,8 @@ 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; + cloudinary.config.secure = true; + cloudinary.config.privateCdn = true; String result = cloudinary.url().generate("test"); assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); } @@ -81,8 +84,8 @@ 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.secure = true; + cloudinary.config.privateCdn = true; cloudinary.config.secureDistribution = "something.cloudfront.net"; String result = cloudinary.url().generate("test"); assertEquals("https://something.cloudfront.net/image/upload/test", result); @@ -91,7 +94,7 @@ public void testSecureNonAkamai() { @Test public void testHttpPrivateCdn() { // should not add cloud_name if private_cdn and not secure - cloudinary.config.privateCdn =true; + cloudinary.config.privateCdn = true; String result = cloudinary.url().generate("test"); assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); } @@ -151,8 +154,7 @@ public void testBaseTransformations() { @Test public void testBaseTransformationArray() { // should support array of base transformations - Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop") - .width(100); + Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals("100", transformation.getHtmlWidth().toString()); assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); @@ -339,7 +341,7 @@ public void testFlags() { result = cloudinary.url().transformation(transformation).generate("test"); assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); } - + @Test public void testOpacity() { // should support opacity @@ -353,9 +355,7 @@ public void testOpacity() { public void testImageTag() { Transformation transformation = new Transformation().width(100).height(101).crop("crop"); String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.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", ObjectUtils.asMap("alt", "my image")); assertEquals( @@ -457,22 +457,119 @@ public void testSignedUrl() { actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); assertEquals(expected, actual); } - + @Test public void testResponsiveWidth() { // should support responsive width Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); String result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); Transformation.setResponsiveWidthTransformation(null); } - + + @Test(expected = RuntimeException.class) + public void testDisallowUrlSuffixInSharedDistribution() { + cloudinary.url().urlSuffix("hello").generate("test"); + } + + @Test(expected = RuntimeException.class) + public void testDisallowUrlSuffixInNonUploadTypes() { + cloudinary.url().urlSuffix("hello").privateCdn(true).type("facebook").generate("test"); + + } + + @Test(expected = RuntimeException.class) + public void testDisallowUrlSuffixWithSlash() { + cloudinary.url().urlSuffix("hello/world").privateCdn(true).generate("test"); + } + + @Test(expected = RuntimeException.class) + public void testDisallowUrlSuffixWithDot() { + cloudinary.url().urlSuffix("hello.world").privateCdn(true).generate("test"); + } + + @Test + public void testSupportUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).generate("test"); + assertEquals("http://test123-res.cloudinary.com/images/test/hello", actual); + + actual = cloudinary.url().urlSuffix("hello").privateCdn(true).transformation(new Transformation().angle(0)).generate("test"); + assertEquals("http://test123-res.cloudinary.com/images/a_0/test/hello", actual); + + } + + @Test + public void testPutFormatAfterUrlSuffix() { + String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).format("jpg").generate("test"); + assertEquals("http://test123-res.cloudinary.com/images/test/hello.jpg", actual); + } + + @Test + public void testNotSignTheUrlSuffix() { + + Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); + String url = cloudinary.url().format("jpg").signed(true).generate("test"); + Matcher matcher = pattern.matcher(url); + matcher.find(); + String expectedSignature = url.substring(matcher.start(), matcher.end()); + + String actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).urlSuffix("hello").generate("test"); + assertEquals("http://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); + matcher.find(); + expectedSignature = url.substring(matcher.start(), matcher.end()); + + actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).urlSuffix("hello").transformation(new Transformation().angle(0)).generate("test"); + + assertEquals("http://test123-res.cloudinary.com/images/" + expectedSignature + "/a_0/test/hello.jpg", actual); + } + + @Test + public void testSupportUrlSuffixForRawUploads() { + String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).resourceType("raw").generate("test"); + assertEquals("http://test123-res.cloudinary.com/files/test/hello", actual); + } + + @Test(expected = RuntimeException.class) + public void testDisllowUseRootPathInSharedDistribution() { + cloudinary.url().useRootPath(true).generate("test"); + } + + @Test + public void testSupportUseRootPathForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); + assertEquals("http://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); + } + + @Test + public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { + + String actual = cloudinary.url().privateCdn(true).urlSuffix("hello").useRootPath(true).generate("test"); + assertEquals("http://test123-res.cloudinary.com/test/hello", actual); + + } + + @Test(expected = RuntimeException.class) + public void testDisllowUseRootPathIfNotImageUploadForFacebook() { + cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); + } + + @Test(expected = RuntimeException.class) + public void testDisllowUseRootPathIfNotImageUploadForRaw() { + cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); + } + public void testUtils() { assertEquals(ObjectUtils.asBoolean(true, null), true); assertEquals(ObjectUtils.asBoolean(false, null), false); From e8fbd88543bc0a0ac1c57a398e9cb186be8a4e83 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 17 Dec 2014 12:32:11 +0200 Subject: [PATCH 031/592] added invalidate to bulk deletes --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 892456dd..b100681e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -117,7 +117,7 @@ public ApiResponse deleteResources(Iterable publicIds, Map options) thro 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 = ObjectUtils.only(options, "keep_original", "next_cursor"); + Map params = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); params.put("public_ids", publicIds); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } @@ -126,7 +126,7 @@ public ApiResponse deleteResourcesByPrefix(String prefix, Map options) throws Ex 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 = ObjectUtils.only(options, "keep_original", "next_cursor"); + Map params = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); params.put("prefix", prefix); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } @@ -134,14 +134,14 @@ public ApiResponse deleteResourcesByPrefix(String prefix, Map options) throws Ex public ApiResponse deleteResourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "keep_original", "next_cursor"), options); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"), options); } public ApiResponse deleteAllResources(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 filtered = ObjectUtils.only(options, "keep_original", "next_cursor"); + Map filtered = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); filtered.put("all", true); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), filtered, options); } From 0dab9d55b7938cfdd29408b5e2455c26a941c048 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 17 Dec 2014 14:07:07 +0200 Subject: [PATCH 032/592] renamed urlSuffix to suffix --- .../src/main/java/com/cloudinary/Url.java | 2 +- .../com/cloudinary/test/CloudinaryTest.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index fd0b20dd..8cd099ef 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -116,7 +116,7 @@ public Url secureCdnSubdomain(boolean secureCdnSubdomain) { return this; } - public Url urlSuffix(String urlSuffix) { + public Url suffix(String urlSuffix) { this.urlSuffix = urlSuffix; return this; } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index 91f57da6..f9d59709 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -475,38 +475,38 @@ public void testResponsiveWidth() { @Test(expected = RuntimeException.class) public void testDisallowUrlSuffixInSharedDistribution() { - cloudinary.url().urlSuffix("hello").generate("test"); + cloudinary.url().suffix("hello").generate("test"); } @Test(expected = RuntimeException.class) public void testDisallowUrlSuffixInNonUploadTypes() { - cloudinary.url().urlSuffix("hello").privateCdn(true).type("facebook").generate("test"); + cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); } @Test(expected = RuntimeException.class) public void testDisallowUrlSuffixWithSlash() { - cloudinary.url().urlSuffix("hello/world").privateCdn(true).generate("test"); + cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); } @Test(expected = RuntimeException.class) public void testDisallowUrlSuffixWithDot() { - cloudinary.url().urlSuffix("hello.world").privateCdn(true).generate("test"); + cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); } @Test public void testSupportUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).generate("test"); + String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); assertEquals("http://test123-res.cloudinary.com/images/test/hello", actual); - actual = cloudinary.url().urlSuffix("hello").privateCdn(true).transformation(new Transformation().angle(0)).generate("test"); + 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); } @Test public void testPutFormatAfterUrlSuffix() { - String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).format("jpg").generate("test"); + String actual = cloudinary.url().suffix("hello").privateCdn(true).format("jpg").generate("test"); assertEquals("http://test123-res.cloudinary.com/images/test/hello.jpg", actual); } @@ -519,7 +519,7 @@ public void testNotSignTheUrlSuffix() { matcher.find(); String expectedSignature = url.substring(matcher.start(), matcher.end()); - String actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).urlSuffix("hello").generate("test"); + 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); url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); @@ -527,14 +527,14 @@ public void testNotSignTheUrlSuffix() { matcher.find(); expectedSignature = url.substring(matcher.start(), matcher.end()); - actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).urlSuffix("hello").transformation(new Transformation().angle(0)).generate("test"); + 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); } @Test public void testSupportUrlSuffixForRawUploads() { - String actual = cloudinary.url().urlSuffix("hello").privateCdn(true).resourceType("raw").generate("test"); + String actual = cloudinary.url().suffix("hello").privateCdn(true).resourceType("raw").generate("test"); assertEquals("http://test123-res.cloudinary.com/files/test/hello", actual); } @@ -555,7 +555,7 @@ public void testSupportUseRootPathForPrivateCdn() { @Test public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).urlSuffix("hello").useRootPath(true).generate("test"); + String actual = cloudinary.url().privateCdn(true).suffix("hello").useRootPath(true).generate("test"); assertEquals("http://test123-res.cloudinary.com/test/hello", actual); } From 7e5c86993a738fdd312b75bae458b5b3471ff78a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 17 Dec 2014 14:51:53 +0200 Subject: [PATCH 033/592] bug fixes --- .../java/com/cloudinary/Configuration.java | 17 ++++++- .../src/main/java/com/cloudinary/Url.java | 50 +++++++++++-------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 79065626..3316f67c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -35,10 +35,11 @@ public class Configuration { public int proxyPort; public Map properties = new HashMap(); public Boolean secureCdnSubdomain; + public boolean useRootPath; public Configuration(){ } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort,Boolean secureCdnSubdomain) { + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort,Boolean secureCdnSubdomain,boolean useRootPath) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -53,6 +54,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.proxyHost = proxyHost; this.proxyPort = proxyPort; this.secureCdnSubdomain = secureCdnSubdomain; + this.useRootPath = useRootPath; } @@ -77,6 +79,7 @@ public void update(Map config) { this.proxyHost = (String) config.get("proxy_host"); this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); + this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); } @@ -96,6 +99,7 @@ public Configuration(Configuration other) { this.proxyHost = other.proxyHost; this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; + this.useRootPath = other.useRootPath; } @@ -183,12 +187,13 @@ public static class Builder { private String proxyHost; private int proxyPort; private Boolean secureCdnSubdomain; + private boolean useRootPath; /** * Creates a {@link Configuration} with the arguments supplied to this builder */ - public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain); } + public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain,useRootPath); } /** * The unique name of your cloud at Cloudinary @@ -279,6 +284,13 @@ public Builder setUploadPrefix(String uploadPrefix) { this.uploadPrefix = uploadPrefix; return this; } + + public Builder setUseRootPath(boolean useRootPath) { + this.useRootPath = useRootPath; + return this; + } + + /** * Initialize builder from existing {@link Configuration} @@ -300,6 +312,7 @@ public Builder from(Configuration other) { this.proxyHost = other.proxyHost; this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; + this.useRootPath = other.useRootPath; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 8cd099ef..28c3ad09 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -26,8 +26,7 @@ public class Url { boolean signUrl; String source = null; private String urlSuffix; - private boolean useRootPath; - private String sourceToSign; + private Boolean useRootPath; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; public Url(Cloudinary cloudinary) { @@ -177,17 +176,22 @@ public String generate() { } public String generate(String source) { - + + boolean useRootPath =this.config.useRootPath; + if (this.useRootPath!=null){ + useRootPath = this.useRootPath; + } + if (StringUtils.isEmpty(this.config.cloudName)) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); } if (!this.config.privateCdn) { if (StringUtils.isNotBlank(urlSuffix)) { - throw new RuntimeException("URL Suffix only supported in private CDN"); + throw new IllegalArgumentException("URL Suffix only supported in private CDN"); } if (useRootPath) { - throw new RuntimeException("Root path only supported in private CDN"); + throw new IllegalArgumentException("Root path only supported in private CDN"); } } @@ -201,21 +205,12 @@ public String generate(String source) { } - if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { if (StringUtils.isEmpty(type) || "asset".equals(type) ) { return source; } } - if (source.contains("/") && !source.matches("v[0-9]+.*") && !source.matches("https?:/.*") && StringUtils.isEmpty(version)) { - version = "1"; - } - - if (version == null) - version = ""; - else - version = "v" + version; if (type!=null && type.equals("fetch") && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); @@ -227,10 +222,18 @@ public String generate(String source) { String[] finalizedSource = finalizeSource(source,format,urlSuffix); source = finalizedSource[0]; - sourceToSign = finalizedSource[1]; - + String sourceToSign = finalizedSource[1]; + if (sourceToSign.contains("/") && !sourceToSign.matches("v[0-9]+.*") && !sourceToSign.matches("https?:/.*") && StringUtils.isEmpty(version)) { + version = "1"; + } + + if (version == null) + version = ""; + else + version = "v" + version; + if (signUrl) { MessageDigest md = null; try { @@ -246,7 +249,7 @@ public String generate(String source) { byte[] digest = md.digest((toSign + this.config.apiSecret).getBytes()); signature = Base64Coder.encodeURLSafeString(digest); - signature = "s--" + signature.substring(0, 8) + "--/" ; + signature = "s--" + signature.substring(0, 8) + "--" ; } String finalResourceType = finalizeResourceType(resourceType,type,urlSuffix,useRootPath,config.shorten); @@ -259,6 +262,7 @@ private String[] finalizeSource(String source, String format, String urlSuffix) String[] result = new String[2]; source = source.replaceAll("([^:])//", "\1/"); + String sourceToSign; if (source.toLowerCase().matches("^https?:/.*")) { source = SmartUrlEncoder.encode(source); sourceToSign = source; @@ -270,8 +274,10 @@ private String[] finalizeSource(String source, String format, String urlSuffix) } sourceToSign = source; if (StringUtils.isNotBlank(urlSuffix)) { - if (urlSuffix.matches("\\w*[\\./]\\w*")) { - throw new RuntimeException("url_suffix should not include . or /"); + Pattern pattern = Pattern.compile("[\\./]"); + Matcher matcher= pattern.matcher(urlSuffix); + if (matcher.find()) { + throw new IllegalArgumentException("url_suffix should not include . or /"); } source = source + "/" + urlSuffix; } @@ -297,7 +303,7 @@ public String finalizeResourceType(String resourceType, String type, String urlS resourceType = "files"; type = null; } else { - throw new RuntimeException("URL Suffix only supported for image/upload and raw/upload"); + throw new IllegalArgumentException("URL Suffix only supported for image/upload and raw/upload"); } } if (useRootPath) { @@ -305,7 +311,7 @@ public String finalizeResourceType(String resourceType, String type, String urlS resourceType = null; type = null; } else { - throw new RuntimeException("Root path only supported for image/upload"); + throw new IllegalArgumentException("Root path only supported for image/upload"); } } if (shorten && resourceType.equals("image") && type.equals("upload")) { @@ -332,7 +338,7 @@ public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean secureDistribution = this.config.privateCdn ? this.config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; } if (!sharedDomain) { - sharedDomain = (secureDistribution == Cloudinary.SHARED_CDN); + sharedDomain = secureDistribution.equals(Cloudinary.SHARED_CDN); } if (secureCdnSubdomain == null && sharedDomain) { From 7040d96510584187d1d7769f07b94f70e61f3747 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Wed, 17 Dec 2014 14:52:15 +0200 Subject: [PATCH 034/592] added new options to url tag --- .../com/cloudinary/taglib/CloudinaryUrl.java | 33 ++++++++++++++++++- .../main/resources/META-INF/cloudinary.tld | 21 ++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java index 19b21982..dcf71785 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java @@ -32,8 +32,11 @@ public class CloudinaryUrl extends SimpleTagSupport implements DynamicAttributes private Boolean secure = null; private Boolean cdnSubdomain = null; private Boolean signed = null; - + private Boolean useRootPath = null; + private Boolean secureCdnSubdomain = null; + private String namedTransformation = null; + private String urlSuffix = null; /** stores the dynamic attributes */ private Map tagAttrs = new HashMap(); @@ -66,6 +69,10 @@ public void doTag() throws JspException, IOException { } if (cdnSubdomain != null) url.cdnSubdomain(cdnSubdomain.booleanValue()); if (signed != null) url.signed(signed.booleanValue()); + if (useRootPath != null) url.useRootPath(useRootPath); + if (urlSuffix != null) url.suffix(urlSuffix); + if (secureCdnSubdomain != null) url.secureCdnSubdomain(secureCdnSubdomain); + out.println(url.generate()); } @@ -162,4 +169,28 @@ private Boolean isSecureRequest() { ServletRequest request = context.getRequest(); return request.getScheme().equals("https"); } + + public Boolean getUseRootPath() { + return useRootPath; + } + + public void setUseRootPath(Boolean useRootPath) { + this.useRootPath = useRootPath; + } + + public Boolean getSecureCdnSubdomain() { + return secureCdnSubdomain; + } + + public void setSecureCdnSubdomain(Boolean secureCdnSubdomain) { + this.secureCdnSubdomain = secureCdnSubdomain; + } + + public String getUrlSuffix() { + return urlSuffix; + } + + public void setUrlSuffix(String urlSuffix) { + this.urlSuffix = urlSuffix; + } } diff --git a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld index 4cecbdce..67232606 100644 --- a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld +++ b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld @@ -1,5 +1,5 @@ - 1.1 @@ -407,6 +407,21 @@ false true + + urlSuffix + false + true + + + secureCdnSubdomain + false + true + + + useRootPath + false + true + true @@ -429,4 +444,4 @@ true - \ No newline at end of file + From cebb62058d18d69dd4ed5fb103e255325b425ae2 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 19 Dec 2014 11:32:35 +0200 Subject: [PATCH 035/592] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7be4ac7..c00f34f4 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ For Java, Cloudinary provides a library for simplifying the integration even fur **Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. -**Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/cloudinary-android +**Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/tree/master/cloudinary-android ## Getting started guide ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](http://cloudinary.com/documentation/java_integration#getting_started_guide)**. From 0abd67782c74e89d380df62e2091dc1122c51b09 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Thu, 18 Dec 2014 21:49:36 +0200 Subject: [PATCH 036/592] add missing tests in adnroid-test. fix signing tests in android-test. be more specific with exception class in http42 Cloudinary tests. --- .../com/cloudinary/test/CloudinaryTest.java | 131 +++++++++++++++++- .../com/cloudinary/test/UploaderTest.java | 18 ++- .../com/cloudinary/test/CloudinaryTest.java | 14 +- 3 files changed, 152 insertions(+), 11 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java index f26bba80..f9cadc77 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java @@ -1,6 +1,8 @@ package com.cloudinary.test; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import android.test.AndroidTestCase; @@ -345,12 +347,12 @@ public void testRecommendedIdentifierFormat() { public void testSignedUrl() { // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--MaRXzoEC--/c_crop,h_20,w_10/v1234/image.jpg"; + String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) .generate("image.jpg"); assertEquals(expected, actual); - expected = "http://res.cloudinary.com/test123/image/upload/s--ZlgFLQcO--/v1234/image.jpg"; + expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); assertEquals(expected, actual); @@ -359,4 +361,129 @@ public void testSignedUrl() { assertEquals(expected, actual); } + public void testDisallowUrlSuffixInSharedDistribution() { + boolean thrown = false; + try { + cloudinary.url().suffix("hello").generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "URL Suffix only supported in private CDN"); + thrown = true; + } + assertTrue(thrown); + } + + public void testDisallowUrlSuffixInNonUploadTypes() { + boolean thrown = false; + try { + cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "URL Suffix only supported for image/upload and raw/upload"); + } + } + + public void testDisallowUrlSuffixWithSlash() { + boolean thrown = false; + try { + cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "url_suffix should not include . or /"); + } + } + + public void testDisallowUrlSuffixWithDot() { + boolean thrown = false; + try { + cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "url_suffix should not include . or /"); + thrown = true; + } + assertTrue(thrown); + } + + public void testSupportUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); + assertEquals("http://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); + } + + 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); + } + + public void testNotSignTheUrlSuffix() { + + Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); + String url = cloudinary.url().format("jpg").signed(true).generate("test"); + Matcher matcher = pattern.matcher(url); + matcher.find(); + 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); + + url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); + matcher = pattern.matcher(url); + matcher.find(); + expectedSignature = url.substring(matcher.start(), matcher.end()); + + 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); + } + + 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); + } + + public void testDisllowUseRootPathInSharedDistribution() { + boolean thrown = false; + try { + cloudinary.url().useRootPath(true).generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "Root path only supported in private CDN"); + thrown = true; + } + assertTrue(thrown); + } + + public void testSupportUseRootPathForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); + assertEquals("http://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); + } + + public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).suffix("hello").useRootPath(true).generate("test"); + assertEquals("http://test123-res.cloudinary.com/test/hello", actual); + } + + public void testDisllowUseRootPathIfNotImageUploadForFacebook() { + boolean thrown = false; + try { + cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "Root path only supported for image/upload"); + thrown = true; + } + assertTrue(thrown); + } + + public void testDisllowUseRootPathIfNotImageUploadForRaw() { + boolean thrown = false; + try { + cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); + } catch (IllegalArgumentException e) { + assertEquals(e.getMessage(), "Root path only supported for image/upload"); + thrown = true; + } + assertTrue(thrown); + } + } diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 5490c8cb..7cf90fe7 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -202,6 +202,8 @@ public void testMulti() throws Exception { } public void testUniqueFilename() throws Exception { + if (cloudinary.config.apiSecret == null) + return; File f = new File(getInstrumentation().getContext().getCacheDir() + "/logo.png"); @@ -223,6 +225,8 @@ public void testUniqueFilename() throws Exception { public void testFaceCoordinates() throws Exception { // should allow sending face coordinates + if (cloudinary.config.apiSecret == null) + return; Coordinates coordinates = new Coordinates(); Rectangle rect1 = new Rectangle(121, 31, 231, 182); Rectangle rect2 = new Rectangle(120, 30, 229, 270); @@ -250,6 +254,8 @@ public void testFaceCoordinates() throws Exception { public void testContext() throws Exception { // should allow sending context + if (cloudinary.config.apiSecret == null) + return; @SuppressWarnings("rawtypes") Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("context", context)); @@ -257,6 +263,8 @@ public void testContext() throws Exception { public void testModerationRequest() throws Exception { // should support requesting manual moderation + if (cloudinary.config.apiSecret == null) + return; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("moderation", "manual"))); assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); @@ -264,6 +272,8 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { // should support requesting raw conversion + if (cloudinary.config.apiSecret == null) + return; try { cloudinary.uploader().upload(getAssetStream("docx.docx"), ObjectUtils.asMap("raw_convert", "illegal", "resource_type", "raw")); } catch (Exception e) { @@ -273,6 +283,8 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { // should support requesting categorization + if (cloudinary.config.apiSecret == null) + return; try { cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { @@ -282,6 +294,8 @@ public void testCategorizationRequest() { public void testDetectionRequest() { // should support requesting detection + if (cloudinary.config.apiSecret == null) + return; try { cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { @@ -291,6 +305,8 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { // should support requesting auto tagging + if (cloudinary.config.apiSecret == null) + return; try { cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("auto_tagging", 0.5f)); @@ -298,8 +314,6 @@ public void testAutoTaggingRequest() { for (int i = 0; i < e.getStackTrace().length; i++) { StackTraceElement x = e.getStackTrace()[i]; } - - assertTrue(e.getMessage().matches("^Must use(.*)")); } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index f9d59709..e9ec7c11 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -473,23 +473,23 @@ public void testResponsiveWidth() { Transformation.setResponsiveWidthTransformation(null); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisallowUrlSuffixInSharedDistribution() { cloudinary.url().suffix("hello").generate("test"); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisallowUrlSuffixInNonUploadTypes() { cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisallowUrlSuffixWithSlash() { cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisallowUrlSuffixWithDot() { cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); } @@ -538,7 +538,7 @@ public void testSupportUrlSuffixForRawUploads() { assertEquals("http://test123-res.cloudinary.com/files/test/hello", actual); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisllowUseRootPathInSharedDistribution() { cloudinary.url().useRootPath(true).generate("test"); } @@ -560,12 +560,12 @@ public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisllowUseRootPathIfNotImageUploadForFacebook() { cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); } - @Test(expected = RuntimeException.class) + @Test(expected = IllegalArgumentException.class) public void testDisllowUseRootPathIfNotImageUploadForRaw() { cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); } From a0d2c1d5d54e0e88ad4118d56d9c29ed154c437a Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 22 Dec 2014 20:16:42 +0200 Subject: [PATCH 037/592] Change log and version update --- CHANGES.txt | 3 ++- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8d034d9b..719089cd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,4 +2,5 @@ 1.0.12 - 2014-03-04 - Fix handling of Booleans in uploader API 1.0.13 - 2014-04-29 - Allow passing ClientConnectionManager. Add support for opacity. Support upload_presets. Support unsigned uploads. Support start_at for resource listing. Support phash for upload and resource details. Support rate limit header in Api calls. 1.0.14 - 2014-07-29 - Add background_removal. Support return_delete_token in upload/update params. Support responsive and hidpi. Support custom coordinates. -1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) \ No newline at end of file +1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) +1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index e96b4458..010e3d19 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -33,7 +33,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.1.0"; + public final static String VERSION = "1.1.1"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; From 1d040a6ac75a7df48450d1d8c43ac9d04105ddca Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 22 Dec 2014 20:15:06 +0200 Subject: [PATCH 038/592] [maven-release-plugin] prepare release cloudinary-parent-1.1.1 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index d1bc905c..27cdfb70 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 com.cloudinary cloudinary-android-test - 1.1.1-SNAPSHOT + 1.1.1 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.1-SNAPSHOT + 1.1.1 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index e15e5599..a1258637 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 3d2cc451..a6c0306c 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index efe8452e..c75faaca 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 1d8a3314..335c75a3 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 cloudinary-taglib diff --git a/pom.xml b/pom.xml index b8602d2a..8214069e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.1-SNAPSHOT + 1.1.1 pom Cloudinary Java Client Library Parent Project From 03b55d35d1324ed34066617c45561b17f9f7d539 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 22 Dec 2014 20:15:11 +0200 Subject: [PATCH 039/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 27cdfb70..f2f84fb8 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT com.cloudinary cloudinary-android-test - 1.1.1 + 1.1.2-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.1 + 1.1.2-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index a1258637..760499b9 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index a6c0306c..a0e32907 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index c75faaca..366e6d9a 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 335c75a3..7e54b7a7 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index 8214069e..32b026da 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.1 + 1.1.2-SNAPSHOT pom Cloudinary Java Client Library Parent Project From a811982d87d2402dcdeab657c7661961aa7427d6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 13 Jan 2015 15:38:19 +0200 Subject: [PATCH 040/592] eager upload params can be both string or List --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 5 ++++- 1 file changed, 4 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 02b97aa0..4217985c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -34,7 +34,10 @@ public static final Map buildUploadParams(Map options) { if (value != null) params.put(attr, value.toString()); } - params.put("eager", buildEager((List) options.get("eager"))); + Object eagerObj = options.get("eager"); + String eager = eagerObj instanceof String ? eagerObj.toString() : buildEager((List) options.get("eager")); + + params.put("eager", eager); params.put("notification_url", (String) options.get("notification_url")); params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("proxy", (String) options.get("proxy")); From 87fc743313ae001b3d474f1d1809d449a16119cc Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Tue, 13 Jan 2015 19:39:28 +0200 Subject: [PATCH 041/592] merged android signature fix --- .../src/main/java/com/cloudinary/Util.java | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 4217985c..82a5a5cd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -18,13 +18,7 @@ public static final Map buildUploadParams(Map options) { if (options == null) options = ObjectUtils.emptyMap(); Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } + params.put("public_id", (String) options.get("public_id")); params.put("callback", (String) options.get("callback")); params.put("format", (String) options.get("format")); @@ -34,10 +28,7 @@ public static final Map buildUploadParams(Map options) { if (value != null) params.put(attr, value.toString()); } - Object eagerObj = options.get("eager"); - String eager = eagerObj instanceof String ? eagerObj.toString() : buildEager((List) options.get("eager")); - params.put("eager", eager); params.put("notification_url", (String) options.get("notification_url")); params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("proxy", (String) options.get("proxy")); @@ -45,11 +36,31 @@ public static final Map buildUploadParams(Map options) { params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); params.put("upload_preset", options.get("upload_preset")); - if (options.get("tags") != null) { - params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); - } - processWriteParameters(options, params); + if (options.get("signature") == null) { + params.put("eager", buildEager((List) options.get("eager"))); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + processWriteParameters(options, params); + } else { + params.put("eager", (String) options.get("eager")); + params.put("transformation", (String) options.get("transformation")); + params.put("headers", (String) options.get("headers")); + params.put("tags", (String) options.get("tags")); + params.put("face_coordinates", (String) options.get("face_coordinates")); + params.put("context", (String) options.get("context")); + params.put("ocr", (String) options.get("ocr")); + params.put("raw_convert", (String) options.get("raw_convert")); + params.put("categorization", (String) options.get("categorization")); + params.put("detection", (String) options.get("detection")); + params.put("similarity_search", (String) options.get("similarity_search")); + params.put("auto_tagging", (String) options.get("auto_tagging")); + } return params; } From 985aafc6f90a2c8b2d0113a6c596dd1b785f71ef Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Jan 2015 12:23:49 +0200 Subject: [PATCH 042/592] Increment to version 1.1.2 - fix support for string eager parameters e.g. for safe mobile flow --- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 010e3d19..05d391f9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -33,7 +33,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.1.1"; + public final static String VERSION = "1.1.2"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; From 36ab327954a789ccd656c1d124124e229cf8df27 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Jan 2015 12:26:16 +0200 Subject: [PATCH 043/592] [maven-release-plugin] prepare release cloudinary-parent-1.1.2 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f2f84fb8..62ab7990 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 com.cloudinary cloudinary-android-test - 1.1.2-SNAPSHOT + 1.1.2 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.2-SNAPSHOT + 1.1.2 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 760499b9..4725b603 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index a0e32907..95dd5fc2 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 366e6d9a..2982c13c 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 7e54b7a7..278513a2 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 cloudinary-taglib diff --git a/pom.xml b/pom.xml index 32b026da..c3a55ed7 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.2-SNAPSHOT + 1.1.2 pom Cloudinary Java Client Library Parent Project From 4c7b23b50a9b9a6b33f1c25fabc7684bea2832fb Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Jan 2015 12:26:21 +0200 Subject: [PATCH 044/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 62ab7990..62870dc5 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT com.cloudinary cloudinary-android-test - 1.1.2 + 1.1.3-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.2 + 1.1.3-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 4725b603..a5299dcd 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 95dd5fc2..86f8b337 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 2982c13c..128b5068 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 278513a2..d71260d7 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index c3a55ed7..1089ea74 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.2 + 1.1.3-SNAPSHOT pom Cloudinary Java Client Library Parent Project From 3bb28f9ede6de80ed9f47cbc76bf2363113c502a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 23 Feb 2015 16:23:53 +0200 Subject: [PATCH 045/592] Added timeout parameter to admin api and Fixed test and configuration issues * Added new timeout parameter to the api configuration * Renamed 'logo.png' to 'old_logo.png' * Created constants for the local and remote image names * (Automatically) Added Library reference in project.properties --- .gitignore | 32 ++++++- cloudinary-android-test/project.properties | 1 + .../assets/images/{logo.png => old_logo.png} | Bin .../com/cloudinary/test/CloudinaryTest.java | 4 +- .../com/cloudinary/test/UploaderTest.java | 45 ++++----- .../java/com/cloudinary/Configuration.java | 27 +++++- .../com/cloudinary/http42/ApiStrategy.java | 20 +++- .../java/com/cloudinary/test/ApiTest.java | 90 +++++++++++------- .../com/cloudinary/test/CloudinaryTest.java | 4 +- .../com/cloudinary/test/UploaderTest.java | 58 +++++------ .../test/resources/{logo.png => old_logo.png} | Bin 11 files changed, 181 insertions(+), 100 deletions(-) rename cloudinary-android-test/src/main/assets/images/{logo.png => old_logo.png} (100%) mode change 100755 => 100644 rename cloudinary-http42/src/test/resources/{logo.png => old_logo.png} (100%) diff --git a/.gitignore b/.gitignore index c930c5ba..40676307 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,32 @@ +## Android default ignore +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files *.class -# Package Files # -*.jar -*.war -*.ear +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +/*/build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log -.* target/ test-output/ .settings @@ -17,3 +38,4 @@ test-output/ appengine-web.xml cloudinary-android-test/src/main/AndroidManifest.xml + diff --git a/cloudinary-android-test/project.properties b/cloudinary-android-test/project.properties index 22d0dca6..c3d5f899 100644 --- a/cloudinary-android-test/project.properties +++ b/cloudinary-android-test/project.properties @@ -12,3 +12,4 @@ # Project target. target=android-7 +android.library.reference.1=../cloudinary-android diff --git a/cloudinary-android-test/src/main/assets/images/logo.png b/cloudinary-android-test/src/main/assets/images/old_logo.png old mode 100755 new mode 100644 similarity index 100% rename from cloudinary-android-test/src/main/assets/images/logo.png rename to cloudinary-android-test/src/main/assets/images/old_logo.png diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java index f9cadc77..62551013 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java @@ -238,8 +238,8 @@ public void testUnderlay() { public void testFetchFormat() { // should support format for fetch urls - String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/logo.png"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/logo.png", result); + 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); } public void testEffect() { diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 7cf90fe7..7e8bc2c7 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -23,8 +23,9 @@ public class UploaderTest extends InstrumentationTestCase { - - private Cloudinary cloudinary; + + public static final String TEST_IMAGE = "images/old_logo.png"; + private Cloudinary cloudinary; private static boolean first = true; public void setUp() throws Exception { @@ -44,7 +45,7 @@ protected InputStream getAssetStream(String filename) throws IOException { public void testUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("colors", true))); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true))); assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); assertNotNull(result.get("colors")); @@ -59,7 +60,7 @@ public void testUpload() throws Exception { public void testUnsignedUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream("images/logo.png"), "sample_preset_dhfjhriu", + JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), "sample_preset_dhfjhriu", ObjectUtils.emptyMap())); assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); @@ -74,7 +75,7 @@ public void testUnsignedUpload() throws Exception { public void testUploadUrl() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap())); + JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/old_logo.png", ObjectUtils.emptyMap())); assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); Map to_sign = new HashMap(); @@ -113,7 +114,7 @@ public void testUploadExternalSignature() throws Exception { params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); Cloudinary emptyCloudinary = new Cloudinary(config); - JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream("images/logo.png"), params)); + JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream(TEST_IMAGE), params)); assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); Map to_sign = new HashMap(); @@ -126,7 +127,7 @@ public void testUploadExternalSignature() throws Exception { public void testRename() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.emptyMap())); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.emptyMap())); cloudinary.uploader().rename(result.getString("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); @@ -154,15 +155,15 @@ public void testExplicit() throws Exception { public void testEager() throws Exception { if (cloudinary.config.apiSecret == null) return; - cloudinary.uploader().upload(getAssetStream("images/logo.png"), + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } public void testHeaders() throws Exception { if (cloudinary.config.apiSecret == null) return; - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", new String[] { "Link: 1" })); - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", new String[] { "Link: 1" })); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } public void testText() throws Exception { @@ -176,8 +177,8 @@ public void testText() throws Exception { public void testSprite() throws Exception { if (cloudinary.config.apiSecret == null) return; - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); JSONObject result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap())); assertEquals(2, result.getJSONObject("image_infos").length()); result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100"))); @@ -190,8 +191,8 @@ public void testSprite() throws Exception { public void testMulti() throws Exception { if (cloudinary.config.apiSecret == null) return; - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); JSONObject result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap())); assertTrue((result.getString("url")).endsWith(".gif")); result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100"))); @@ -205,9 +206,9 @@ public void testUniqueFilename() throws Exception { if (cloudinary.config.apiSecret == null) return; - File f = new File(getInstrumentation().getContext().getCacheDir() + "/logo.png"); + File f = new File(getInstrumentation().getContext().getCacheDir() + "/old_logo.png"); - InputStream is = getAssetStream("images/logo.png"); + InputStream is = getAssetStream(TEST_IMAGE); int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); @@ -232,7 +233,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120, 30, 229, 270); coordinates.addRect(rect1); coordinates.addRect(rect2); - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); JSONArray resultFaces = result.getJSONArray("faces"); assertEquals(2, resultFaces.length()); @@ -258,14 +259,14 @@ public void testContext() throws Exception { return; @SuppressWarnings("rawtypes") Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("context", context)); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("context", context)); } public void testModerationRequest() throws Exception { // should support requesting manual moderation if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("moderation", "manual"))); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("moderation", "manual"))); assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); } @@ -286,7 +287,7 @@ public void testCategorizationRequest() { if (cloudinary.config.apiSecret == null) return; try { - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("categorization", "illegal")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); } @@ -297,7 +298,7 @@ public void testDetectionRequest() { if (cloudinary.config.apiSecret == null) return; try { - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("detection", "illegal")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); } @@ -309,7 +310,7 @@ public void testAutoTaggingRequest() { return; try { - cloudinary.uploader().upload(getAssetStream("images/logo.png"), ObjectUtils.asMap("auto_tagging", 0.5f)); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("auto_tagging", 0.5f)); } catch (Exception e) { for (int i = 0; i < e.getStackTrace().length; i++) { StackTraceElement x = e.getStackTrace()[i]; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 3316f67c..25ad639f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -36,10 +36,16 @@ public class Configuration { public Map properties = new HashMap(); public Boolean secureCdnSubdomain; public boolean useRootPath; + public int timeout; + public Configuration(){ } - - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback,String proxyHost,int proxyPort,Boolean secureCdnSubdomain,boolean useRootPath) { + + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath) { + this(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, 0); + } + + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -55,6 +61,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.proxyPort = proxyPort; this.secureCdnSubdomain = secureCdnSubdomain; this.useRootPath = useRootPath; + this.timeout = 0; } @@ -80,6 +87,7 @@ public void update(Map config) { this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); + this.timeout = ObjectUtils.asInteger(config.get("timeout"), 0); } @@ -100,6 +108,7 @@ public Configuration(Configuration other) { this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; + this.timeout = other.timeout; } @@ -188,7 +197,18 @@ public static class Builder { private int proxyPort; private Boolean secureCdnSubdomain; private boolean useRootPath; - + private int timeout; + + /** + * Set the HTTP connection timeout. + * @param timeout time in milliseconds, or 0 to use the default platform value + * @return builder for chaining + */ + public Builder setTimeout(int timeout) { + this.timeout = timeout; + return this; + } + /** * Creates a {@link Configuration} with the arguments supplied to this builder @@ -313,6 +333,7 @@ public Builder from(Configuration other) { this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; + this.timeout = other.timeout; return this; } 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 2537e4e0..74ae2a8f 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -15,6 +15,8 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.ClientConnectionManager; 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; @@ -43,11 +45,13 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map 0) { + HttpParams httpParams = client.getParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, timeout ); + HttpConnectionParams.setSoTimeout(httpParams, timeout ); + } + URI apiUri = apiUrlBuilder.build(); HttpUriRequest request = null; switch (method) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index c3cd16ce..343b7aa3 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import org.apache.http.conn.ConnectTimeoutException; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; @@ -36,7 +37,8 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) public class ApiTest { - private Cloudinary cloudinary; + public static final String SRC_TEST_IMAGE = "src/test/resources/old_logo.png"; + private Cloudinary cloudinary; private Api api; private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); @@ -82,9 +84,9 @@ public static void setUpClass() throws IOException { } Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload("src/test/resources/logo.png", options); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options.put("public_id", "api_test1"); - cloudinary.uploader().upload("src/test/resources/logo.png", options); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } @Rule public TestName currentTest = new TestName(); @@ -115,16 +117,38 @@ public void test01ResourceTypes() throws Exception { assertContains("image", (Collection) result.get("resource_types")); } - @Test - public void test02Resources() throws Exception { - // should allow listing resources - Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } - - @Test + @Test + public void test02Resources() throws Exception { + // should allow listing resources + Map result = api.resources(ObjectUtils.emptyMap()); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Test + public void testTimeoutParameter() throws Exception { + // should allow listing resources + Map options = new HashMap(); + options.put("timeout", Integer.valueOf(5000)); + Map result = api.resources(options); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @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"); + + } + + @Test public void test03ResourcesCursor() throws Exception { // should allow listing resources with cursor Map options = new HashMap(); @@ -185,7 +209,7 @@ public void testResourcesListingStartAt() throws Exception { Thread.sleep(2000L); java.util.Date startAt = new java.util.Date(); Thread.sleep(2000L); - Map response = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); List resources = (List) listResources.get("resources"); assertEquals(response.get("public_id"), resources.get(0).get("public_id")); @@ -238,7 +262,7 @@ public void test07ResourceMetadata() throws Exception { @Test public void test08DeleteDerived() throws Exception { // should allow deleting derived resource - cloudinary.uploader().upload("src/test/resources/logo.png", + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); assertNotNull(resource); @@ -255,7 +279,7 @@ public void test08DeleteDerived() throws Exception { @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test3")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); @@ -265,7 +289,7 @@ public void test09DeleteResources() throws Exception { @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "api_test_by_prefix")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); @@ -275,7 +299,7 @@ public void test09aDeleteResourcesByPrefix() throws Exception { @Test(expected = NotFound.class) public void test09aDeleteResourcesByTags() throws Exception { // should allow deleting resources - cloudinary.uploader().upload("src/test/resources/logo.png", + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); assertNotNull(resource); @@ -405,7 +429,7 @@ public void test19Ping() throws Exception { // resources! public void testDeleteAllResources() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); Map result = api.resource("api_test5", ObjectUtils.emptyMap()); assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); @@ -418,7 +442,7 @@ public void testDeleteAllResources() throws Exception { @Test public void testManualModeration() throws Exception { // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); } @@ -427,7 +451,7 @@ public void testManualModeration() throws Exception { public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -439,7 +463,7 @@ public void testOcrUpdate() { public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -451,7 +475,7 @@ public void testRawConvertUpdate() { public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -463,7 +487,7 @@ public void testCategorizationUpdate() { public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -475,7 +499,7 @@ public void testDetectionUpdate() { public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -487,7 +511,7 @@ public void testSimilaritySearchUpdate() { public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[] { 121, 31, 110, 151}; @@ -586,9 +610,9 @@ public void testUpdateUploadPreset() throws Exception { @Test public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); @@ -611,10 +635,10 @@ public void testListByModerationUpdate() throws Exception { // @Test public void testFolderApi() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); Map result = api.rootFolders(null); assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index e9ec7c11..4e2e7356 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -281,8 +281,8 @@ public void testUnderlay() { @Test public void testFetchFormat() { // should support format for fetch urls - String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/logo.png"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/logo.png", result); + 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); } @Test diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index d13284a1..8fea5071 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -27,6 +27,8 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class UploaderTest { + public static final String SRC_TEST_IMAGE = "src/test/resources/old_logo.png"; + public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; private Cloudinary cloudinary; @BeforeClass @@ -48,7 +50,7 @@ public void setUp() { @Test public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("colors", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true)); assertEquals(result.get("width"), 241); assertEquals(result.get("height"), 51); assertNotNull(result.get("colors")); @@ -62,7 +64,7 @@ public void testUpload() throws IOException { @Test public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload("http://cloudinary.com/images/logo.png", ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.emptyMap()); assertEquals(result.get("width"), 241); assertEquals(result.get("height"), 51); Map to_sign = new HashMap(); @@ -86,7 +88,7 @@ public void testUploadDataUri() throws IOException { @Test public void testRename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); @@ -105,10 +107,10 @@ public void testRename() throws Exception { @Test public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true)); - assertTrue(((String) result.get("public_id")).matches("logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals((String) result.get("public_id"), "logo"); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); + assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); + assertEquals((String) result.get("public_id"), "old_logo"); } @Test public void testExplicit() throws IOException { @@ -121,13 +123,13 @@ public void testExplicit() throws IOException { @Test public void testEager() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } @Test public void testHeaders() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } @Test @@ -150,8 +152,8 @@ public void testImageUploadTag() { @Test public void testSprite() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); @@ -162,8 +164,8 @@ public void testSprite() throws IOException { @Test public void testMulti() throws IOException { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); assertTrue(((String) result.get("url")).endsWith(".gif")); result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); @@ -175,9 +177,9 @@ public void testMulti() throws IOException { @Test public void testTags() throws Exception { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); String public_id = (String)result.get("public_id"); - Map result2 = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.emptyMap()); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); String public_id2 = (String)result2.get("public_id"); cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); @@ -197,7 +199,7 @@ public void testTags() throws Exception { public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); assertEquals(result.get("format"), "png"); } @@ -207,7 +209,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { boolean errorFound = false; String[] formats = {"jpg"}; try{ - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); } catch(Exception e) { errorFound=true; } @@ -218,7 +220,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); assertEquals("jpg", result.get("format")); } @@ -230,7 +232,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120,30,109,150); coordinates.addRect(rect1); coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); ArrayList resultFaces = ((ArrayList) result.get("faces")); assertEquals(2, resultFaces.size()); @@ -269,7 +271,7 @@ public void testFaceCoordinates() throws Exception { public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("custom_coordinates", coordinates)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[]{121,31,110,151}; Object[] actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); @@ -291,7 +293,7 @@ public void testCustomCoordinates() throws Exception { public void testContext() throws Exception { //should allow sending context Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("context", context)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); @@ -303,7 +305,7 @@ public void testContext() throws Exception { @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("moderation", "manual")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); } @@ -313,7 +315,7 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { //should support requesting raw conversion try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("raw_convert", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -323,7 +325,7 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { //should support requesting categorization try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("categorization", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -333,7 +335,7 @@ public void testCategorizationRequest() { public void testDetectionRequest() { //should support requesting detection try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("detection", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); } catch(Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -343,7 +345,7 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { //should support requesting auto tagging try { - cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("auto_tagging", 0.5f)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); } catch(Exception e) { assertTrue(e.getMessage().matches("^Must use(.*)")); } @@ -361,7 +363,7 @@ public void testUploadLargeRawFiles() throws Exception { public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload("src/test/resources/logo.png", preset.get("name").toString(), ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.emptyMap()); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } diff --git a/cloudinary-http42/src/test/resources/logo.png b/cloudinary-http42/src/test/resources/old_logo.png similarity index 100% rename from cloudinary-http42/src/test/resources/logo.png rename to cloudinary-http42/src/test/resources/old_logo.png From a404eb27431f68860c59a3e6f7f55d9c033c94bb Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 24 Feb 2015 12:08:31 +0200 Subject: [PATCH 046/592] Fix test after file name change --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 7e8bc2c7..f4dbe60d 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -219,9 +219,9 @@ public void testUniqueFilename() throws Exception { fos.close(); JSONObject result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true))); - assertTrue(result.getString("public_id").matches("logo_[a-z0-9]{6}")); + assertTrue(result.getString("public_id").matches("old_logo_[a-z0-9]{6}")); result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true, "unique_filename", false))); - assertEquals(result.getString("public_id"), "logo"); + assertEquals(result.getString("public_id"), "old_logo"); } public void testFaceCoordinates() throws Exception { From fd21a76b404187170938492c1c8213cdc1573f4c Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 24 Feb 2015 12:23:10 +0200 Subject: [PATCH 047/592] Update version to 1.1.3 --- CHANGES.txt | 4 +++- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 719089cd..52387add 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,4 +3,6 @@ 1.0.13 - 2014-04-29 - Allow passing ClientConnectionManager. Add support for opacity. Support upload_presets. Support unsigned uploads. Support start_at for resource listing. Support phash for upload and resource details. Support rate limit header in Api calls. 1.0.14 - 2014-07-29 - Add background_removal. Support return_delete_token in upload/update params. Support responsive and hidpi. Support custom coordinates. 1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) -1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. \ No newline at end of file +1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. +1.1.2 - 2015-01-15 - fix support for string eager parameters +1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 05d391f9..16e78ab3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -33,7 +33,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.1.2"; + public final static String VERSION = "1.1.3"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; From d58e149685f0bfec552fe32c14c870bca412c9b9 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 24 Feb 2015 12:12:45 +0200 Subject: [PATCH 048/592] [maven-release-plugin] prepare release cloudinary-parent-1.1.3 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 62870dc5..531deb9d 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 com.cloudinary cloudinary-android-test - 1.1.3-SNAPSHOT + 1.1.3 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.3-SNAPSHOT + 1.1.3 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index a5299dcd..02257932 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 86f8b337..9ee43437 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 128b5068..dc9eaebc 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index d71260d7..a5dfab5e 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 cloudinary-taglib diff --git a/pom.xml b/pom.xml index 1089ea74..67f423be 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.3-SNAPSHOT + 1.1.3 pom Cloudinary Java Client Library Parent Project From 5c373a805cf7be6da3102497e5abd67030b8befa Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 24 Feb 2015 12:12:50 +0200 Subject: [PATCH 049/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 531deb9d..eb88749f 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT com.cloudinary cloudinary-android-test - 1.1.3 + 1.1.4-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.3 + 1.1.4-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 02257932..9f76b630 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 9ee43437..d483c539 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index dc9eaebc..edd32da4 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index a5dfab5e..c6df9423 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT cloudinary-taglib diff --git a/pom.xml b/pom.xml index 67f423be..3dfabde8 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.3 + 1.1.4-SNAPSHOT pom Cloudinary Java Client Library Parent Project From b1c31af6ee4e6beb857144f9a798464c518f8a7c Mon Sep 17 00:00:00 2001 From: itaibenari Date: Wed, 25 Feb 2015 08:57:43 +0200 Subject: [PATCH 050/592] Update README to point to 1.1.3 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c00f34f4..2221480b 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http42 - 1.1.0 + 1.1.3 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.1.0/cloudinary-core-1.1.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http42/1.1.0/cloudinary-http42-1.1.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.1.3/cloudinary-core-1.1.3.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http42/1.1.3/cloudinary-http42-1.1.3.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/blob/master/pom.xml) for library dependencies. ## Try it right away From d81403103e40fbe8299839ccc5e69b8eb9df939e Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Sun, 15 Mar 2015 14:52:27 +0200 Subject: [PATCH 051/592] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2221480b..efe564fb 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.1.3/cloudinary-core-1.1.3.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http42/1.1.3/cloudinary-http42-1.1.3.jar) -and see [pom.xml](https://github.com/cloudinary/cloudinary_java/cloudinary-http42/blob/master/pom.xml) for library dependencies. +and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http42/pom.xml) for library dependencies. ## Try it right away From 074dad17e793dc19249c36e1708d69a42934ba9d Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Sun, 15 Mar 2015 16:11:17 +0200 Subject: [PATCH 052/592] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index efe564fb..be99c75e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Images are seamlessly delivered through a fast CDN, and much much more. Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. + Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. For Java, Cloudinary provides a library for simplifying the integration even further. @@ -134,7 +135,7 @@ The uploaded image is assigned a randomly generated public ID. The image is imme You can also specify your own public ID: - cloudinary.uploader().upload("http://www.example.com/image.jpg", Cloudinary.asMap("public_id", "sample_remote")) + cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")) cloudinary.url().generate("sample_remote.jpg") From 8dfd7e108633e65982fb436eb77b27ecc0170916 Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Sun, 15 Mar 2015 16:14:10 +0200 Subject: [PATCH 053/592] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index be99c75e..2bbc6758 100644 --- a/README.md +++ b/README.md @@ -101,21 +101,21 @@ Any image uploaded to Cloudinary can be transformed and embedded using powerful The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: - cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") + cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg"); Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: - cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg") + cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg"); You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. Embedding a Facebook profile to match your graphic design is very simple: - cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg") + cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg"); Same goes for Twitter: - cloudinary.url().type("twitter_name").generate("billclinton.jpg") + cloudinary.url().type("twitter_name").generate("billclinton.jpg"); ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_manipulation) for more information about displaying and transforming images in Java**. @@ -125,19 +125,19 @@ Assuming you have your Cloudinary configuration parameters defined (`cloud_name` The following example uploads a local JPG to the cloud: - cloudinary.uploader().upload("my_picture.jpg", Cloudinary.emptyMap()) + cloudinary.uploader().upload("my_picture.jpg", Cloudinary.emptyMap()); The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: - cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg") + cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg"); http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg You can also specify your own public ID: - cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")) + cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")); - cloudinary.url().generate("sample_remote.jpg") + cloudinary.url().generate("sample_remote.jpg"); http://res.cloudinary.com/demo/image/upload/sample_remote.jpg @@ -149,7 +149,7 @@ Returns an html image tag pointing to Cloudinary. Usage: - cloudinary.url().format("png").transformation(new Transformation().width(100).height(100).crop("fill")).imageTag("sample") + cloudinary.url().format("png").transformation(new Transformation().width(100).height(100).crop("fill")).imageTag("sample"); # From c067695950fccbb612aff7fdfe51b8bf1850996f Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Sun, 15 Mar 2015 16:15:18 +0200 Subject: [PATCH 054/592] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2bbc6758..b829c567 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ The uploaded image is assigned a randomly generated public ID. The image is imme cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg"); - http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg + # http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg You can also specify your own public ID: @@ -139,7 +139,7 @@ You can also specify your own public ID: cloudinary.url().generate("sample_remote.jpg"); - http://res.cloudinary.com/demo/image/upload/sample_remote.jpg + # http://res.cloudinary.com/demo/image/upload/sample_remote.jpg ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. From 51f87f7d6a0bbf72df114cf882fbaa64b888eeb6 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 23 Mar 2015 08:50:42 +0200 Subject: [PATCH 055/592] better support for utf-8 --- .../main/java/com/cloudinary/http42/UploaderStrategy.java | 8 +++++--- .../src/test/java/com/cloudinary/test/UploaderTest.java | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 63b5dcb4..64042017 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.Collection; import java.util.Map; @@ -58,18 +59,19 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT); + Charset utf8 = Charset.forName("UTF-8"); MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); // 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))); + 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)); + multipart.addPart(param.getKey(), new StringBody(value, utf8)); } } } @@ -80,7 +82,7 @@ public Map callApi(String action, Map params, Map options, Objec if (file instanceof File) { multipart.addPart("file", new FileBody((File) file)); } else if (file instanceof String) { - multipart.addPart("file", new StringBody((String) file)); + multipart.addPart("file", new StringBody((String) file, utf8)); } else if (file instanceof byte[]) { multipart.addPart("file", new ByteArrayBody((byte[]) file, "file")); } else if (file == null) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 8fea5071..3613f183 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -85,6 +85,12 @@ public void testUploadDataUri() throws IOException { String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } + + @Test + public void testUploadUTF8() throws IOException { + Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); + assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); + } @Test public void testRename() throws Exception { From f8e18a3db4f379b4227bfebf387900ba10a73c26 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 23 Mar 2015 20:11:41 +0200 Subject: [PATCH 056/592] Modify exception message to say that Admin API is not supported. --- .../src/main/java/com/cloudinary/android/ApiStrategy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java index 211473bb..8cb0cf09 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java @@ -10,8 +10,8 @@ public class ApiStrategy extends AbstractApiStrategy { @SuppressWarnings("rawtypes") @Override - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - throw new Exception("not implemented"); + public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + throw new Exception("Administration API is not supported for mobile applications."); } } From 8b9a775ee8e9e10bee72cf5c89f730bc607c29f8 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 24 Mar 2015 13:16:21 +0200 Subject: [PATCH 057/592] Fix documentation and imports --- README.md | 2 +- .../cloudinary/android/MultipartUtility.java | 2 +- .../java/com/cloudinary/Configuration.java | 8 +++--- .../src/main/java/com/cloudinary/Url.java | 5 ++-- .../java/com/cloudinary/api/ApiResponse.java | 5 ++-- .../com/cloudinary/utils/Base64Coder.java | 24 ++++++++--------- .../java/org/cloudinary/json/JSONObject.java | 2 +- .../com/cloudinary/http42/ApiStrategy.java | 3 ++- .../cloudinary/http42/UrlBuilderStrategy.java | 3 ++- .../com/cloudinary/http42/api/Response.java | 5 ++-- .../java/com/cloudinary/test/ApiTest.java | 22 ++++++++------- .../com/cloudinary/test/UploaderTest.java | 3 ++- .../cloudinary/taglib/CloudinaryImageTag.java | 27 ++++++++++++------- .../com/cloudinary/taglib/CloudinaryUrl.java | 6 +++-- 14 files changed, 67 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index c00f34f4..4637066f 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ Assuming you have your Cloudinary configuration parameters defined (`cloud_name` The following example uploads a local JPG to the cloud: - cloudinary.uploader().upload("my_picture.jpg", Cloudinary.emptyMap()) + cloudinary.uploader().upload("my_picture.jpg", ObjectUtils.emptyMap()) The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 51614414..491f7987 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -74,7 +74,7 @@ public void addFormField(String name, String value) { * Adds a upload file section to the request * * @param fieldName - * name attribute in + * name attribute in {@code } * @param uploadFile * a File to be uploaded * @throws IOException diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 25ad639f..9846aaea 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -115,7 +115,7 @@ public Configuration(Configuration other) { /** * Create a new Configuration from an existing one * @param other - * @return + * @return a new configuration with the arguments supplied by another configuration object */ public static Configuration from(Configuration other) { return new Builder().from(other).build(); @@ -127,7 +127,7 @@ public static Configuration from(Configuration other) { * Example url: cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7 * * @param cloudinaryUrl configuration url - * @return + * @return a new configuration with the arguments supplied by the url */ public static Configuration from(String cloudinaryUrl) { return from(parseConfigUrl(cloudinaryUrl)); @@ -314,8 +314,8 @@ public Builder setUseRootPath(boolean useRootPath) { /** * Initialize builder from existing {@link Configuration} - * @param other - * @return + * @param other a different configuration object + * @return an initialized builder configured with other */ public Builder from(Configuration other) { this.cloudName = other.cloudName; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 28c3ad09..0aca9477 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -36,9 +36,8 @@ public Url(Cloudinary cloudinary) { private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); /** - * Parses a cloudinary identifier of the form: - * [/][/][v/][.][#] + * Parses a cloudinary identifier of the form:
+ * {@code [/][/][v/][.][#]} */ public Url fromIdentifier(String identifier) { Matcher matcher = identifierPattern.matcher(identifier); diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java index 9edb8a82..32ed9490 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java @@ -1,9 +1,10 @@ package com.cloudinary.api; +import java.text.ParseException; import java.util.Map; @SuppressWarnings("rawtypes") public interface ApiResponse extends Map { - Map rateLimits() throws java.text.ParseException; - RateLimit apiRateLimit() throws java.text.ParseException; + Map rateLimits() throws ParseException; + RateLimit apiRateLimit() throws ParseException; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java index a22ca79f..f110f425 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java @@ -70,7 +70,7 @@ public static String encodeString(String s) { /** * Encodes a byte array into Base 64 format and breaks the output into lines * of 76 characters. This method is compatible with - * sun.misc.BASE64Encoder.encodeBuffer(byte[]). + * {@code sun.misc.BASE64Encoder.encodeBuffer(byte[])}. * * @param in * An array containing the data bytes to be encoded. @@ -87,10 +87,10 @@ public static String encodeLines(byte[] in) { * @param in * An array containing the data bytes to be encoded. * @param iOff - * Offset of the first byte in in to be processed. + * Offset of the first byte in {@code in} to be processed. * @param iLen - * Number of bytes to be processed in in, starting - * at iOff. + * Number of bytes to be processed in {@code in}, starting + * at {@code iOff}. * @param lineLen * Line length for the output data. Should be a multiple of 4. * @param lineSeparator @@ -134,7 +134,7 @@ public static char[] encode(byte[] in) { * @param in * An array containing the data bytes to be encoded. * @param iLen - * Number of bytes to process in in. + * Number of bytes to process in {@code in}. * @return A character array containing the Base64 encoded data. */ public static char[] encode(byte[] in, int iLen) { @@ -148,10 +148,10 @@ public static char[] encode(byte[] in, int iLen) { * @param in * An array containing the data bytes to be encoded. * @param iOff - * Offset of the first byte in in to be processed. + * Offset of the first byte in {@code in} to be processed. * @param iLen - * Number of bytes to process in in, starting at - * iOff. + * Number of bytes to process in {@code in}, starting at + * {@code iOff}. * @return A character array containing the Base64 encoded data. */ public static char[] encode(byte[] in, int iOff, int iLen) { @@ -197,7 +197,7 @@ public static String decodeString(String s) { * Decodes a byte array from Base64 format and ignores line separators, tabs * and blanks. CR, LF, Tab and Space characters are ignored in the input * data. This method is compatible with - * sun.misc.BASE64Decoder.decodeBuffer(String). + * {@code sun.misc.BASE64Decoder.decodeBuffer(String)}. * * @param s * A Base64 String to be decoded. @@ -251,11 +251,11 @@ public static byte[] decode(char[] in) { * @param in * A character array containing the Base64 encoded data. * @param iOff - * Offset of the first character in in to be + * Offset of the first character in {@code in} to be * processed. * @param iLen - * Number of characters to process in in, starting - * at iOff. + * Number of characters to process in {@code in}, starting + * at {@code iOff}. * @return An array containing the decoded data bytes. * @throws IllegalArgumentException * If the input is not valid Base64 encoded data. diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java index 05b4a5c6..3ad4154d 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java @@ -1196,7 +1196,7 @@ public JSONObject putOpt(String key, Object value) throws JSONException { /** * Produce a string in double quotes with backslash sequences in all the - * right places. A backslash will be inserted within rateLimits() throws java.text.ParseException { + public Map rateLimits() throws ParseException { Header[] headers = this.response.getAllHeaders(); Map limits = new HashMap(); for (Header header : headers) { @@ -63,7 +64,7 @@ public Map rateLimits() throws java.text.ParseException { return limits; } - public RateLimit apiRateLimit() throws java.text.ParseException { + public RateLimit apiRateLimit() throws ParseException { return rateLimits().get("Api"); } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 343b7aa3..6300abe7 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -13,11 +13,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.conn.ConnectTimeoutException; +import org.cloudinary.json.JSONArray; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; @@ -40,7 +42,7 @@ public class ApiTest { public static final String SRC_TEST_IMAGE = "src/test/resources/old_logo.png"; private Cloudinary cloudinary; private Api api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + private static String uniqueTag = String.format("api_test_tag_%d", new Date().getTime()); @BeforeClass public static void setUpClass() throws IOException { @@ -207,7 +209,7 @@ public void testResourcesListingStartAt() throws Exception { // should allow listing resources by start date - make sure your clock // is set correctly!!! Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); + Date startAt = new Date(); Thread.sleep(2000L); Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); @@ -432,7 +434,7 @@ public void testDeleteAllResources() throws Exception { cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); + assertEquals(1, ((JSONArray) result.get("derived")).length()); api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); result = api.resource("api_test5", ObjectUtils.emptyMap()); // assertEquals(0, ((org.cloudinary.json.JSONArray) @@ -533,7 +535,7 @@ public void testApiLimits() throws Exception { assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); - assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); + assertTrue(result2.apiRateLimit().getReset().after(new Date())); } @Test @@ -567,10 +569,10 @@ public void testGetUploadPreset() throws Exception { assertEquals(Boolean.TRUE, preset.get("unsigned")); Map settings = (Map) preset.get("settings"); assertEquals(settings.get("folder"), "folder"); - Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); + Map outTransformation = (Map) ((ArrayList) settings.get("transformation")).get(0); assertEquals(outTransformation.get("width"), 100); assertEquals(outTransformation.get("crop"), "scale"); - Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); + Object[] outTags = ((ArrayList) settings.get("tags")).toArray(); assertArrayEquals(tags, outTags); Map outContext = (Map) settings.get("context"); assertEquals(context, outContext); @@ -640,11 +642,11 @@ public void testFolderApi() throws Exception { cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); + assertEquals("test_folder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("name")); result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); + assertEquals("test_folder1/test_subfolder1", ((Map) ((JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", ((Map) ((JSONArray) result.get("folders")).get(1)).get("path")); try { api.subFolders("test_folder", null); } catch (Exception e) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 8fea5071..ec39bbc8 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -355,7 +356,7 @@ public void testAutoTaggingRequest() { public void testUploadLargeRawFiles() throws Exception { // support uploading large raw files Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); - assertEquals((int)(new java.io.File("src/test/resources/docx.docx").length()), response.get("bytes")); + assertEquals((int)(new File("src/test/resources/docx.docx").length()), response.get("bytes")); assertEquals(Boolean.TRUE, response.get("done")); } 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 01e1320e..213d23e1 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java @@ -14,15 +14,24 @@ import com.cloudinary.*; /** - * - * - * Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - * String result = cloudinary.url().transformation(transformation).imageTag("test", - * Cloudinary.asMap("alt", "my image")); - * - * my image - * + * Generates an image html tag.
+ * For example,
+ * {@code } + *
is equivalent to:
+ *

{@code
+ * Transformation transformation = new Transformation()
+ *      .width(100)
+ *      .height(101)
+ *      .crop("crop");
+ * String result = cloudinary.url()
+ *      .transformation(transformation)
+ *      .imageTag("test", Cloudinary.asMap("alt", "my image"));
+ * }
+ *
+ * Both code segments above produce the following tag:
+ * {@code my image } + *
* @author jpollak * */ diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java index dcf71785..6e36f6fc 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java @@ -14,8 +14,10 @@ import com.cloudinary.*; /** - * - * http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test + * Generates a cloudinary resource url + * + *
For example,
{@code } + * will produce
{@code http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test} * */ public class CloudinaryUrl extends SimpleTagSupport implements DynamicAttributes { From a4764938573da82002cdc530a45720ee4428d2d9 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 23 Mar 2015 14:38:20 +0200 Subject: [PATCH 058/592] initial support http 4.4 --- cloudinary-core/pom.xml | 2 + .../main/java/com/cloudinary/Cloudinary.java | 18 +- cloudinary-http42/pom.xml | 8 +- .../java/com/cloudinary/test/ApiTest.java | 643 +---------------- .../com/cloudinary/test/CloudinaryTest.java | 588 +--------------- .../com/cloudinary/test/UploaderTest.java | 374 +--------- cloudinary-http44/pom.xml | 42 ++ .../com/cloudinary/http44/ApiStrategy.java | 150 ++++ .../cloudinary/http44/UploaderStrategy.java | 137 ++++ .../cloudinary/http44/UrlBuilderStrategy.java | 23 + .../com/cloudinary/http44/api/Response.java | 69 ++ .../java/com/cloudinary/test/ApiTest.java | 4 + .../com/cloudinary/test/CloudinaryTest.java | 5 + .../com/cloudinary/test/UploaderTest.java | 5 + cloudinary-test-common/pom.xml | 26 + .../com/cloudinary/test/AbstractApiTest.java | 647 ++++++++++++++++++ .../test/AbstractCloudinaryTest.java | 591 ++++++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 377 ++++++++++ .../src/main}/resources/docx.docx | Bin .../src/main}/resources/favicon.ico | Bin .../src/main}/resources/old_logo.png | Bin pom.xml | 2 + 22 files changed, 2107 insertions(+), 1604 deletions(-) create mode 100644 cloudinary-http44/pom.xml create mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java create mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java create mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java create mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java create mode 100644 cloudinary-test-common/pom.xml create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java rename {cloudinary-http42/src/test => cloudinary-test-common/src/main}/resources/docx.docx (100%) rename {cloudinary-http42/src/test => cloudinary-test-common/src/main}/resources/favicon.ico (100%) rename {cloudinary-http42/src/test => cloudinary-test-common/src/main}/resources/old_logo.png (100%) diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index d483c539..87f38585 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -10,4 +10,6 @@ cloudinary-core jar + Cloudinary Core Library + diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 16e78ab3..f1eea1b2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -24,9 +24,21 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) public class Cloudinary { - private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList("com.cloudinary.android.UploaderStrategy","com.cloudinary.http42.UploaderStrategy","com.cloudinary.http43.UploaderStrategy")); - private static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy" )); - private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UrlBuilderStrategy", "com.cloudinary.http42.UrlBuilderStrategy", "com.cloudinary.http43.UrlBuilderStrategy" )); + 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")); + private static List API_STRATEGIES = new ArrayList(Arrays.asList( + "com.cloudinary.android.ApiStrategy", + "com.cloudinary.http42.ApiStrategy", + "com.cloudinary.http43.ApiStrategy", + "com.cloudinary.http44.ApiStrategy" )); + private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( + "com.cloudinary.android.UrlBuilderStrategy", + "com.cloudinary.http42.UrlBuilderStrategy", + "com.cloudinary.http43.UrlBuilderStrategy", + "com.cloudinary.http44.UrlBuilderStrategy" )); 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-http42/pom.xml b/cloudinary-http42/pom.xml index edd32da4..1ad858c3 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -12,6 +12,7 @@ Cloudinary Apache HTTP 4.2 Library + com.cloudinary cloudinary-core @@ -37,6 +38,11 @@ httpmime 4.2.1 - + + com.cloudinary + cloudinary-test-common + ${project.version} + test + diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 343b7aa3..a0863cb6 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -1,143 +1,15 @@ package com.cloudinary.test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TestName; - -import com.cloudinary.Api; -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.BadRequest; -import com.cloudinary.api.exceptions.NotFound; -import com.cloudinary.utils.ObjectUtils; - -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class ApiTest { - - public static final String SRC_TEST_IMAGE = "src/test/resources/old_logo.png"; - private Cloudinary cloudinary; - private Api api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); - - @BeforeClass - public static void setUpClass() throws IOException { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - return; - } - Api api = cloudinary.api(); - try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", - Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options.put("public_id", "api_test1"); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - } - - @Rule public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.config.apiSecret); - this.api = cloudinary.api(); - - - } - public Map findByAttr(List elements, String attr, Object value) { - for (Map element : elements) { - if (value.equals(element.get(attr))) { - return element; - } - } - return null; - } - - @Test - public void test01ResourceTypes() throws Exception { - // should allow listing resource_types - Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); - } - - @Test - public void test02Resources() throws Exception { - // should allow listing resources - Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } - - @Test - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = new HashMap(); - options.put("timeout", Integer.valueOf(5000)); - Map result = api.resources(options); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); - } +import org.apache.http.conn.ConnectTimeoutException; - @Test(expected = ConnectTimeoutException.class) +public class ApiTest extends AbstractApiTest { + @Test(expected = ConnectTimeoutException.class) public void testTimeoutException() throws Exception { // should allow listing resources Map options = new HashMap(); @@ -147,513 +19,4 @@ public void testTimeoutException() throws Exception { Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); } - - @Test - public void test03ResourcesCursor() throws Exception { - // should allow listing resources with cursor - Map options = new HashMap(); - options.put("max_results", 1); - Map result = api.resources(options); - List resources = (List) result.get("resources"); - assertNotNull(resources); - assertEquals(1, resources.size()); - assertNotNull(result.get("next_cursor")); - - options.put("next_cursor", result.get("next_cursor")); - Map result2 = api.resources(options); - List resources2 = (List) result2.get("resources"); - assertNotNull(resources2); - assertEquals(resources2.size(), 1); - assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void test04ResourcesByType() throws Exception { - // should allow listing resources by type - Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - } - - @Test - public void test05ResourcesByPrefix() throws Exception { - // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void testResourcesListingDirection() throws Exception { - // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); - List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); - List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); - } - - @Ignore - public void testResourcesListingStartAt() throws Exception { - // should allow listing resources by start date - make sure your clock - // is set correctly!!! - Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); - Thread.sleep(2000L); - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); - List resources = (List) listResources.get("resources"); - assertEquals(response.get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void testResourcesByPublicIds() throws Exception { - // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test06ResourcesTag() throws Exception { - // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); - assertNotNull(resource); - List resources = (List) result.get("resources"); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test07ResourceMetadata() throws Exception { - // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); - assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); - assertEquals(resource.get("bytes"), 3381); - assertEquals(((List) resource.get("derived")).size(), 1); - } - - @Test - public void test08DeleteDerived() throws Exception { - // should allow deleting derived resource - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - List derived = (List) resource.get("derived"); - assertEquals(derived.size(), 1); - String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - derived = (List) resource.get("derived"); - assertEquals(derived.size(), 0); - } - - @Test(expected = NotFound.class) - public void test09DeleteResources() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByPrefix() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByTags() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); - } - - @Test - public void test10Tags() throws Exception { - // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - } - - @Test - public void test11TagsPrefix() throws Exception { - // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); - tags = (List) result.get("tags"); - assertEquals(0, tags.size()); - } - - @Test - public void test12Transformations() throws Exception { - // should allow listing transformations - Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); - - assertNotNull(transformation); - assertTrue((Boolean) transformation.get("used")); - } - - @Test - public void test13TransformationMetadata() throws Exception { - // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); - } - - @Test - public void test14TransformationUpdate() throws Exception { - // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), false); - } - - @Test - public void test15TransformationCreate() throws Exception { - // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test15aTransformationUnsafeUpdate() throws Exception { - // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), - ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test16aTransformationDelete() throws Exception { - // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test - public void test17aTransformationDeleteImplicit() throws Exception { - // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ - @Test(expected = NotFound.class) - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - @Test - public void test18Usage() throws Exception { - // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); - assertNotNull(result.get("last_updated")); - } - - @Test - public void test19Ping() throws Exception { - // should support ping API call - Map result = api.ping(ObjectUtils.emptyMap()); - assertEquals(result.get("status"), "ok"); - } - - // This test must be last because it deletes (potentially) all dependent - // transformations which some tests rely on. - // Add @Test if you really want to test it - This test deletes derived - // resources! - public void testDeleteAllResources() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); - api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); - // assertEquals(0, ((org.cloudinary.json.JSONArray) - // result.get("derived")).size()); - } - - @Test - public void testManualModeration() throws Exception { - // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); - } - - @Test - public void testOcrUpdate() { - // should support requesting ocr info - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testRawConvertUpdate() { - // should support requesting raw conversion - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testCategorizationUpdate() { - // should support requesting categorization - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testDetectionUpdate() { - // should support requesting detection - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testSimilaritySearchUpdate() { - // should support requesting similarity search - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testUpdateCustomCoordinates() throws IOException, Exception { - // should update custom coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - int[] expected = new int[] { 121, 31, 110, 151}; - ArrayList actual = (ArrayList) ((ArrayList)((Map) result.get("coordinates")).get("custom")).get(0); - for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i], actual.get(i)); - } - } - - @Test - public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response - // header - ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); - ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); - assertNotNull(result1.apiRateLimit()); - assertNotNull(result2.apiRateLimit()); - assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); - assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); - assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); - assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); - assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); - } - - @Test - public void testListUploadPresets() throws Exception { - // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); - - ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); - assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } - - @Test - public void testGetUploadPreset() throws Exception { - // should allow getting a single upload_preset - String[] tags = { "a", "b", "c" }; - Map context = ObjectUtils.asMap("a", "b", "c", "d"); - Transformation transformation = new Transformation(); - transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", - context)); - 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"); - Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); - assertEquals(outTransformation.get("width"), 100); - assertEquals(outTransformation.get("crop"), "scale"); - Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); - assertArrayEquals(tags, outTags); - Map outContext = (Map) settings.get("context"); - assertEquals(context, outContext); - } - - @Test - public void testDeleteUploadPreset() throws Exception { - // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - boolean error = false; - try { - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - error = true; - } - assertTrue(error); - } - - @Test - public void testUpdateUploadPreset() throws Exception { - // should allow updating upload_presets - 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)); - 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, preset.get("settings")); - api.deleteUploadPreset(name, ObjectUtils.emptyMap()); - } - - @Test - public void testListByModerationUpdate() throws Exception { - // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); - Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); - Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); - Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); - } - - // For this test to work, "Auto-create folders" should be enabled in the - // Upload Settings. - // Uncomment @Test if you really want to test it. - // @Test - public void testFolderApi() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); - Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); - result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); - try { - api.subFolders("test_folder", null); - } catch (Exception e) { - assertTrue(e instanceof NotFound); - } - api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); - } - - private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java index 4e2e7356..7d7d9b30 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1,591 +1,5 @@ package com.cloudinary.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +public class CloudinaryTest extends AbstractCloudinaryTest { -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Transformation; -import com.cloudinary.utils.ObjectUtils; - -public class CloudinaryTest { - - private Cloudinary cloudinary; - - @Rule - public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); - } - - @Test - public void testCloudName() { - // should use cloud_name from config - String result = cloudinary.url().generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testSecureDistribution() { - // should use default secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistributionOverwrite() { - // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); - assertEquals("https://something.else.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistibution() { - // should take secure distribution from config if secure=TRUE - cloudinary.config.secureDistribution = "config.secure.distribution.com"; - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - 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"); - assertEquals("https://something.cloudfront.net/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testFormat() { - // should use format from options - String result = cloudinary.url().format("jpg").generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); - } - - @Test - public void testCrop() { - Transformation transformation = new Transformation().width(100).height(101); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); - assertEquals("101", transformation.getHtmlHeight().toString()); - assertEquals("100", transformation.getHtmlWidth().toString()); - transformation = new Transformation().width(100).height(101).crop("crop"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); - } - - @Test - public void testVariousOptions() { - // should use x, y, radius, prefix, gravity and quality from options - Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); - } - - @Test - public void testTransformationSimple() { - // should support named transformation - Transformation transformation = new Transformation().named("blip"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); - } - - @Test - public void testTransformationArray() { - // should support array of named transformations - Transformation transformation = new Transformation().named("blip", "blop"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); - } - - @Test - public void testBaseTransformations() { - // should support base transformation - Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); - } - - @Test - public void testBaseTransformationArray() { - // should support array of base transformations - Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); - } - - @Test - public void testNoEmptyTransformation() { - // should not include empty transformations - Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); - } - - @Test - 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); - } - - @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); - } - - @Test - public void testIgnoreHttp() { - // should ignore http links only if type is not given or is asset - String result = cloudinary.url().generate("http://test"); - assertEquals("http://test", result); - 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); - } - - @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); - } - - @Test - public void testCname() { - // should support external cname - String result = cloudinary.url().cname("hello.com").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"); - assertEquals("http://a2.hello.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testBackground() { - // should support background - Transformation transformation = new Transformation().background("red"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); - transformation = new Transformation().background("#112233"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); - } - - @Test - public void testDefaultImage() { - // should support default_image - Transformation transformation = new Transformation().defaultImage("default"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); - } - - @Test - public void testAngle() { - // should support angle - Transformation transformation = new Transformation().angle(12); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); - transformation = new Transformation().angle("exif", "12"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); - } - - @Test - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); - } - - @Test - public void testUnderlay() { - Transformation transformation = new Transformation().underlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); - } - - @Test - 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); - } - - @Test - public void testEffect() { - // should support effect - Transformation transformation = new Transformation().effect("sepia"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); - } - - @Test - public void testEffectWithParam() { - // should support effect with param - Transformation transformation = new Transformation().effect("sepia", 10); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); - } - - @Test - public void testDensity() { - // should support density - Transformation transformation = new Transformation().density(150); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); - } - - @Test - public void testPage() { - // should support page - Transformation transformation = new Transformation().page(5); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); - } - - @Test - public void testBorder() { - // should support border - Transformation transformation = new Transformation().border(5, "black"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); - transformation = new Transformation().border(5, "#ffaabbdd"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); - transformation = new Transformation().border("1px_solid_blue"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); - } - - @Test - public void testFlags() { - // should support flags - Transformation transformation = new Transformation().flags("abc"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); - transformation = new Transformation().flags("abc", "def"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); - } - - @Test - public void testOpacity() { - // should support opacity - Transformation transformation = new Transformation().opacity(50); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testImageTag() { - Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals("my image", result); - transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); - assertEquals( - "my image", - result); - transformation = new Transformation().width("auto").crop("crop"); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); - assertEquals( - "my image", - result); - } - - @Test - public void testFolders() { - // should add version if public_id contains / - String result = cloudinary.url().generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); - result = cloudinary.url().version(123).generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); - } - - @Test - public void testFoldersWithVersion() { - // should not add version if public_id contains version already - String result = cloudinary.url().generate("v1234/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); - } - - @Test - public void testShorten() { - // should allow to shorted image/upload urls - String result = cloudinary.url().shorten(true).generate("test"); - assertEquals("http://res.cloudinary.com/test123/iu/test", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testPrivateDownload() throws Exception { - String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("img", parameters.get("public_id")); - assertEquals("jpg", parameters.get("format")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download", uri.getPath()); - } - - @SuppressWarnings("unchecked") - @Test - public void testZipDownload() throws Exception { - String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("ttag", parameters.get("tag")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); - } - - @Test - public void testSpriteCss() { - String result = cloudinary.url().generateSpriteCss("test"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - result = cloudinary.url().generateSpriteCss("test.css"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testEscapePublicId() { - // should escape public_ids - Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); - for (Map.Entry entry : tests.entrySet()) { - String result = cloudinary.url().generate(entry.getKey()); - assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); - } - } - - @Test - public void testSignedUrl() { - // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; - String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) - .generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; - actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; - actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - } - - @Test - public void testResponsiveWidth() { - // should support responsive width - Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - String result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); - trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(null); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInSharedDistribution() { - cloudinary.url().suffix("hello").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInNonUploadTypes() { - cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithSlash() { - cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithDot() { - cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); - } - - @Test - public void testSupportUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); - assertEquals("http://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); - - } - - @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); - } - - @Test - public void testNotSignTheUrlSuffix() { - - Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); - String url = cloudinary.url().format("jpg").signed(true).generate("test"); - Matcher matcher = pattern.matcher(url); - matcher.find(); - 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); - - url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); - matcher = pattern.matcher(url); - matcher.find(); - expectedSignature = url.substring(matcher.start(), matcher.end()); - - 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); - } - - @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); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathInSharedDistribution() { - cloudinary.url().useRootPath(true).generate("test"); - } - - @Test - public void testSupportUseRootPathForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); - assertEquals("http://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); - } - - @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); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForFacebook() { - cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForRaw() { - cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); - } - - public void testUtils() { - assertEquals(ObjectUtils.asBoolean(true, null), true); - assertEquals(ObjectUtils.asBoolean(false, null), false); - } - - public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { - Map params = new HashMap(); - for (String param : uri.getQuery().split("&")) { - String pair[] = param.split("="); - String key = URLDecoder.decode(pair[0], "UTF-8"); - String value = ""; - if (pair.length > 1) { - value = URLDecoder.decode(pair[1], "UTF-8"); - } - params.put(new String(key), new String(value)); - } - return params; - } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java index 3613f183..712b0ffd 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java @@ -1,377 +1,5 @@ package com.cloudinary.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.Rectangle; - -@SuppressWarnings({"rawtypes", "unchecked"}) -public class UploaderTest { - - public static final String SRC_TEST_IMAGE = "src/test/resources/old_logo.png"; - public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; - private Cloudinary cloudinary; - - @BeforeClass - public static void setUpClass() { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - } - } - - @Rule public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.config.apiSecret); - } - - @Test - public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true)); - assertEquals(result.get("width"), 241); - assertEquals(result.get("height"), 51); - assertNotNull(result.get("colors")); - assertNotNull(result.get("predominant")); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 241); - assertEquals(result.get("height"), 51); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 16); - assertEquals(result.get("height"), 16); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadUTF8() throws IOException { - Map result = cloudinary.uploader().upload("src/test/resources/logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); - assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); - } - - @Test - public void testRename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - - cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); - assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); - - Map result2 = cloudinary.uploader().upload("src/test/resources/favicon.ico", ObjectUtils.emptyMap()); - boolean error_found=false; - try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); - } catch(Exception e) { - error_found=true; - } - assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); - assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); - } - - @Test - public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); - assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals((String) result.get("public_id"), "old_logo"); - } - @Test - public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); - String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); - String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); - String cloudName = cloudinary.config.cloudName; - assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); - } - - @Test - public void testEager() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - } - - @Test - public void testHeaders() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); - } - - @Test - public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); - assertTrue(((Integer) result.get("width")) > 1); - assertTrue(((Integer) result.get("height")) > 1); - } - - @Test - public void testImageUploadTag() { - String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); - assertTrue(tag.contains("type='file'")); - assertTrue(tag.contains("data-cloudinary-field='test-field'")); - assertTrue(tag.contains("class='cloudinary-fileupload'")); - assertTrue(tag.contains("htmlattr='htmlvalue'")); - tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); - assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); - } - - @Test - public void testSprite() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); - assertEquals(2, ((Map) result.get("image_infos")).size()); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); - assertTrue(((String) result.get("css_url")).contains("w_100")); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); - assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); - } - - @Test - public void testMulti() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); - assertTrue(((String) result.get("url")).endsWith(".gif")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); - assertTrue(((String) result.get("url")).contains("w_100")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); - assertTrue(((String) result.get("url")).contains("w_111")); - assertTrue(((String) result.get("url")).endsWith(".pdf")); - } - - @Test - public void testTags() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - String public_id = (String)result.get("public_id"); - Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - String public_id2 = (String)result2.get("public_id"); - cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); - cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); - List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); - tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); - cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); - cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); - tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); - } - - @Test - public void testAllowedFormats() throws Exception { - //should allow whitelisted formats if allowed_formats - String[] formats = {"png"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); - assertEquals(result.get("format"), "png"); - } - - @Test - public void testAllowedFormatsWithIllegalFormat() throws Exception { - //should prevent non whitelisted formats from being uploaded if allowed_formats is specified - boolean errorFound = false; - String[] formats = {"jpg"}; - try{ - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); - } catch(Exception e) { - errorFound=true; - } - assertTrue(errorFound); - } - - @Test - public void testAllowedFormatsWithFormat() throws Exception { - //should allow non whitelisted formats if type is specified and convert to that type - String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); - assertEquals("jpg", result.get("format")); - } - - @Test - public void testFaceCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121,31,110,151); - Rectangle rect2 = new Rectangle(120,30,109,150); - coordinates.addRect(rect1); - coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); - ArrayList resultFaces = ((ArrayList) result.get("faces")); - assertEquals(2, resultFaces.size()); - - Object[] resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); - - assertEquals(rect1.x, resultCoordinates[0]); - assertEquals(rect1.y, resultCoordinates[1]); - assertEquals(rect1.width, resultCoordinates[2]); - assertEquals(rect1.height, resultCoordinates[3]); - - resultCoordinates =((ArrayList)resultFaces.get(1)).toArray(); - - assertEquals(rect2.x, resultCoordinates[0]); - assertEquals(rect2.y, resultCoordinates[1]); - assertEquals(rect2.width, resultCoordinates[2]); - assertEquals(rect2.height, resultCoordinates[3]); - - Coordinates differentCoordinates = new Coordinates(); - Rectangle rect3 = new Rectangle(122,32,111,152); - differentCoordinates.addRect(rect3); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); - - resultFaces = (ArrayList) info.get("faces"); - assertEquals(1, resultFaces.size()); - resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); - - assertEquals(rect3.x, resultCoordinates[0]); - assertEquals(rect3.y, resultCoordinates[1]); - assertEquals(rect3.width, resultCoordinates[2]); - assertEquals(rect3.height, resultCoordinates[3]); - - } - - @Test - public void testCustomCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - int[] expected = new int[]{121,31,110,151}; - Object[] actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } - - coordinates = new Coordinates(new int[]{122,32,110,152}); - cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); - result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - expected = new int[]{122,32,110,152}; - actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } - } - - @Test - public void testContext() throws Exception { - //should allow sending context - Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); - Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); - info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); - } - - @Test - public void testModerationRequest() throws Exception { - //should support requesting manual moderation - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); - assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); - } - - - @Test - public void testRawConvertRequest() { - //should support requesting raw conversion - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testCategorizationRequest() { - //should support requesting categorization - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testDetectionRequest() { - //should support requesting detection - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); - } - } - - @Test - public void testAutoTaggingRequest() { - //should support requesting auto tagging - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); - } catch(Exception e) { - assertTrue(e.getMessage().matches("^Must use(.*)")); - } - } - - @Test - public void testUploadLargeRawFiles() throws Exception { - // support uploading large raw files - Map response = cloudinary.uploader().uploadLargeRaw("src/test/resources/docx.docx", ObjectUtils.emptyMap()); - assertEquals((int)(new java.io.File("src/test/resources/docx.docx").length()), response.get("bytes")); - assertEquals(Boolean.TRUE, response.get("done")); - } - - @Test - public void testUnsignedUpload() throws Exception { - // should support unsigned uploading using presets - Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.emptyMap()); - assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); - cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); - } +public class UploaderTest extends AbstractUploaderTest { } diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml new file mode 100644 index 00000000..10777c89 --- /dev/null +++ b/cloudinary-http44/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + com.cloudinary + cloudinary-parent + 1.1.4-SNAPSHOT + + + cloudinary-http44 + jar + + Cloudinary Apache HTTP 4.4 Library + + + com.cloudinary + cloudinary-core + ${project.version} + + + org.apache.commons + commons-lang3 + 3.1 + + + org.apache.httpcomponents + httpclient + 4.4 + + + org.apache.httpcomponents + httpmime + 4.4 + + + com.cloudinary + cloudinary-test-common + ${project.version} + test + + + diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java new file mode 100644 index 00000000..b2640790 --- /dev/null +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -0,0 +1,150 @@ +package com.cloudinary.http44; + +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +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.Api; +import com.cloudinary.Uploader; +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; + +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(Cloudinary.USER_AGENT); + + // 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 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); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + + + + String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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())); + } + } + + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + + 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-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java new file mode 100644 index 00000000..f551ee19 --- /dev/null +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -0,0 +1,137 @@ +package com.cloudinary.http44; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.Map; + +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +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.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(Cloudinary.USER_AGENT); + + // 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) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + + HttpPost postMethod = new HttpPost(apiUrl); + Charset utf8 = Charset.forName("UTF-8"); + + MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); + multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + multipart.setCharset(utf8); + // 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)); + } + } else { + String value = param.getValue().toString(); + if (StringUtils.isNotBlank(value)) { + multipart.addTextBody(param.getKey(), value); + } + } + } + + if (file instanceof String && !((String) file).matches("https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) file); + } + if (file instanceof File) { + multipart.addBinaryBody("file", (File) file); + } else if (file instanceof String) { + multipart.addTextBody("file", (String) file); + } else if (file instanceof byte[]) { + multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, "file"); + } else if (file == null) { + // no-problem + } else { + throw new IOException("Uprecognized file parameter " + file); + } + postMethod.setEntity(multipart.build()); + + HttpResponse response = client.execute(postMethod); + int code = response.getStatusLine().getStatusCode(); + InputStream responseStream = response.getEntity().getContent(); + String responseData = StringUtils.read(responseStream); + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("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 (result.containsKey("error")) { + Map error = (Map) result.get("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException((String) error.get("message")); + } + } + return result; + } + +} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java new file mode 100644 index 00000000..3b7bd1e6 --- /dev/null +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java @@ -0,0 +1,23 @@ +package com.cloudinary.http44; + +import org.apache.http.client.utils.URIBuilder; + +public class UrlBuilderStrategy extends com.cloudinary.strategies.AbstractUrlBuilderStrategy { + + private URIBuilder builder; + @Override + public void addParam(String key, Object value){ + builder.addParameter(key, (String) value); + } + + @Override + public String url(){ + return builder.toString(); + } + + @Override + public void initialize() throws Exception { + this.builder = new URIBuilder(source); + } + +} 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 new file mode 100644 index 00000000..d7b77569 --- /dev/null +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java @@ -0,0 +1,69 @@ +package com.cloudinary.http44.api; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +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)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); + + 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/ApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java new file mode 100644 index 00000000..3cc4ab52 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class ApiTest extends AbstractApiTest { +} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java new file mode 100644 index 00000000..7d7d9b30 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -0,0 +1,5 @@ +package com.cloudinary.test; + +public class CloudinaryTest extends AbstractCloudinaryTest { + +} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java new file mode 100644 index 00000000..50c2a6ed --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java @@ -0,0 +1,5 @@ +package com.cloudinary.test; + +public class UploaderTest extends AbstractUploaderTest { + +} \ No newline at end of file diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml new file mode 100644 index 00000000..22da8edf --- /dev/null +++ b/cloudinary-test-common/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + + com.cloudinary + cloudinary-parent + 1.1.4-SNAPSHOT + + + cloudinary-test-common + jar + + Cloudinary Test Common + + + com.cloudinary + cloudinary-core + ${project.version} + + + junit + junit + 4.10 + + + 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 new file mode 100644 index 00000000..5529edda --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -0,0 +1,647 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.BadRequest; +import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.utils.ObjectUtils; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class AbstractApiTest { + + public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; + private Cloudinary cloudinary; + protected Api api; + private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + return; + } + Api api = cloudinary.api(); + try { + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", + Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options.put("public_id", "api_test1"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + } + + @Rule public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + + + } + + public Map findByAttr(List elements, String attr, Object value) { + for (Map element : elements) { + if (value.equals(element.get(attr))) { + return element; + } + } + return null; + } + + @Test + public void test01ResourceTypes() throws Exception { + // should allow listing resource_types + Map result = api.resourceTypes(ObjectUtils.emptyMap()); + assertContains("image", (Collection) result.get("resource_types")); + } + + @Test + public void test02Resources() throws Exception { + // should allow listing resources + Map result = api.resources(ObjectUtils.emptyMap()); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Test + public void testTimeoutParameter() throws Exception { + // should allow listing resources + Map options = new HashMap(); + options.put("timeout", Integer.valueOf(5000)); + Map result = api.resources(options); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + assertEquals(resource.get("type"), "upload"); + } + + @Test + public void test03ResourcesCursor() throws Exception { + // should allow listing resources with cursor + Map options = new HashMap(); + options.put("max_results", 1); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + assertEquals(1, resources.size()); + assertNotNull(result.get("next_cursor")); + + options.put("next_cursor", result.get("next_cursor")); + Map result2 = api.resources(options); + List resources2 = (List) result2.get("resources"); + assertNotNull(resources2); + assertEquals(resources2.size(), 1); + assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void test04ResourcesByType() throws Exception { + // should allow listing resources by type + Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + } + + @Test + public void test05ResourcesByPrefix() throws Exception { + // should allow listing resources by prefix + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void testResourcesListingDirection() throws Exception { + // should allow listing resources in both directions + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + List resources = (List) result.get("resources"); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + List resourcesDesc = (List) result.get("resources"); + Collections.reverse(resources); + assertEquals(resources, resourcesDesc); + } + + @Ignore + public void testResourcesListingStartAt() throws Exception { + // should allow listing resources by start date - make sure your clock + // is set correctly!!! + Thread.sleep(2000L); + java.util.Date startAt = new java.util.Date(); + Thread.sleep(2000L); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); + assertEquals(response.get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void testResourcesByPublicIds() throws Exception { + // should allow listing resources by public ids + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test06ResourcesTag() throws Exception { + // should allow listing resources by tag + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); + assertNotNull(resource); + List resources = (List) result.get("resources"); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test07ResourceMetadata() throws Exception { + // should allow get resource metadata + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + assertNotNull(resource); + assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("bytes"), 3381); + assertEquals(((List) resource.get("derived")).size(), 1); + } + + @Test + public void test08DeleteDerived() throws Exception { + // should allow deleting derived resource + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = (List) resource.get("derived"); + assertEquals(derived.size(), 1); + String derived_resource_id = (String) derived.get(0).get("id"); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = (List) resource.get("derived"); + assertEquals(derived.size(), 0); + } + + @Test(expected = NotFound.class) + public void test09DeleteResources() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByPrefix() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByTags() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); + } + + @Test + public void test10Tags() throws Exception { + // should allow listing tags + Map result = api.tags(ObjectUtils.emptyMap()); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + } + + @Test + public void test11TagsPrefix() throws Exception { + // should allow listing tag by prefix + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); + tags = (List) result.get("tags"); + assertEquals(0, tags.size()); + } + + @Test + public void test12Transformations() throws Exception { + // should allow listing transformations + Map result = api.transformations(ObjectUtils.emptyMap()); + Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + + assertNotNull(transformation); + assertTrue((Boolean) transformation.get("used")); + } + + @Test + public void test13TransformationMetadata() throws Exception { + // should allow getting transformation metadata + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); + } + + @Test + public void test14TransformationUpdate() throws Exception { + // should allow updating transformation allowed_for_strict + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), false); + } + + @Test + public void test15TransformationCreate() throws Exception { + // should allow creating named transformation + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test15aTransformationUnsafeUpdate() throws Exception { + // should allow unsafe update of named transformation + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), + ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test16aTransformationDelete() throws Exception { + // should allow deleting named transformation + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test16bTransformationDelete() throws Exception { + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test + public void test17aTransformationDeleteImplicit() throws Exception { + // should allow deleting implicit transformation + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + /** + * @throws Exception + * @expectedException \Cloudinary\Api\NotFound + */ + @Test(expected = NotFound.class) + public void test17bTransformationDeleteImplicit() throws Exception { + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + @Test + public void test18Usage() throws Exception { + // should support usage API call + Map result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + @Test + public void test19Ping() throws Exception { + // should support ping API call + Map result = api.ping(ObjectUtils.emptyMap()); + assertEquals(result.get("status"), "ok"); + } + + // This test must be last because it deletes (potentially) all dependent + // transformations which some tests rely on. + // Add @Test if you really want to test it - This test deletes derived + // resources! + public void testDeleteAllResources() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); + // assertEquals(0, ((org.cloudinary.json.JSONArray) + // result.get("derived")).size()); + } + + @Test + public void testManualModeration() throws Exception { + // should support setting manual moderation status + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); + } + + @Test + public void testOcrUpdate() { + // should support requesting ocr info + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testRawConvertUpdate() { + // should support requesting raw conversion + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testCategorizationUpdate() { + // should support requesting categorization + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testDetectionUpdate() { + // should support requesting detection + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testSimilaritySearchUpdate() { + // should support requesting similarity search + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testUpdateCustomCoordinates() throws IOException, Exception { + // should update custom coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + int[] expected = new int[] { 121, 31, 110, 151}; + ArrayList actual = (ArrayList) ((ArrayList)((Map) result.get("coordinates")).get("custom")).get(0); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual.get(i)); + } + } + + @Test + public void testApiLimits() throws Exception { + // should support reporting the current API limits found in the response + // header + ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); + ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); + assertNotNull(result1.apiRateLimit()); + assertNotNull(result2.apiRateLimit()); + assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); + assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); + assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); + assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); + assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); + } + + @Test + public void testListUploadPresets() throws Exception { + // should allow creating and listing upload_presets + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); + + ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); + assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); + assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); + assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } + + @Test + public void testGetUploadPreset() throws Exception { + // should allow getting a single upload_preset + String[] tags = { "a", "b", "c" }; + Map context = ObjectUtils.asMap("a", "b", "c", "d"); + Transformation transformation = new Transformation(); + transformation.width(100).crop("scale"); + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", + context)); + 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"); + Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); + assertEquals(outTransformation.get("width"), 100); + assertEquals(outTransformation.get("crop"), "scale"); + Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); + assertArrayEquals(tags, outTags); + Map outContext = (Map) settings.get("context"); + assertEquals(context, outContext); + } + + @Test + public void testDeleteUploadPreset() throws Exception { + // should allow deleting upload_presets", :upload_preset => true do + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + boolean error = false; + try { + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + error = true; + } + assertTrue(error); + } + + @Test + public void testUpdateUploadPreset() throws Exception { + // should allow updating upload_presets + 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)); + 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, preset.get("settings")); + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); + } + + @Test + public void testListByModerationUpdate() throws Exception { + // "should support listing by moderation kind and value + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); + Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); + Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); + Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); + assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); + } + + // For this test to work, "Auto-create folders" should be enabled in the + // Upload Settings. + // Uncomment @Test if you really want to test it. + // @Test + public void testFolderApi() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + Map result = api.rootFolders(null); + assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); + result = api.subFolders("test_folder1", null); + assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); + try { + api.subFolders("test_folder", null); + } catch (Exception e) { + assertTrue(e instanceof NotFound); + } + api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); + } + + private void assertContains(Object object, Collection list) { + assertTrue(list.contains(object)); + } +} diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java new file mode 100644 index 00000000..bdbfdc78 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java @@ -0,0 +1,591 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; + +abstract public class AbstractCloudinaryTest { + + private Cloudinary cloudinary; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); + } + + @Test + public void testCloudName() { + // should use cloud_name from config + String result = cloudinary.url().generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testSecureDistribution() { + // should use default secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistributionOverwrite() { + // should allow overwriting secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); + assertEquals("https://something.else.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistibution() { + // should take secure distribution from config if secure=TRUE + cloudinary.config.secureDistribution = "config.secure.distribution.com"; + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + 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"); + assertEquals("https://something.cloudfront.net/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testFormat() { + // should use format from options + String result = cloudinary.url().format("jpg").generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); + } + + @Test + public void testCrop() { + Transformation transformation = new Transformation().width(100).height(101); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); + assertEquals("101", transformation.getHtmlHeight().toString()); + assertEquals("100", transformation.getHtmlWidth().toString()); + transformation = new Transformation().width(100).height(101).crop("crop"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); + } + + @Test + public void testVariousOptions() { + // should use x, y, radius, prefix, gravity and quality from options + Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); + } + + @Test + public void testTransformationSimple() { + // should support named transformation + Transformation transformation = new Transformation().named("blip"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); + } + + @Test + public void testTransformationArray() { + // should support array of named transformations + Transformation transformation = new Transformation().named("blip", "blop"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); + } + + @Test + public void testBaseTransformations() { + // should support base transformation + Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); + } + + @Test + public void testBaseTransformationArray() { + // should support array of base transformations + Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); + } + + @Test + public void testNoEmptyTransformation() { + // should not include empty transformations + Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); + } + + @Test + 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); + } + + @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); + } + + @Test + public void testIgnoreHttp() { + // should ignore http links only if type is not given or is asset + String result = cloudinary.url().generate("http://test"); + assertEquals("http://test", result); + 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); + } + + @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); + } + + @Test + public void testCname() { + // should support external cname + String result = cloudinary.url().cname("hello.com").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"); + assertEquals("http://a2.hello.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testBackground() { + // should support background + Transformation transformation = new Transformation().background("red"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); + transformation = new Transformation().background("#112233"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); + } + + @Test + public void testDefaultImage() { + // should support default_image + Transformation transformation = new Transformation().defaultImage("default"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); + } + + @Test + public void testAngle() { + // should support angle + Transformation transformation = new Transformation().angle(12); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); + transformation = new Transformation().angle("exif", "12"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); + } + + @Test + public void testOverlay() { + // should support overlay + Transformation transformation = new Transformation().overlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); + // should not pass width/height to html if overlay + transformation = new Transformation().overlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); + } + + @Test + public void testUnderlay() { + Transformation transformation = new Transformation().underlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); + // should not pass width/height to html if underlay + transformation = new Transformation().underlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); + } + + @Test + 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); + } + + @Test + public void testEffect() { + // should support effect + Transformation transformation = new Transformation().effect("sepia"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); + } + + @Test + public void testEffectWithParam() { + // should support effect with param + Transformation transformation = new Transformation().effect("sepia", 10); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); + } + + @Test + public void testDensity() { + // should support density + Transformation transformation = new Transformation().density(150); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); + } + + @Test + public void testPage() { + // should support page + Transformation transformation = new Transformation().page(5); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); + } + + @Test + public void testBorder() { + // should support border + Transformation transformation = new Transformation().border(5, "black"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); + transformation = new Transformation().border(5, "#ffaabbdd"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); + transformation = new Transformation().border("1px_solid_blue"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); + } + + @Test + public void testFlags() { + // should support flags + Transformation transformation = new Transformation().flags("abc"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); + transformation = new Transformation().flags("abc", "def"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); + } + + @Test + public void testOpacity() { + // should support opacity + Transformation transformation = new Transformation().opacity(50); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testImageTag() { + Transformation transformation = new Transformation().width(100).height(101).crop("crop"); + String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals("my image", result); + transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); + assertEquals( + "my image", + result); + transformation = new Transformation().width("auto").crop("crop"); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); + assertEquals( + "my image", + result); + } + + @Test + public void testFolders() { + // should add version if public_id contains / + String result = cloudinary.url().generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); + result = cloudinary.url().version(123).generate("folder/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); + } + + @Test + public void testFoldersWithVersion() { + // should not add version if public_id contains version already + String result = cloudinary.url().generate("v1234/test"); + assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); + } + + @Test + public void testShorten() { + // should allow to shorted image/upload urls + String result = cloudinary.url().shorten(true).generate("test"); + assertEquals("http://res.cloudinary.com/test123/iu/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testPrivateDownload() throws Exception { + String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("img", parameters.get("public_id")); + assertEquals("jpg", parameters.get("format")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download", uri.getPath()); + } + + @SuppressWarnings("unchecked") + @Test + public void testZipDownload() throws Exception { + String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("ttag", parameters.get("tag")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); + } + + @Test + public void testSpriteCss() { + String result = cloudinary.url().generateSpriteCss("test"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + result = cloudinary.url().generateSpriteCss("test.css"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testEscapePublicId() { + // should escape public_ids + Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + for (Map.Entry entry : tests.entrySet()) { + String result = cloudinary.url().generate(entry.getKey()); + assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); + } + } + + @Test + public void testSignedUrl() { + // should correctly sign a url + String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; + String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) + .generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; + actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + + expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; + actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + } + + @Test + public void testResponsiveWidth() { + // should support responsive width + Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + String result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); + trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInSharedDistribution() { + cloudinary.url().suffix("hello").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInNonUploadTypes() { + cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithSlash() { + cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithDot() { + cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); + } + + @Test + public void testSupportUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); + assertEquals("http://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); + + } + + @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); + } + + @Test + public void testNotSignTheUrlSuffix() { + + Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); + String url = cloudinary.url().format("jpg").signed(true).generate("test"); + Matcher matcher = pattern.matcher(url); + matcher.find(); + 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); + + url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); + matcher = pattern.matcher(url); + matcher.find(); + expectedSignature = url.substring(matcher.start(), matcher.end()); + + 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); + } + + @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); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathInSharedDistribution() { + cloudinary.url().useRootPath(true).generate("test"); + } + + @Test + public void testSupportUseRootPathForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); + assertEquals("http://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); + } + + @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); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForFacebook() { + cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForRaw() { + cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); + } + + public void testUtils() { + assertEquals(ObjectUtils.asBoolean(true, null), true); + assertEquals(ObjectUtils.asBoolean(false, null), false); + } + + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { + Map params = new HashMap(); + for (String param : uri.getQuery().split("&")) { + String pair[] = param.split("="); + String key = URLDecoder.decode(pair[0], "UTF-8"); + String value = ""; + if (pair.length > 1) { + value = URLDecoder.decode(pair[1], "UTF-8"); + } + params.put(new String(key), new String(value)); + } + return params; + } +} 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 new file mode 100644 index 00000000..7e77da5e --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -0,0 +1,377 @@ +package com.cloudinary.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; + +@SuppressWarnings({"rawtypes", "unchecked"}) +abstract public class AbstractUploaderTest { + + public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; + public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; + private Cloudinary cloudinary; + + @BeforeClass + public static void setUpClass() { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + } + + @Rule public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + } + + @Test + public void testUpload() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true)); + assertEquals(result.get("width"), 241); + assertEquals(result.get("height"), 51); + assertNotNull(result.get("colors")); + assertNotNull(result.get("predominant")); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testUploadUrl() throws IOException { + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.emptyMap()); + assertEquals(result.get("width"), 241); + assertEquals(result.get("height"), 51); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testUploadDataUri() throws IOException { + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); + assertEquals(result.get("width"), 16); + assertEquals(result.get("height"), 16); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + @Test + public void testUploadUTF8() throws IOException { + Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); + assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); + } + + @Test + public void testRename() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + + cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); + + Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", ObjectUtils.emptyMap()); + boolean error_found=false; + try { + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + } catch(Exception e) { + error_found=true; + } + assertTrue(error_found); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); + assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); + } + + @Test + public void testUniqueFilename() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); + assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); + assertEquals((String) result.get("public_id"), "old_logo"); + } + @Test + public void testExplicit() throws IOException { + Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); + String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); + String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); + String cloudName = cloudinary.config.cloudName; + assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); + } + + @Test + public void testEager() throws IOException { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + } + + @Test + public void testHeaders() throws IOException { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + } + + @Test + public void testText() throws IOException { + Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); + assertTrue(((Integer) result.get("width")) > 1); + assertTrue(((Integer) result.get("height")) > 1); + } + + @Test + public void testImageUploadTag() { + String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); + assertTrue(tag.contains("type='file'")); + assertTrue(tag.contains("data-cloudinary-field='test-field'")); + assertTrue(tag.contains("class='cloudinary-fileupload'")); + assertTrue(tag.contains("htmlattr='htmlvalue'")); + tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); + assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); + } + + @Test + public void testSprite() throws IOException { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); + assertEquals(2, ((Map) result.get("image_infos")).size()); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); + assertTrue(((String) result.get("css_url")).contains("w_100")); + result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); + assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); + } + + @Test + public void testMulti() throws IOException { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); + assertTrue(((String) result.get("url")).endsWith(".gif")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); + assertTrue(((String) result.get("url")).contains("w_100")); + result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); + assertTrue(((String) result.get("url")).contains("w_111")); + assertTrue(((String) result.get("url")).endsWith(".pdf")); + } + + @Test + public void testTags() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + String public_id = (String)result.get("public_id"); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + String public_id2 = (String)result2.get("public_id"); + cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); + cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); + List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); + tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); + cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); + cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); + } + + @Test + public void testAllowedFormats() throws Exception { + //should allow whitelisted formats if allowed_formats + String[] formats = {"png"}; + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + assertEquals(result.get("format"), "png"); + } + + @Test + public void testAllowedFormatsWithIllegalFormat() throws Exception { + //should prevent non whitelisted formats from being uploaded if allowed_formats is specified + boolean errorFound = false; + String[] formats = {"jpg"}; + try{ + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + } catch(Exception e) { + errorFound=true; + } + assertTrue(errorFound); + } + + @Test + public void testAllowedFormatsWithFormat() throws Exception { + //should allow non whitelisted formats if type is specified and convert to that type + String[] formats = {"jpg"}; + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); + assertEquals("jpg", result.get("format")); + } + + @Test + public void testFaceCoordinates() throws Exception { + //should allow sending face coordinates + Coordinates coordinates = new Coordinates(); + Rectangle rect1 = new Rectangle(121,31,110,151); + Rectangle rect2 = new Rectangle(120,30,109,150); + coordinates.addRect(rect1); + coordinates.addRect(rect2); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); + ArrayList resultFaces = ((ArrayList) result.get("faces")); + assertEquals(2, resultFaces.size()); + + Object[] resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect1.x, resultCoordinates[0]); + assertEquals(rect1.y, resultCoordinates[1]); + assertEquals(rect1.width, resultCoordinates[2]); + assertEquals(rect1.height, resultCoordinates[3]); + + resultCoordinates =((ArrayList)resultFaces.get(1)).toArray(); + + assertEquals(rect2.x, resultCoordinates[0]); + assertEquals(rect2.y, resultCoordinates[1]); + assertEquals(rect2.width, resultCoordinates[2]); + assertEquals(rect2.height, resultCoordinates[3]); + + Coordinates differentCoordinates = new Coordinates(); + Rectangle rect3 = new Rectangle(122,32,111,152); + differentCoordinates.addRect(rect3); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); + + resultFaces = (ArrayList) info.get("faces"); + assertEquals(1, resultFaces.size()); + resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect3.x, resultCoordinates[0]); + assertEquals(rect3.y, resultCoordinates[1]); + assertEquals(rect3.width, resultCoordinates[2]); + assertEquals(rect3.height, resultCoordinates[3]); + + } + + @Test + public void testCustomCoordinates() throws Exception { + //should allow sending face coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + int[] expected = new int[]{121,31,110,151}; + Object[] actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++){ + assertEquals(expected[i], actual[i]); + } + + coordinates = new Coordinates(new int[]{122,32,110,152}); + cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); + result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + expected = new int[]{122,32,110,152}; + actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++){ + assertEquals(expected[i], actual[i]); + } + } + + @Test + public void testContext() throws Exception { + //should allow sending context + Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); + Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); + info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); + } + + @Test + public void testModerationRequest() throws Exception { + //should support requesting manual moderation + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); + assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); + } + + + @Test + public void testRawConvertRequest() { + //should support requesting raw conversion + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testCategorizationRequest() { + //should support requesting categorization + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testDetectionRequest() { + //should support requesting detection + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); + } catch(Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + } + } + + @Test + public void testAutoTaggingRequest() { + //should support requesting auto tagging + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); + } catch(Exception e) { + assertTrue(e.getMessage().matches("^Must use(.*)")); + } + } + + @Test + public void testUploadLargeRawFiles() throws Exception { + // support uploading large raw files + Map response = cloudinary.uploader().uploadLargeRaw("../cloudinary-test-common/src/main/resources/docx.docx", ObjectUtils.emptyMap()); + assertEquals((int)(new java.io.File("../cloudinary-test-common/src/main/resources/docx.docx").length()), response.get("bytes")); + assertEquals(Boolean.TRUE, response.get("done")); + } + + @Test + public void testUnsignedUpload() throws Exception { + // should support unsigned uploading using presets + Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); + Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.emptyMap()); + assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); + cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); + } + +} diff --git a/cloudinary-http42/src/test/resources/docx.docx b/cloudinary-test-common/src/main/resources/docx.docx similarity index 100% rename from cloudinary-http42/src/test/resources/docx.docx rename to cloudinary-test-common/src/main/resources/docx.docx diff --git a/cloudinary-http42/src/test/resources/favicon.ico b/cloudinary-test-common/src/main/resources/favicon.ico similarity index 100% rename from cloudinary-http42/src/test/resources/favicon.ico rename to cloudinary-test-common/src/main/resources/favicon.ico diff --git a/cloudinary-http42/src/test/resources/old_logo.png b/cloudinary-test-common/src/main/resources/old_logo.png similarity index 100% rename from cloudinary-http42/src/test/resources/old_logo.png rename to cloudinary-test-common/src/main/resources/old_logo.png diff --git a/pom.xml b/pom.xml index 3dfabde8..7bc4d46e 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,9 @@ cloudinary-core cloudinary-android cloudinary-taglib + cloudinary-test-common cloudinary-http42 + cloudinary-http44 cloudinary-android-test From af4c0d6d2149f8ff089db3b5eed80fff0c563796 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Tue, 31 Mar 2015 21:33:04 +0300 Subject: [PATCH 059/592] use upload_chuncked endpoint for upload large --- .../com/cloudinary/test/UploaderTest.java | 40 +++++++++- .../cloudinary/android/MultipartUtility.java | 7 +- .../cloudinary/android/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/Uploader.java | 74 +++++++++++++------ .../cloudinary/http42/UploaderStrategy.java | 5 ++ .../cloudinary/http44/UploaderStrategy.java | 13 ++-- .../cloudinary/test/AbstractUploaderTest.java | 50 +++++++++++-- 7 files changed, 157 insertions(+), 34 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index f4dbe60d..81f11e56 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -1,9 +1,12 @@ package com.cloudinary.test; +import static org.junit.Assert.assertEquals; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -29,7 +32,8 @@ public class UploaderTest extends InstrumentationTestCase { private static boolean first = true; public void setUp() throws Exception { - this.cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getInstrumentation().getContext())); + String url = Utils.cloudinaryUrlFromContext(getInstrumentation().getContext()); + this.cloudinary = new Cloudinary(url); if (first) { first = false; if (cloudinary.config.apiSecret == null) { @@ -318,4 +322,38 @@ public void testAutoTaggingRequest() { assertTrue(e.getMessage().matches("^Must use(.*)")); } } + + @SuppressWarnings("unchecked") + public void testUploadLarge() throws Exception { + // support uploading large files + if (cloudinary.config.apiSecret == null) + return; + + File temp = File.createTempFile("cldupload.test.", ""); + FileOutputStream out = new FileOutputStream(temp); + int[] header = new int[]{0x42,0x4D,0x4A,0xB9,0x59,0x00,0x00,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x05,0x00,0x00,0x78,0x05,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0xC0,0xB8,0x59,0x00,0x61,0x0F,0x00,0x00,0x61,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xB8,0x1E,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC4,0xF5,0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + byte[] byteHeader = new byte[138]; + for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; + byte[] piece = new byte[10]; + Arrays.fill(piece, (byte) 0xff); + out.write(byteHeader); + for (int i = 1; i <= 588000; i++) { + out.write(piece); + } + out.close(); + assertEquals(5880138, temp.length()); + + JSONObject resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000))); + assertEquals("raw", resource.getString("resource_type")); + + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5243000))); + assertEquals("image", resource.getString("resource_type")); + assertEquals(1400L, resource.getLong("width")); + assertEquals(1400L, resource.getLong("height")); + + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138))); + assertEquals("image", resource.getString("resource_type")); + assertEquals(1400L, resource.getLong("width")); + assertEquals(1400L, resource.getLong("height")); + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 51614414..42c9d8cf 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -39,7 +39,7 @@ public class MultipartUtility { * @param charset * @throws IOException */ - public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { + public MultipartUtility(String requestURL, String charset, String boundary, String contentRange) throws IOException { this.charset = charset; this.boundary = boundary; @@ -47,11 +47,16 @@ public MultipartUtility(String requestURL, String charset, String boundary) thro httpConn = (HttpURLConnection) url.openConnection(); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); + if (contentRange != null) httpConn.setRequestProperty("Content-Range", contentRange); httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); httpConn.setRequestProperty("User-Agent", USER_AGENT); outputStream = httpConn.getOutputStream(); writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); } + + public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { + this(requestURL, charset, boundary, null); + } /** * Adds a form field to the request diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 925e0a74..1645da3a 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -45,7 +45,7 @@ public Map callApi(String action, Map params, Map options, Objec params.put("api_key", apiKey); } String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId()); + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (String) options.get("content_range")); // Remove blank parameters for (Map.Entry param : params.entrySet()) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 522084ec..d6525002 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -61,59 +61,91 @@ public Map uploadLargeRaw(Object file, Map options) throws IOException { @SuppressWarnings("resource") public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { + Map sentOptions = new HashMap(); + sentOptions.putAll(options); + sentOptions.put("resource_type", "raw"); + return uploadLarge(file, sentOptions, bufferSize); + } + + public Map uploadLarge(Object file, Map options) throws IOException { + int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), 20000000); + return uploadLarge(file, options, bufferSize); + } + + public Map uploadLarge(Object file, Map options, int bufferSize) throws IOException { InputStream input; + long length = -1; if (file instanceof InputStream) { input = (InputStream) file; } else if (file instanceof File) { + length = ((File) file).length(); input = new FileInputStream((File) file); } else if (file instanceof byte[]) { + length = ( (byte[]) file ).length; input = new ByteArrayInputStream((byte[]) file); } else { - input = new FileInputStream(new File(file.toString())); + File f = new File(file.toString()); + length = f.length(); + input = new FileInputStream(f); } try { - Map result = uploadLargeRawParts(input, options, bufferSize); + Map result = uploadLargeParts(input, options, bufferSize, length); return result; } finally { input.close(); } } - private Map uploadLargeRawParts(InputStream input, Map options, int bufferSize) throws IOException { - Map params = ObjectUtils.only(options, "public_id", "backup", "type"); + private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length) throws IOException { + Map params = buildUploadParams(options); Map nextParams = new HashMap(); nextParams.putAll(params); Map sentParams = new HashMap(); Map sentOptions = new HashMap(); sentOptions.putAll(options); - sentOptions.put("resource_type", "raw"); byte[] buffer = new byte[bufferSize]; + byte[] nibbleBuffer = new byte[1]; int bytesRead = 0; int currentBufferSize = 0; - int partNumber = 1; - while ((bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize)) != -1) { - if (bytesRead + currentBufferSize == bufferSize) { - nextParams.put("part_number", Integer.toString(partNumber)); + int partNumber = 0; + long totalBytes = 0; + Map response = null; + while (true) { + bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize); + boolean atEnd = bytesRead == -1; + boolean fullBuffer = !atEnd && (bytesRead + currentBufferSize) == bufferSize; + if (!atEnd) currentBufferSize += bytesRead; + + if (atEnd || fullBuffer) { + totalBytes += currentBufferSize; sentParams.clear(); sentParams.putAll(nextParams); - Map response = callApi("upload_large", sentParams, sentOptions, buffer); - if (partNumber == 1) { - nextParams.put("public_id", response.get("public_id")); - nextParams.put("upload_id", response.get("upload_id")); + int currentLoc = bufferSize * partNumber; + if (!atEnd) { + //verify not on end - try read another byte + bytesRead = input.read(nibbleBuffer, 0, 1); + atEnd = bytesRead == -1; + } + if (atEnd) { + if (length == -1) length = totalBytes; + byte[] finalBuffer = new byte[currentBufferSize]; + System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); + buffer = finalBuffer; } - currentBufferSize = 0; + String range = String.format("bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); + sentOptions.put("content_range", range); + response = callApi("upload", sentParams, sentOptions, buffer); + nextParams.put("public_id", response.get("public_id")); + nextParams.put("upload_id", response.get("upload_id")); + if (atEnd) break; + buffer[0] = nibbleBuffer[0]; + currentBufferSize = 1; partNumber++; - } else { - currentBufferSize += bytesRead; } } - byte[] finalBuffer = new byte[currentBufferSize]; - System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); - nextParams.put("final", true); - nextParams.put("part_number", Integer.toString(partNumber)); - return callApi("upload_large", nextParams, sentOptions, finalBuffer); + return response; } public Map destroy(String publicId, Map options) throws IOException { diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 64042017..4c884dd6 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -59,6 +59,11 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT); + + if (options.get("content_range") != null) { + postMethod.setHeader("Content-Range", (String) options.get("content_range")); + } + Charset utf8 = Charset.forName("UTF-8"); MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index f551ee19..1210ee15 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -3,7 +3,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; import java.util.Collection; import java.util.Map; @@ -13,6 +12,7 @@ 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; @@ -70,21 +70,24 @@ public Map callApi(String action, Map params, Map options, Objec String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); HttpPost postMethod = new HttpPost(apiUrl); - Charset utf8 = Charset.forName("UTF-8"); + + if (options.get("content_range") != null) { + postMethod.setHeader("Content-Range", (String) options.get("content_range")); + } MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - multipart.setCharset(utf8); + 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)); + multipart.addTextBody(param.getKey() + "[]", ObjectUtils.asString(value), contentType); } } else { String value = param.getValue().toString(); if (StringUtils.isNotBlank(value)) { - multipart.addTextBody(param.getKey(), value); + multipart.addTextBody(param.getKey(), value, contentType); } } } 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 7e77da5e..4aa26bc6 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 @@ -4,9 +4,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; +import static org.junit.Assert.assertArrayEquals; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -358,11 +363,46 @@ public void testAutoTaggingRequest() { } @Test - public void testUploadLargeRawFiles() throws Exception { - // support uploading large raw files - Map response = cloudinary.uploader().uploadLargeRaw("../cloudinary-test-common/src/main/resources/docx.docx", ObjectUtils.emptyMap()); - assertEquals((int)(new java.io.File("../cloudinary-test-common/src/main/resources/docx.docx").length()), response.get("bytes")); - assertEquals(Boolean.TRUE, response.get("done")); + public void testUploadLarge() throws Exception { + // support uploading large files + + File temp = File.createTempFile("cldupload.test.", ""); + FileOutputStream out = new FileOutputStream(temp); + int[] header = new int[]{0x42,0x4D,0x4A,0xB9,0x59,0x00,0x00,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x05,0x00,0x00,0x78,0x05,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0xC0,0xB8,0x59,0x00,0x61,0x0F,0x00,0x00,0x61,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xB8,0x1E,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC4,0xF5,0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + byte[] byteHeader = new byte[138]; + for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; + byte[] piece = new byte[10]; + Arrays.fill(piece, (byte) 0xff); + out.write(byteHeader); + for (int i = 1; i <= 588000; i++) { + out.write(piece); + } + out.close(); + assertEquals(5880138, temp.length()); + ArrayList tags = new java.util.ArrayList(); + tags.add("upload_large_tag"); + + Map resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); + assertEquals(tags, resource.get("tags")); + assertEquals("raw", resource.get("resource_type")); + + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5243000, "tags", tags)); + assertEquals(tags, resource.get("tags")); + assertEquals("image", resource.get("resource_type")); + assertEquals(1400, resource.get("width")); + assertEquals(1400, resource.get("height")); + + resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); + assertEquals(tags, resource.get("tags")); + assertEquals("image", resource.get("resource_type")); + assertEquals(1400, resource.get("width")); + assertEquals(1400, resource.get("height")); + + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); + assertEquals(tags, resource.get("tags")); + assertEquals("image", resource.get("resource_type")); + assertEquals(1400, resource.get("width")); + assertEquals(1400, resource.get("height")); } @Test From b49b585ea81308e27342f3656dcd49d218ad3ad4 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Tue, 31 Mar 2015 21:35:22 +0300 Subject: [PATCH 060/592] stop using AbstractUrlBuilderStrategy. use URLEncoder instead --- .../main/java/com/cloudinary/Cloudinary.java | 26 ++++++++++++------- .../test/AbstractCloudinaryTest.java | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index f1eea1b2..031f9c75 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -3,6 +3,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; +import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -190,12 +191,7 @@ public String privateDownload(String publicId, String format, Map param : params.entrySet()) { - builder.addParam(param.getKey(), param.getValue().toString()); - } - return builder.url(); + return buildUrl(cloudinaryApiUrl("download", options), params); } public String zipDownload(String tag, Map options) throws Exception { @@ -211,11 +207,23 @@ public String zipDownload(String tag, Map options) throws Except } params.put("transformation", transformation); signRequest(params, options); - AbstractUrlBuilderStrategy builder= urlBuilderStrategy.init(cloudinaryApiUrl("download_tag.zip", options)); + return buildUrl(cloudinaryApiUrl("download_tag.zip", options), params); + } + + private String buildUrl(String base, Map params) throws UnsupportedEncodingException { + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(base); + if (!params.isEmpty()) { + urlBuilder.append("?"); + } + boolean first = true; for (Map.Entry param : params.entrySet()) { - builder.addParam(param.getKey(), param.getValue().toString()); + if (!first) urlBuilder.append("&"); + urlBuilder.append(param.getKey()).append("=").append( + URLEncoder.encode(param.getValue().toString(), "UTF-8")); + first = false; } - return builder.url(); + return urlBuilder.toString(); } protected Map parseConfigUrl(String cloudinaryUrl) { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java index bdbfdc78..3eafb43f 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java @@ -402,10 +402,10 @@ public void testShorten() { @SuppressWarnings("unchecked") @Test public void testPrivateDownload() throws Exception { - String url = cloudinary.privateDownload("img", "jpg", ObjectUtils.emptyMap()); + String url = cloudinary.privateDownload("imgÿ=&é", "jpg", ObjectUtils.emptyMap()); URI uri = new URI(url); Map parameters = getUrlParameters(uri); - assertEquals("img", parameters.get("public_id")); + assertEquals("imgÿ=&é", parameters.get("public_id")); assertEquals("jpg", parameters.get("format")); assertEquals("a", parameters.get("api_key")); assertEquals("/v1_1/test123/image/download", uri.getPath()); @@ -577,7 +577,7 @@ public void testUtils() { public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); - for (String param : uri.getQuery().split("&")) { + for (String param : uri.getRawQuery().split("&")) { String pair[] = param.split("="); String key = URLDecoder.decode(pair[0], "UTF-8"); String value = ""; From 5839c0be0949b2419193e55492121bdb8a8be948 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Thu, 2 Apr 2015 10:05:27 +0300 Subject: [PATCH 061/592] scrub UrlBuilderStrategy, enable crippled core mode without loading strategies, move core test to core --- .../com/cloudinary/test/CloudinaryTest.java | 489 ------------------ .../android/UrlBuilderStrategy.java | 33 -- .../main/java/com/cloudinary/Cloudinary.java | 27 +- .../java/com/cloudinary/Configuration.java | 22 +- .../AbstractUrlBuilderStrategy.java | 18 - .../com/cloudinary/test/CloudinaryTest.java | 4 +- .../cloudinary/http42/UrlBuilderStrategy.java | 23 - .../com/cloudinary/test/CloudinaryTest.java | 5 - .../cloudinary/http44/UrlBuilderStrategy.java | 23 - .../com/cloudinary/test/CloudinaryTest.java | 5 - 10 files changed, 23 insertions(+), 626 deletions(-) delete mode 100644 cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java rename cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java => cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java (99%) delete mode 100644 cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java delete mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java delete mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java deleted file mode 100644 index 62551013..00000000 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/CloudinaryTest.java +++ /dev/null @@ -1,489 +0,0 @@ -package com.cloudinary.test; - -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import android.test.AndroidTestCase; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Transformation; -import com.cloudinary.utils.ObjectUtils; - -@SuppressWarnings({ "unchecked" }) -public class CloudinaryTest extends AndroidTestCase { - - private Cloudinary cloudinary; - - public void setUp() { - this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); - } - - public void testCloudName() { - // should use cloud_name from config - String result = cloudinary.url().generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); - } - - 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); - } - - public void testSecureDistribution() { - // should use default secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); - } - - public void testSecureDistributionOverwrite() { - // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); - assertEquals("https://something.else.com/test123/image/upload/test", result); - } - - public void testSecureDistibution() { - // should take secure distribution from config if secure=TRUE - cloudinary.config.secureDistribution = "config.secure.distribution.com"; - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); - } - - 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); - } - - 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"); - assertEquals("https://something.cloudfront.net/image/upload/test", result); - } - - 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); - } - - public void testFormat() { - // should use format from options - String result = cloudinary.url().format("jpg").generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); - } - - public void testCrop() { - Transformation transformation = new Transformation().width(100).height(101); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); - assertEquals("101", transformation.getHtmlHeight().toString()); - assertEquals("100", transformation.getHtmlWidth().toString()); - transformation = new Transformation().width(100).height(101).crop("crop"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); - } - - public void testVariousOptions() { - // should use x, y, radius, prefix, gravity and quality from options - Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); - } - - public void testTransformationSimple() { - // should support named transformation - Transformation transformation = new Transformation().named("blip"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); - } - - public void testTransformationArray() { - // should support array of named transformations - Transformation transformation = new Transformation().named("blip", "blop"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); - } - - public void testBaseTransformations() { - // should support base transformation - Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); - } - - public void testBaseTransformationArray() { - // should support array of base transformations - Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop") - .width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); - } - - public void testNoEmptyTransformation() { - // should not include empty transformations - Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); - } - - 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); - } - - 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); - } - - public void testIgnoreHttp() { - // should ignore http links only if type is not given or is asset - String result = cloudinary.url().generate("http://test"); - assertEquals("http://test", result); - 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); - } - - 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); - } - - public void testCname() { - // should support extenal cname - String result = cloudinary.url().cname("hello.com").generate("test"); - assertEquals("http://hello.com/test123/image/upload/test", result); - } - - public void testCnameSubdomain() { - // should support extenal cname with cdn_subdomain on - String result = cloudinary.url().cname("hello.com").cdnSubdomain(true).generate("test"); - assertEquals("http://a2.hello.com/test123/image/upload/test", result); - } - - 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); - } - - public void testBackground() { - // should support background - Transformation transformation = new Transformation().background("red"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); - transformation = new Transformation().background("#112233"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); - } - - public void testDefaultImage() { - // should support default_image - Transformation transformation = new Transformation().defaultImage("default"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); - } - - public void testAngle() { - // should support angle - Transformation transformation = new Transformation().angle(12); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); - transformation = new Transformation().angle("exif", "12"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); - } - - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); - } - - public void testUnderlay() { - Transformation transformation = new Transformation().underlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); - } - - 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); - } - - public void testEffect() { - // should support effect - Transformation transformation = new Transformation().effect("sepia"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); - } - - public void testEffectWithParam() { - // should support effect with param - Transformation transformation = new Transformation().effect("sepia", 10); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); - } - - public void testDensity() { - // should support density - Transformation transformation = new Transformation().density(150); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); - } - - public void testPage() { - // should support page - Transformation transformation = new Transformation().page(5); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); - } - - public void testBorder() { - // should support border - Transformation transformation = new Transformation().border(5, "black"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); - transformation = new Transformation().border(5, "#ffaabbdd"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); - transformation = new Transformation().border("1px_solid_blue"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); - } - - public void testFlags() { - // should support flags - Transformation transformation = new Transformation().flags("abc"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); - transformation = new Transformation().flags("abc", "def"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); - } - - public void testImageTag() { - Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals( - "my image", - result); - } - - public void testFolders() { - // should add version if public_id contains / - String result = cloudinary.url().generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); - result = cloudinary.url().version(123).generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); - } - - public void testFoldersWithVersion() { - // should not add version if public_id contains version already - String result = cloudinary.url().generate("v1234/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); - } - - public void testShorten() { - // should allow to shorted image/upload urls - String result = cloudinary.url().shorten(true).generate("test"); - assertEquals("http://res.cloudinary.com/test123/iu/test", result); - } - - @SuppressWarnings("unchecked") - public void testEscapePublicId() { - // should escape public_ids - Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); - for (Map.Entry entry : tests.entrySet()) { - String result = cloudinary.url().generate(entry.getKey()); - assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); - } - } - - public void testRecommendedIdentifierFormat() { - String imageIdentifier = "image:upload:dfhjghjkdisudgfds7iyf.jpg"; - String[] components = imageIdentifier.split(":"); - - String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); - assertEquals("http://res.cloudinary.com/test123/image/upload/dfhjghjkdisudgfds7iyf.jpg", url); - - String rawIdentifier = "raw:upload:cguysfdsfuydsfyuds31.doc"; - components = rawIdentifier.split(":"); - - url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); - assertEquals("http://res.cloudinary.com/test123/raw/upload/cguysfdsfuydsfyuds31.doc", url); - } - - public void testSignedUrl() { - // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; - String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) - .generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; - actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - - expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; - actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - } - - public void testDisallowUrlSuffixInSharedDistribution() { - boolean thrown = false; - try { - cloudinary.url().suffix("hello").generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "URL Suffix only supported in private CDN"); - thrown = true; - } - assertTrue(thrown); - } - - public void testDisallowUrlSuffixInNonUploadTypes() { - boolean thrown = false; - try { - cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "URL Suffix only supported for image/upload and raw/upload"); - } - } - - public void testDisallowUrlSuffixWithSlash() { - boolean thrown = false; - try { - cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "url_suffix should not include . or /"); - } - } - - public void testDisallowUrlSuffixWithDot() { - boolean thrown = false; - try { - cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "url_suffix should not include . or /"); - thrown = true; - } - assertTrue(thrown); - } - - public void testSupportUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); - assertEquals("http://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); - } - - 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); - } - - public void testNotSignTheUrlSuffix() { - - Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); - String url = cloudinary.url().format("jpg").signed(true).generate("test"); - Matcher matcher = pattern.matcher(url); - matcher.find(); - 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); - - url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); - matcher = pattern.matcher(url); - matcher.find(); - expectedSignature = url.substring(matcher.start(), matcher.end()); - - 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); - } - - 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); - } - - public void testDisllowUseRootPathInSharedDistribution() { - boolean thrown = false; - try { - cloudinary.url().useRootPath(true).generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Root path only supported in private CDN"); - thrown = true; - } - assertTrue(thrown); - } - - public void testSupportUseRootPathForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); - assertEquals("http://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); - } - - public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).suffix("hello").useRootPath(true).generate("test"); - assertEquals("http://test123-res.cloudinary.com/test/hello", actual); - } - - public void testDisllowUseRootPathIfNotImageUploadForFacebook() { - boolean thrown = false; - try { - cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Root path only supported for image/upload"); - thrown = true; - } - assertTrue(thrown); - } - - public void testDisllowUseRootPathIfNotImageUploadForRaw() { - boolean thrown = false; - try { - cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); - } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "Root path only supported for image/upload"); - thrown = true; - } - assertTrue(thrown); - } - -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java deleted file mode 100644 index 4752d20f..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UrlBuilderStrategy.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cloudinary.android; - -import java.net.URISyntaxException; - -import android.net.Uri; - -import com.cloudinary.strategies.AbstractUrlBuilderStrategy; - -public class UrlBuilderStrategy extends AbstractUrlBuilderStrategy { - - private Uri.Builder builder; - - public UrlBuilderStrategy() throws URISyntaxException { - super(); - } - - @Override - public void addParam(String key, Object value) { - builder.appendQueryParameter(key, (String) value); - } - - @Override - public String url() { - return builder.toString(); - } - - @Override - public void initialize() throws Exception { - this.builder = Uri.parse(source).buildUpon(); - - } - -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 031f9c75..2220c7e6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -17,7 +17,6 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.strategies.AbstractUrlBuilderStrategy; import com.cloudinary.strategies.StrategyLoader; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -35,11 +34,6 @@ public class Cloudinary { "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy", "com.cloudinary.http44.ApiStrategy" )); - private static List URLBUILDER_STRATEGIES = new ArrayList(Arrays.asList( - "com.cloudinary.android.UrlBuilderStrategy", - "com.cloudinary.http42.UrlBuilderStrategy", - "com.cloudinary.http43.UrlBuilderStrategy", - "com.cloudinary.http44.UrlBuilderStrategy" )); public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; @@ -52,7 +46,6 @@ public class Cloudinary { public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; - private AbstractUrlBuilderStrategy urlBuilderStrategy; public Uploader uploader(){ return new Uploader(this,uploaderStrategy); @@ -76,13 +69,8 @@ public static void registerAPIStrategy(String className){ } } - public static void registerUrlBuilderStrategy(String className){ - if (!URLBUILDER_STRATEGIES.contains(className)){ - URLBUILDER_STRATEGIES.add(className); - } - } - private void loadStrategies() { + if (!this.config.loadStrategies) return; uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); if (uploaderStrategy==null){ @@ -93,33 +81,26 @@ private void loadStrategies() { if (apiStrategy==null){ throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); } - - urlBuilderStrategy= StrategyLoader.find(URLBUILDER_STRATEGIES); - if (urlBuilderStrategy==null){ - throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(URLBUILDER_STRATEGIES, ",") + "]"); - } } public Cloudinary(Map config) { - loadStrategies(); this.config = new Configuration(config); - + loadStrategies(); } public Cloudinary(String cloudinaryUrl) { - loadStrategies(); this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + loadStrategies(); } public Cloudinary() { - loadStrategies(); String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); if (cloudinaryUrl != null) { this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); }else { this.config = new Configuration(); } - + loadStrategies(); } public Url url() { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 25ad639f..dd7dc86c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -37,15 +37,16 @@ public class Configuration { public Boolean secureCdnSubdomain; public boolean useRootPath; public int timeout; + public boolean loadStrategies = true; public Configuration(){ } private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath) { - this(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, 0); + this(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, 0, true); } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout) { + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -62,6 +63,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.secureCdnSubdomain = secureCdnSubdomain; this.useRootPath = useRootPath; this.timeout = 0; + this.loadStrategies = loadStrategies; } @@ -87,6 +89,7 @@ public void update(Map config) { this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); + this.loadStrategies = ObjectUtils.asBoolean(config.get("load_strategies"), true); this.timeout = ObjectUtils.asInteger(config.get("timeout"), 0); } @@ -167,8 +170,10 @@ private static Configuration parseConfigUrl(String cloudinaryUrl) { }else if (key.equals("cdn_subdomain")){ builder.setCdnSubdomain(ObjectUtils.asBoolean(val, false)); }else if (key.equals("shorten")){ - builder.setShorten(ObjectUtils.asBoolean(val, false)); - }else { + builder.setShorten(ObjectUtils.asBoolean(val, false)); + } else if (key.equals("load_strategies")){ + builder.setLoadStrategies(ObjectUtils.asBoolean(val, true)); + } else { // Log.w("Cloudinary", "ignoring invalid parameter " + val); } @@ -197,6 +202,7 @@ public static class Builder { private int proxyPort; private Boolean secureCdnSubdomain; private boolean useRootPath; + private boolean loadStrategies = true; private int timeout; /** @@ -213,7 +219,7 @@ public Builder setTimeout(int timeout) { /** * Creates a {@link Configuration} with the arguments supplied to this builder */ - public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain,useRootPath); } + public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain,useRootPath, timeout, loadStrategies); } /** * The unique name of your cloud at Cloudinary @@ -310,6 +316,11 @@ public Builder setUseRootPath(boolean useRootPath) { return this; } + public Builder setLoadStrategies(boolean loadStrategies) { + this.loadStrategies = loadStrategies; + return this; + } + /** @@ -333,6 +344,7 @@ public Builder from(Configuration other) { this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; + this.loadStrategies = other.loadStrategies; this.timeout = other.timeout; return this; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java deleted file mode 100644 index 4ae98aea..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUrlBuilderStrategy.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.cloudinary.strategies; - - -public abstract class AbstractUrlBuilderStrategy { - protected String source; - public AbstractUrlBuilderStrategy(){ - } - - public abstract void addParam(String key, Object value); - public abstract String url(); - public abstract void initialize() throws Exception; - - public AbstractUrlBuilderStrategy init(String source) throws Exception{ - this.source = source; - initialize(); - return this; - } -} diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java similarity index 99% rename from cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java rename to cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index 3eafb43f..84b3edca 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractCloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -21,7 +21,7 @@ import com.cloudinary.Transformation; import com.cloudinary.utils.ObjectUtils; -abstract public class AbstractCloudinaryTest { +public class CloudinaryTest { private Cloudinary cloudinary; @@ -31,7 +31,7 @@ abstract public class AbstractCloudinaryTest { @Before public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123"); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); } @Test diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java deleted file mode 100644 index a56a0b8e..00000000 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UrlBuilderStrategy.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cloudinary.http42; - -import org.apache.http.client.utils.URIBuilder; - -public class UrlBuilderStrategy extends com.cloudinary.strategies.AbstractUrlBuilderStrategy { - - private URIBuilder builder; - @Override - public void addParam(String key, Object value){ - builder.addParameter(key, (String) value); - } - - @Override - public String url(){ - return builder.toString(); - } - - @Override - public void initialize() throws Exception { - this.builder = new URIBuilder(source); - } - -} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java deleted file mode 100644 index 7d7d9b30..00000000 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cloudinary.test; - -public class CloudinaryTest extends AbstractCloudinaryTest { - -} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java deleted file mode 100644 index 3b7bd1e6..00000000 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UrlBuilderStrategy.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cloudinary.http44; - -import org.apache.http.client.utils.URIBuilder; - -public class UrlBuilderStrategy extends com.cloudinary.strategies.AbstractUrlBuilderStrategy { - - private URIBuilder builder; - @Override - public void addParam(String key, Object value){ - builder.addParameter(key, (String) value); - } - - @Override - public String url(){ - return builder.toString(); - } - - @Override - public void initialize() throws Exception { - this.builder = new URIBuilder(source); - } - -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java deleted file mode 100644 index 7d7d9b30..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cloudinary.test; - -public class CloudinaryTest extends AbstractCloudinaryTest { - -} From 3882abd942f3869e701ccd8683fe281dba4b6d01 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Thu, 2 Apr 2015 12:52:44 +0300 Subject: [PATCH 062/592] video transformation parameters and zoom transformation --- .../java/com/cloudinary/Transformation.java | 262 +++++++++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 215 +++++++++++--- 2 files changed, 425 insertions(+), 52 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 99763fdf..3bf3b5e3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -6,6 +6,8 @@ import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -23,6 +25,8 @@ public class Transformation { private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); protected static Map responsiveWidthTransformation = null; + private static final Pattern RANGE_VALUE_RE = Pattern.compile("^((?:\\d+\\.)?\\d+)([%pP])?$"); + private static final Pattern RANGE_RE = Pattern.compile("^(\\d+\\.)?\\d+[%pP]?\\.\\.(\\d+\\.)?\\d+[%pP]?$"); public Transformation(Transformation transformation) { this(dup(transformation.transformations)); @@ -172,6 +176,156 @@ public Transformation dpr(int value) { public Transformation dpr(String value) { return param("dpr", value); } + + public Transformation duration(String value) { + return param("duration", value); + } + + public Transformation duration(float value) { + return param("duration", new Float(value)); + } + + public Transformation duration(double value) { + return param("duration", new Double(value)); + } + + public Transformation durationPercent(float value) { + return param("duration", new Float(value).toString() + "p"); + } + + public Transformation durationPercent(double value) { + return param("duration", new Double(value).toString() + "p"); + } + + public Transformation startOffset(String value) { + return param("start_offset", value); + } + + public Transformation startOffset(float value) { + return param("start_offset", new Float(value)); + } + + public Transformation startOffset(double value) { + return param("start_offset", new Double(value)); + } + + public Transformation startOffsetPercent(float value) { + return param("start_offset", new Float(value).toString() + "p"); + } + + public Transformation startOffsetPercent(double value) { + return param("start_offset", new Double(value).toString() + "p"); + } + + public Transformation endOffset(String value) { + return param("end_offset", value); + } + + public Transformation endOffset(float value) { + return param("end_offset", new Float(value)); + } + + public Transformation endOffset(double value) { + return param("end_offset", new Double(value)); + } + + public Transformation endOffsetPercent(float value) { + return param("end_offset", new Float(value).toString() + "p"); + } + + public Transformation endOffsetPercent(double value) { + return param("end_offset", new Double(value).toString() + "p"); + } + + public Transformation offset(String value) { + return param("offset", value); + } + + public Transformation offset(String[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + return param("offset", value); + } + + public Transformation offset(float[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + Number[] numberArray = new Number[]{value[0], value[1]}; + return offset(numberArray); + } + + public Transformation offset(double[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + Number[] numberArray = new Number[]{value[0], value[1]}; + return offset(numberArray); + } + + public Transformation offset(Number[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + return param("offset", value); + } + + public Transformation videoCodec(String value) { + return param("video_codec", value); + } + + public Transformation videoCodec(Map value) { + return param("video_codec", value); + } + + public Transformation audioCodec(String value) { + return param("audio_codec", value); + } + + public Transformation audioFrequency(String value) { + return param("audio_frequency", value); + } + + public Transformation audioFrequency(int value) { + return param("audio_frequency", value); + } + + public Transformation bitRate(String value) { + return param("bit_rate", value); + } + + public Transformation bitRate(int value) { + return param("bit_rate", new Integer(value)); + } + + public Transformation videoSampling(String value) { + return param("video_sampling", value); + } + + public Transformation videoSamplingFrames(int value) { + return param("video_sampling", value); + } + + public Transformation videoSamplingSeconds(Number value) { + return param("video_sampling", value.toString() + "s"); + } + + public Transformation videoSamplingSeconds(int value) { + return videoSamplingSeconds(new Integer(value)); + } + + public Transformation videoSamplingSeconds(float value) { + return videoSamplingSeconds(new Float(value)); + } + + public Transformation videoSamplingSeconds(double value) { + return videoSamplingSeconds(new Double(value)); + } + + public Transformation zoom(String value) { + return param("zoom", value); + } + + public Transformation zoom(float value) { + return param("zoom", new Float(value)); + } + + public Transformation zoom(double value) { + return param("zoom", new Double(value)); + } public Transformation responsiveWidth(boolean value) { return param("responsive_width", value); @@ -279,22 +433,57 @@ public String generate(Map options) { String flags = StringUtils.join(ObjectUtils.asArray(options.get("flags")), "."); + String duration = normRangeValue(options.get("duration")); + String startOffset = normRangeValue(options.get("start_offset")); + String endOffset = normRangeValue(options.get("end_offset")); + String[] offset = splitRange(options.get("offset")); + if (offset != null) { + startOffset = normRangeValue(offset[0]); + endOffset = normRangeValue(offset[1]); + } + + String videoCodec = processVideoCodecParam(options.get("video_codec")); + String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); + SortedMap params = new TreeMap(); - params.put("w", width); - params.put("h", height); - params.put("t", namedTransformation); - params.put("c", crop); + params.put("a", angle); params.put("b", background); + params.put("c", crop); params.put("co", color); - params.put("a", angle); - params.put("fl", flags); - String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); params.put("dpr", dpr); - - String[] simple_params = new String[] { "x", "x", "y", "y", "r", "radius", "d", "default_image", "g", - "gravity", "cs", "color_space", "p", "prefix", "l", "overlay", "u", "underlay", "f", "fetch_format", - "dn", "density", "pg", "page", "dl", "delay", "e", "effect", "bo", "border", "q", "quality", "o", - "opacity" }; + params.put("du", duration); + params.put("eo", endOffset); + params.put("fl", flags); + params.put("h", height); + params.put("so", startOffset); + params.put("t", namedTransformation); + params.put("vc", videoCodec); + params.put("w", width); + + String[] simple_params = new String[] { + "ac", "audio_codec", + "af", "audio_frequency", + "bo", "border", + "br", "bit_rate", + "cs", "color_space", + "d", "default_image", + "dl", "delay", + "dn", "density", + "e", "effect", + "f", "fetch_format", + "g", "gravity", + "l", "overlay", + "o", "opacity", + "p", "prefix", + "pg", "page", + "q", "quality", + "r", "radius", + "u", "underlay", + "vs", "video_sampling", + "x", "x", + "y", "y", + "z", "zoom" }; + for (int i = 0; i < simple_params.length; i += 2) { params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); } @@ -364,5 +553,54 @@ public static void setDefaultIsResponsive(boolean isResponsive) { public static void setDefaultDPR(Object dpr) { defaultDPR = dpr; } + + private static String[] splitRange(Object range) { + if (range instanceof String[] && ((String[]) range).length >= 2) { + String[] stringArrayRange = ((String[]) range); + return new String[]{stringArrayRange[0], stringArrayRange[1]}; + } else if (range instanceof Number[] && ((Number[]) range).length >= 2) { + Number[] numberArrayRange = ((Number[]) range); + return new String[]{numberArrayRange[0].toString(), numberArrayRange[1].toString()}; + } else if (range instanceof String && RANGE_RE.matcher((String) range).matches()) { + return ((String) range).split("\\.\\.", 2); + } else { + return null; + } + } + + private static String normRangeValue(Object objectValue) { + if (objectValue == null) return null; + String value = objectValue.toString(); + if (StringUtils.isEmpty(value)) return null; + + Matcher matcher = RANGE_VALUE_RE.matcher(value); + + if (!matcher.matches()) { + return null; + } + + String modifier = ""; + if (matcher.groupCount() == 2 && !StringUtils.isEmpty(matcher.group(2))) { + modifier = "p"; + } + return matcher.group(1) + modifier; + } + + private static String processVideoCodecParam(Object param) { + StringBuilder outParam = new StringBuilder(); + if (param instanceof String) { + outParam.append(param); + } if (param instanceof Map) { + Map paramMap = ( Map ) param; + outParam.append(paramMap.get("codec")); + if (paramMap.containsKey("profile")) { + outParam.append(":").append(paramMap.get("profile")); + if (paramMap.containsKey("level")) { + outParam.append(":").append(paramMap.get("level")); + } + } + } + return outParam.toString(); + } } 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 84b3edca..873405b9 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -9,6 +9,7 @@ import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,7 +23,9 @@ import com.cloudinary.utils.ObjectUtils; public class CloudinaryTest { - + private static final String DEFAULT_ROOT_PATH = "http://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; @Rule @@ -38,7 +41,7 @@ public void setUp() { public void testCloudName() { // should use cloud_name from config String result = cloudinary.url().generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "test", result); } @Test @@ -103,19 +106,19 @@ public void testHttpPrivateCdn() { public void testFormat() { // should use format from options String result = cloudinary.url().format("jpg").generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/test.jpg", result); + assertEquals(DEFAULT_UPLOAD_PATH + "test.jpg", result); } @Test public void testCrop() { Transformation transformation = new Transformation().width(100).height(101); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_101,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "h_101,w_100/test", result); assertEquals("101", transformation.getHtmlHeight().toString()); assertEquals("100", transformation.getHtmlWidth().toString()); transformation = new Transformation().width(100).height(101).crop("crop"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_101,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_101,w_100/test", result); } @Test @@ -123,7 +126,7 @@ public void testVariousOptions() { // should use x, y, radius, prefix, gravity and quality from options Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); } @Test @@ -131,7 +134,7 @@ public void testTransformationSimple() { // should support named transformation Transformation transformation = new Transformation().named("blip"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "t_blip/test", result); } @Test @@ -139,7 +142,7 @@ public void testTransformationArray() { // should support array of named transformations Transformation transformation = new Transformation().named("blip", "blop"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/t_blip.blop/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "t_blip.blop/test", result); } @Test @@ -148,7 +151,7 @@ public void testBaseTransformations() { Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/c_crop,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/c_crop,w_100/test", result); } @Test @@ -157,7 +160,7 @@ public void testBaseTransformationArray() { Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); } @Test @@ -165,7 +168,7 @@ public void testNoEmptyTransformation() { // should not include empty transformations Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_fill,x_100,y_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/test", result); } @Test @@ -226,10 +229,10 @@ public void testBackground() { // should support background Transformation transformation = new Transformation().background("red"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_red/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "b_red/test", result); transformation = new Transformation().background("#112233"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/b_rgb:112233/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "b_rgb:112233/test", result); } @Test @@ -237,7 +240,7 @@ public void testDefaultImage() { // should support default_image Transformation transformation = new Transformation().defaultImage("default"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/d_default/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "d_default/test", result); } @Test @@ -245,10 +248,10 @@ public void testAngle() { // should support angle Transformation transformation = new Transformation().angle(12); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_12/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "a_12/test", result); transformation = new Transformation().angle("exif", "12"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/a_exif.12/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "a_exif.12/test", result); } @Test @@ -256,26 +259,26 @@ public void testOverlay() { // should support overlay Transformation transformation = new Transformation().overlay("text:hello"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/l_text:hello/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); // should not pass width/height to html if overlay transformation = new Transformation().overlay("text:hello").width(100).height(100); result = cloudinary.url().transformation(transformation).generate("test"); assertNull(transformation.getHtmlHeight()); assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,l_text:hello,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); } @Test public void testUnderlay() { Transformation transformation = new Transformation().underlay("text:hello"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/u_text:hello/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "u_text:hello/test", result); // should not pass width/height to html if underlay transformation = new Transformation().underlay("text:hello").width(100).height(100); result = cloudinary.url().transformation(transformation).generate("test"); assertNull(transformation.getHtmlHeight()); assertNull(transformation.getHtmlWidth()); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_100,u_text:hello,w_100/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); } @Test @@ -290,7 +293,7 @@ public void testEffect() { // should support effect Transformation transformation = new Transformation().effect("sepia"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia/test", result); } @Test @@ -298,7 +301,7 @@ public void testEffectWithParam() { // should support effect with param Transformation transformation = new Transformation().effect("sepia", 10); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/e_sepia:10/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia:10/test", result); } @Test @@ -306,7 +309,7 @@ public void testDensity() { // should support density Transformation transformation = new Transformation().density(150); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/dn_150/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "dn_150/test", result); } @Test @@ -314,7 +317,7 @@ public void testPage() { // should support page Transformation transformation = new Transformation().page(5); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/pg_5/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "pg_5/test", result); } @Test @@ -322,13 +325,13 @@ public void testBorder() { // should support border Transformation transformation = new Transformation().border(5, "black"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_black/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_black/test", result); transformation = new Transformation().border(5, "#ffaabbdd"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_5px_solid_rgb:ffaabbdd/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_rgb:ffaabbdd/test", result); transformation = new Transformation().border("1px_solid_blue"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/bo_1px_solid_blue/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_1px_solid_blue/test", result); } @Test @@ -336,10 +339,10 @@ public void testFlags() { // should support flags Transformation transformation = new Transformation().flags("abc"); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc/test", result); transformation = new Transformation().flags("abc", "def"); result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/fl_abc.def/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc.def/test", result); } @Test @@ -347,7 +350,7 @@ public void testOpacity() { // should support opacity Transformation transformation = new Transformation().opacity(50); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/o_50/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "o_50/test", result); } @SuppressWarnings("unchecked") @@ -380,16 +383,16 @@ public void testImageTag() { public void testFolders() { // should add version if public_id contains / String result = cloudinary.url().generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1/folder/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); result = cloudinary.url().version(123).generate("folder/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v123/folder/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "v123/folder/test", result); } @Test public void testFoldersWithVersion() { // should not add version if public_id contains version already String result = cloudinary.url().generate("v1234/test"); - assertEquals("http://res.cloudinary.com/test123/image/upload/v1234/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "v1234/test", result); } @Test @@ -437,23 +440,23 @@ public void testEscapePublicId() { Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); for (Map.Entry entry : tests.entrySet()) { String result = cloudinary.url().generate(entry.getKey()); - assertEquals("http://res.cloudinary.com/test123/image/upload/" + entry.getValue(), result); + assertEquals(DEFAULT_UPLOAD_PATH + "" + entry.getValue(), result); } } @Test public void testSignedUrl() { // should correctly sign a url - String expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; + String expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) .generate("image.jpg"); assertEquals(expected, actual); - expected = "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg"; + expected = DEFAULT_UPLOAD_PATH + "s----SjmNDA--/v1234/image.jpg"; actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); assertEquals(expected, actual); - expected = "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; + expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); assertEquals(expected, actual); } @@ -464,12 +467,12 @@ public void testResponsiveWidth() { Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); String result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_limit,w_auto/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_limit,w_auto/test", result); Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); - assertEquals("http://res.cloudinary.com/test123/image/upload/c_crop,h_100,w_100/c_pad,w_auto/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_pad,w_auto/test", result); Transformation.setResponsiveWidthTransformation(null); } @@ -569,7 +572,139 @@ public void testDisllowUseRootPathIfNotImageUploadForFacebook() { public void testDisllowUseRootPathIfNotImageUploadForRaw() { cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); } - + + @Test + public void testVideoCodec() { + // should support a string value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoCodec("auto")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_auto/video_id", actual); + // should support a hash value + actual = cloudinary.url().resourceType("video") + .transformation( + new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264", "profile", "basic", "level","3.1")) + ).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); + } + + @Test + public void testAudioCodec(){ + // should support a string value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioCodec("acc")).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "ac_acc/video_id", actual); + } + + @Test + public void testBitRate() { + // should support a numeric value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate(2048)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_2048/video_id", actual); + // should support a string value + actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("44k")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_44k/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("1m")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_1m/video_id", actual); + + } + + @Test + public void testAudioFrequency() { + // should support an integer value + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().audioFrequency(44100)).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); + // should support a string value + actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioFrequency("44100")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); + } + + @Test + public void testVideoSampling() { + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().videoSamplingFrames(20)).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20s/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20.0)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20.0s/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSampling("2.3s")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_2.3s/video_id", actual); + } + + @Test + public void testStartOffset() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63p")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63%")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffsetPercent(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + } + + @Test + public void testDuration() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63p")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63%")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().durationPercent(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + } + + @Test + public void testOffset() { + + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset("2.66..3.21")).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.21,so_2.66/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new float[] { 2.67f, 3.22f })).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new double[] { 2.67, 3.22 })).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[] { "35%", "70%" })).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_70p,so_35p/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[] { "36p", "71p" })).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_71p,so_36p/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[] { "35.5p", "70.5p" })).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_70.5p,so_35.5p/video_id", actual); + + } + + @Test + public void testZoom() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom("1.5")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom(1.5)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); + } + + @Test public void testUtils() { assertEquals(ObjectUtils.asBoolean(true, null), true); assertEquals(ObjectUtils.asBoolean(false, null), false); From 4343c943054ace9e6ae7a508633a70a7f820860f Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Thu, 2 Apr 2015 13:02:30 +0300 Subject: [PATCH 063/592] support eager_async in explicit --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index d6525002..ee30ecbc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -59,7 +59,6 @@ public Map uploadLargeRaw(Object file, Map options) throws IOException { return uploadLargeRaw(file, options, 20000000); } - @SuppressWarnings("resource") public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { Map sentOptions = new HashMap(); sentOptions.putAll(options); @@ -72,6 +71,7 @@ public Map uploadLarge(Object file, Map options) throws IOException { return uploadLarge(file, options, bufferSize); } + @SuppressWarnings("resource") public Map uploadLarge(Object file, Map options, int bufferSize) throws IOException { InputStream input; long length = -1; @@ -177,6 +177,8 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("callback", (String) options.get("callback")); params.put("type", (String) options.get("type")); params.put("eager", Util.buildEager((List) options.get("eager"))); + params.put("eager_async", ObjectUtils.asBoolean(options.get("eager_async"), false).toString()); + params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("headers", Util.buildCustomHeaders(options.get("headers"))); params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); if (options.get("face_coordinates") != null) { From d6882f006e61cd98838a187229f9be3a5826eeee Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Thu, 2 Apr 2015 14:02:00 +0300 Subject: [PATCH 064/592] support ftp url upload --- .../src/main/java/com/cloudinary/android/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/http42/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/http44/UploaderStrategy.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 1645da3a..7b8d3e1e 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -60,7 +60,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } if (file instanceof File) { diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 4c884dd6..4e261cc0 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -81,7 +81,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } if (file instanceof File) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 1210ee15..b7559f35 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -92,7 +92,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } if (file instanceof File) { From bf6d9e52016665e804133357d3279ac9129c6e6f Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 6 Apr 2015 12:20:17 +0300 Subject: [PATCH 065/592] add support for video tag. refactor Url based tags --- .../main/java/com/cloudinary/StoredFile.java | 6 + .../java/com/cloudinary/Transformation.java | 6 + .../main/java/com/cloudinary/Uploader.java | 6 +- .../src/main/java/com/cloudinary/Url.java | 241 +++++++++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 174 +++++++++++++ .../cloudinary/taglib/CloudinaryImageTag.java | 158 +----------- .../com/cloudinary/taglib/CloudinaryUrl.java | 24 +- .../cloudinary/taglib/CloudinaryVideoTag.java | 111 ++++++++ .../main/resources/META-INF/cloudinary.tld | 106 ++++++++ samples/photo_album/pom.xml | 7 +- .../controllers/PhotoController.java | 8 +- .../webapp/WEB-INF/mvc-dispatcher-servlet.xml | 2 +- .../src/main/webapp/WEB-INF/pages/photos.jsp | 8 +- 13 files changed, 687 insertions(+), 170 deletions(-) create mode 100644 cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java index 957ef1f6..3f1cae8c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java +++ b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java @@ -20,6 +20,8 @@ public class StoredFile { private static final String IMAGE_RESOURCE_TYPE = "image"; + private static final String VIDEO_RESOURCE_TYPE = "video"; + private static final String AUTO_RESOURCE_TYPE = "auto"; private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); @@ -119,4 +121,8 @@ public String getComputedSignature(Cloudinary cloudinary) { public boolean getIsImage() { return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); } + + public boolean getIsVideo() { + return VIDEO_RESOURCE_TYPE.equals(resourceType); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 3bf3b5e3..ad0c4189 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -349,6 +349,12 @@ public Transformation params(Map transformation) { public Transformation chain() { return params(new HashMap()); } + + public Transformation chainWith(Transformation transformation) { + List transformations = dup(this.transformations); + transformations.addAll(dup(transformation.transformations)); + return new Transformation(transformations); + } public Transformation param(String key, Object value) { transformation.put(key, value); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index ee30ecbc..249ac32d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -351,7 +351,11 @@ public String imageUploadTag(String field, Map options, Map html StringBuilder builder = new StringBuilder(); builder.append(""); + } + + public String videoTag(String source, Map attributes) { + if (StringUtils.isEmpty(source)) + source = this.source; + if (StringUtils.isEmpty(source)) + source = publicId; + if (StringUtils.isEmpty(source)) + throw new IllegalArgumentException("must supply source or public id"); + source = VIDEO_EXTENSION_RE.matcher(source).replaceFirst(""); + + if (this.resourceType == null) this.resourceType = "video"; + attributes = new TreeMap(attributes); // Make sure they are ordered. + + String[] sourceTypes = this.sourceTypes; + + if (sourceTypes == null) { + sourceTypes = DEFAULT_VIDEO_SOURCE_TYPES; + } + + String posterUrl = this.finalizePosterUrl(source); + + if (!StringUtils.isEmpty(posterUrl)) + attributes.put("poster", posterUrl); + + StringBuilder html = new StringBuilder().append(" 1; + if (!multiSource) { + url = generate(source + "." + sourceTypes[0]); + attributes.put("src", url); + } else { + generate(source); + } + + if (this.transformation.getHtmlHeight() != null) + attributes.put("height", this.transformation.getHtmlHeight()); + if (attributes.containsKey("html_height")) + attributes.put("height", attributes.remove("html_height")); + if (this.transformation.getHtmlWidth() != null) + attributes.put("width", this.transformation.getHtmlWidth()); + if (attributes.containsKey("html_width")) + attributes.put("width", attributes.remove("html_width")); + + for (Map.Entry attr : attributes.entrySet()) { + html.append(" ").append(attr.getKey()); + if (attr.getValue() != null) { + String value = ObjectUtils.asString(attr.getValue()); + html.append("='").append(value).append("'"); + } + } + + html.append(">"); + + if (multiSource) { + for (String sourceType : sourceTypes) { + this.appendVideoSources(html, source, sourceType); + } + } + + if (this.fallbackContent != null) + html.append(this.fallbackContent); + html.append(""); + return html.toString(); + } public String generateSpriteCss(String source) { this.type = "sprite"; 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 873405b9..95f18bac 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -709,6 +709,180 @@ public void testUtils() { assertEquals(ObjectUtils.asBoolean(true, null), true); assertEquals(ObjectUtils.asBoolean(false, null), false); } + + @Test + public void testVideoTag() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + assertEquals(expectedTag, cloudinary.url().videoTag("movie", ObjectUtils.emptyMap())); + assertEquals(expectedTag, cloudinary.url().publicId("movie").videoTag()); + } + + @Test + public void testVideoTagWithAttributes() { + Map attributes = ObjectUtils.asMap( + "autoplay", true, + "controls", null, + "loop", null, + "muted", "true", + "preload", null, + "style", "border: 1px"); + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + assertEquals(expectedTag, cloudinary.url().videoTag("movie", attributes)); + } + + @Test + public void testVideoTagWithTransformation() { + Transformation transformation = new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264")) + .audioCodec("acc").startOffset(3); + String expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264/movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl); + String actualTag = cloudinary.url().transformation(transformation).sourceTypes(new String[] { "mp4" }) + .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + assertEquals(expectedTag, actualTag); + + transformation.width(250); + expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264,w_250/movie"; + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap()); + assertEquals(expectedTag, actualTag); + + transformation.crop("fit"); + expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,c_fit,so_3.0,vc_h264,w_250/movie"; + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithFallback() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String fallback = "Cannot display video"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, fallback); + String actualTag = cloudinary.url().fallbackContent(fallback).sourceTypes(new String[] { "mp4" }) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl, fallback); + actualTag = cloudinary.url().fallbackContent(fallback).videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithSourceTypes() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl); + String actualTag = cloudinary.url().sourceTypes(new String[] { "ogv", "mp4" }) + .videoTag("movie.mp4", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithSourceTransformation() { + String expectedUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/movie"; + String expectedOgvUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/q_70/movie"; + String expectedMp4Url = VIDEO_UPLOAD_PATH + "q_50/w_100/q_30/movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url, expectedOgvUrl); + String actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) + .sourceTransformationFor("mp4", new Transformation().quality(30)) + .sourceTransformationFor("ogv", new Transformation().quality(70)) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url); + actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) + .sourceTransformationFor("mp4", new Transformation().quality(30)) + .sourceTransformationFor("ogv", new Transformation().quality(70)) + .sourceTypes(new String[] { "webm", "mp4" }).videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithPoster() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String posterUrl = "http://image/somewhere.jpg"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + String actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}).poster(posterUrl) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + posterUrl = VIDEO_UPLOAD_PATH + "g_north/movie.jpg"; + expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(new Transformation().gravity("north")) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + posterUrl = DEFAULT_UPLOAD_PATH + "g_north/my_poster.jpg"; + expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(cloudinary.url() + .publicId("my_poster") + .format("jpg") + .transformation(new Transformation().gravity("north"))) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(null) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(false) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); 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 01e1320e..240a566f 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java @@ -26,71 +26,26 @@ * @author jpollak * */ -public class CloudinaryImageTag extends SimpleTagSupport implements DynamicAttributes { +public class CloudinaryImageTag extends CloudinaryUrl { private String id = null; private String extraClasses = null; - - private String src = null; - private StoredFile storedSrc = null; - - private String type = null; - private String resourceType = null; - private String format = null; - - private String transformation = null; - - private Boolean secure = null; - private Boolean cdnSubdomain = null; - private Boolean signed = null; - - private String namedTransformation = null; - - /** stores the dynamic attributes */ - private Map tagAttrs = new HashMap(); - - public void doTag() throws JspException, IOException { - Cloudinary cloudinary = Singleton.getCloudinary(); - if (cloudinary == null) { - throw new JspException("Cloudinary config could not be located"); - } - - JspWriter out = getJspContext().getOut(); - - Map attributes = new HashMap(); + + protected Map prepareAttributes() { + Map attributes = new HashMap(); if (id != null) { attributes.put("id", id); } if (extraClasses != null) { attributes.put("class", extraClasses); } - - Url url = cloudinary.url(); - if (storedSrc != null) { - url.source(storedSrc); - } else { - url.source(src); - } - Transformation baseTransformation = new Transformation().params(tagAttrs); - if (null != namedTransformation && !namedTransformation.isEmpty()) baseTransformation.named(namedTransformation); - if (null == transformation || transformation.isEmpty()) { - url.transformation(baseTransformation); - } else { - url.transformation(baseTransformation.chain().rawTransformation(transformation)); - } - if (format != null) url.format(format); - if (type != null) url.type(type); - if (resourceType != null) url.resourceType(resourceType); - - if (secure != null) { - url.secure(secure.booleanValue()); - } else if(Boolean.TRUE.equals(isSecureRequest())) { - url.secure(true); - } - if (cdnSubdomain != null) url.cdnSubdomain(cdnSubdomain.booleanValue()); - if (signed != null) url.signed(signed.booleanValue()); - - out.println(url.imageTag(attributes)); + return attributes; + } + + public void doTag() throws JspException, IOException { + JspWriter out = getJspContext().getOut(); + Url url = this.prepareUrl(); + out.println(url.imageTag(prepareAttributes())); } public void setId(String id) { @@ -109,22 +64,6 @@ public void setExtraClasses(String extraClasses) { this.extraClasses = extraClasses; } - public void setSrc(String src) { - this.src = src; - } - - public StoredFile getStoredSrc() { - return storedSrc; - } - - public void setStoredSrc(StoredFile storedSrc) { - this.storedSrc = storedSrc; - } - - public String getSrc() { - return src; - } - @Deprecated public void setPublicId(String src) { this.src = src; @@ -135,79 +74,4 @@ public String getPublicId() { return src; } - public void setFormat(String format) { - this.format = format; - } - - public String getFormat() { - return format; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getResourceType() { - return resourceType; - } - - public void setResourceType(String resourceType) { - this.resourceType = resourceType; - } - - public String getTransformation() { - return transformation; - } - - public void setTransformation(String transformation) { - this.transformation = transformation.replaceAll("\\s+","/"); - } - - public Boolean getSecure() { - return secure; - } - - public void setSecure(Boolean secure) { - this.secure = secure; - } - - public Boolean getCdnSubdomain() { - return cdnSubdomain; - } - - public void setCdnSubdomain(Boolean cdnSubdomain) { - this.cdnSubdomain = cdnSubdomain; - } - - public Boolean getSigned() { - return signed; - } - - public void setSigned(Boolean signed) { - this.signed = signed; - } - - public String getNamed() { - return namedTransformation; - } - - public void setNamed(String namedTransformation) { - this.namedTransformation = namedTransformation; - } - - @Override - public void setDynamicAttribute(String uri, String name, Object value) throws JspException { - tagAttrs.put(name, value); - } - - private Boolean isSecureRequest() { - PageContext context = (PageContext) getJspContext(); - if (context == null) return null; - ServletRequest request = context.getRequest(); - return request.getScheme().equals("https"); - } } \ No newline at end of file diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java index dcf71785..4ad4217e 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUrl.java @@ -20,7 +20,7 @@ */ public class CloudinaryUrl extends SimpleTagSupport implements DynamicAttributes { - private String src = null; + protected String src = null; private StoredFile storedSrc = null; private String type = null; @@ -39,17 +39,15 @@ public class CloudinaryUrl extends SimpleTagSupport implements DynamicAttributes private String urlSuffix = null; /** stores the dynamic attributes */ - private Map tagAttrs = new HashMap(); - - public void doTag() throws JspException, IOException { - Cloudinary cloudinary = Singleton.getCloudinary(); + protected Map tagAttrs = new HashMap(); + + protected Url prepareUrl() throws JspException { + Cloudinary cloudinary = Singleton.getCloudinary(); if (cloudinary == null) { throw new JspException("Cloudinary config could not be located"); } - - JspWriter out = getJspContext().getOut(); - - Url url = cloudinary.url(); + + Url url = cloudinary.url(); if (storedSrc != null) { url.source(storedSrc); } else { @@ -72,8 +70,12 @@ public void doTag() throws JspException, IOException { if (useRootPath != null) url.useRootPath(useRootPath); if (urlSuffix != null) url.suffix(urlSuffix); if (secureCdnSubdomain != null) url.secureCdnSubdomain(secureCdnSubdomain); - - + return url; + } + + public void doTag() throws JspException, IOException { + JspWriter out = getJspContext().getOut(); + Url url = this.prepareUrl(); out.println(url.generate()); } diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java new file mode 100644 index 00000000..9ed803ee --- /dev/null +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java @@ -0,0 +1,111 @@ +package com.cloudinary.taglib; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; + +import com.cloudinary.Transformation; +import com.cloudinary.Url; + +public class CloudinaryVideoTag extends CloudinaryImageTag { + private String sourceTypes; + private Object poster; + private Boolean autoplay; + private Boolean controls; + private Boolean loop; + private Boolean muted; + private Boolean preload; + + public Boolean getAutoplay() { + return autoplay; + } + public void setAutoplay(Boolean autoplay) { + this.autoplay = autoplay; + } + public Boolean getControls() { + return controls; + } + public void setControls(Boolean controls) { + this.controls = controls; + } + public Boolean getLoop() { + return loop; + } + public void setLoop(Boolean loop) { + this.loop = loop; + } + public Boolean getMuted() { + return muted; + } + public void setMuted(Boolean muted) { + this.muted = muted; + } + public Boolean getPreload() { + return preload; + } + public void setPreload(Boolean preload) { + this.preload = preload; + } + public Object getPoster() { + return poster; + } + public void setPoster(Object poster) { + this.poster = poster; + } + + public String getSourceTypes() { + return sourceTypes; + } + public void setSourceTypes(String sourceTypes) { + this.sourceTypes = sourceTypes; + } + + public void doTag() throws JspException, IOException { + JspWriter out = getJspContext().getOut(); + Url url = this.prepareUrl(); + + String sourceTypes[] = null; + if (this.sourceTypes != null) { + sourceTypes = this.sourceTypes.split(","); + url.sourceTypes(sourceTypes); + } + + if (this.poster != null) { + if (this.poster.equals("false")) { + url.poster(false); + } else { + url.poster(this.poster); + } + } + + Map attributes = prepareAttributes(); + + if (sourceTypes == null) sourceTypes = Url.DEFAULT_VIDEO_SOURCE_TYPES; + for (String sourceType : sourceTypes) { + String transformationAttribute = sourceType + "Transformation"; + if (this.tagAttrs.containsKey(transformationAttribute)) { + Transformation transformation = null; + Object transformationAttrValue = tagAttrs.remove(transformationAttribute); + if (transformationAttrValue instanceof Transformation) { + transformation = (Transformation) transformationAttrValue; + } else if (transformationAttrValue instanceof Map) { + transformation = new Transformation().params((Map) transformationAttrValue); + } else { + transformation = new Transformation().rawTransformation((String) transformationAttrValue); + } + url.sourceTransformationFor(sourceType, transformation ); + } + } + + if (autoplay != null) attributes.put("autoplay", autoplay.toString()); + if (controls != null) attributes.put("controls", controls.toString()); + if (loop != null) attributes.put("loop", loop.toString()); + if (muted != null) attributes.put("muted", muted.toString()); + if (preload != null) attributes.put("preload", preload.toString()); + + out.println(url.videoTag(attributes)); + } +} diff --git a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld index 67232606..8a8f260a 100644 --- a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld +++ b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld @@ -353,6 +353,112 @@ true + + video + com.cloudinary.taglib.CloudinaryVideoTag + scriptless + + id + false + true + + + extraClasses + false + true + + + src + false + true + + + storedSrc + false + true + + + publicId + false + true + + + format + false + true + + + type + false + true + + + resourceType + false + true + + + transformation + false + true + + + secure + false + true + + + cdnSubdomain + false + true + + + signed + false + true + + + named + false + true + + + sourceTypes + false + true + + + poster + false + true + + + autoplay + false + true + + + controls + false + true + + + loop + false + true + + + muted + false + true + + + preload + false + true + + true + url com.cloudinary.taglib.CloudinaryUrl diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 93283b53..20ddb348 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,7 +23,12 @@ com.cloudinary cloudinary-taglib - 1.1.0 + 1.1.4-SNAPSHOT + + + com.cloudinary + cloudinary-http44 + 1.1.4-SNAPSHOT org.springframework diff --git a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java index 16fefc3d..0f44739b 100644 --- a/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java +++ b/samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java @@ -41,7 +41,13 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); - photoUpload.setVersion((Long) uploadResult.get("version")); + Object version = uploadResult.get("version"); + if (version instanceof Integer) { + photoUpload.setVersion(new Long((Integer) version)); + } else { + photoUpload.setVersion((Long) version); + } + photoUpload.setSignature((String) uploadResult.get("signature")); photoUpload.setFormat((String) uploadResult.get("format")); photoUpload.setResourceType((String) uploadResult.get("resource_type")); diff --git a/samples/photo_album/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml b/samples/photo_album/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml index 6209a18f..7922432e 100644 --- a/samples/photo_album/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml +++ b/samples/photo_album/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml @@ -31,7 +31,7 @@ class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> - + - " target="_blank">Non Image File + + " target="_blank">Open in new Tab + + + + " target="_blank">Non Image File + From be5a22ec5948878529658e2eb61fb069de52c810 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 13 Apr 2015 16:21:54 +0300 Subject: [PATCH 066/592] support byte[] file input for upload --- .../src/main/java/com/cloudinary/android/UploaderStrategy.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 7b8d3e1e..335aff25 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -1,5 +1,6 @@ package com.cloudinary.android; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -69,6 +70,8 @@ public Map callApi(String action, Map params, Map options, Objec multipart.addFormField("file", (String) file); } else if (file instanceof InputStream) { multipart.addFilePart("file", (InputStream) file); + } else if (file instanceof byte[]) { + multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file)); } HttpURLConnection connection = multipart.execute(); int code; From dcd3f2606345107621eec8368aeda0a13e74d2ac Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 13 Apr 2015 18:28:02 +0300 Subject: [PATCH 067/592] Increment to version 1.2.0 --- CHANGES.txt | 3 ++- README.md | 8 ++++---- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 52387add..79154d50 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,4 +5,5 @@ 1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) 1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. 1.1.2 - 2015-01-15 - fix support for string eager parameters -1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration \ No newline at end of file +1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration +1.2.0 - 2015-04-13 - Support httpcomponents 4.4. Support for video tag and transformations. support ftp url upload. support eager_async in explicit. Fix UTF-8 issues in API. Improved parameter support for upload_large. \ No newline at end of file diff --git a/README.md b/README.md index b829c567..57b5f68b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. +**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.0 cloudinary-http44 is available. **Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/tree/master/cloudinary-android @@ -27,11 +27,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary - cloudinary-http42 - 1.1.3 + cloudinary-http44 + 1.2.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.1.3/cloudinary-core-1.1.3.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http42/1.1.3/cloudinary-http42-1.1.3.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.0/cloudinary-http42-1.2.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http42/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 2220c7e6..4870c468 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.1.3"; + public final static String VERSION = "1.2.0"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; From 143f36fbeeb2e878658f7f8cd5a3a858961c3a87 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 13 Apr 2015 18:34:49 +0300 Subject: [PATCH 068/592] [maven-release-plugin] prepare release cloudinary-parent-1.2.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index eb88749f..2c51de32 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 com.cloudinary cloudinary-android-test - 1.1.4-SNAPSHOT + 1.2.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.1.4-SNAPSHOT + 1.2.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 9f76b630..178dd721 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 87f38585..77c99edb 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 1ad858c3..137e493a 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-http42 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 10777c89..e2cadaf8 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index c6df9423..f8409b08 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 22da8edf..bacc3758 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 7bc4d46e..7730ef0f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.1.4-SNAPSHOT + 1.2.0 pom Cloudinary Java Client Library Parent Project From 98bec404e4754b16aea52300e8517f27d8a1ab7f Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 13 Apr 2015 18:34:55 +0300 Subject: [PATCH 069/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 2c51de32..9d3357d9 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.2.0 + 1.2.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.0 + 1.2.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 178dd721..88c1c29c 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 77c99edb..b3b92a84 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 137e493a..35342452 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index e2cadaf8..66dd2209 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index f8409b08..d222b534 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index bacc3758..a1408d31 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 7730ef0f..26e78c2c 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.0 + 1.2.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project From 0d084b4dfd852f8759be0f0d055c171888fd96b8 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 13 Apr 2015 20:17:06 +0300 Subject: [PATCH 070/592] Fix references to 1.1.4-SNAPSHOT. Fix wrong URLs in README.md --- README.md | 4 ++-- samples/photo_album/pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 57b5f68b..b418a8db 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. 1.2.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.0/cloudinary-http42-1.2.0.jar) -and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http42/pom.xml) for library dependencies. +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.0/cloudinary-http44-1.2.0.jar) +and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 20ddb348..c48fb29b 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,12 +23,12 @@ com.cloudinary cloudinary-taglib - 1.1.4-SNAPSHOT + 1.2.1-SNAPSHOT com.cloudinary cloudinary-http44 - 1.1.4-SNAPSHOT + 1.2.1-SNAPSHOT org.springframework From 58d8606f92edd2381ca6f375c4cc1102d71cbb03 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 20 May 2015 10:25:58 +0300 Subject: [PATCH 071/592] Update README.md Fixed reference to version 1.2.0 files --- cloudinary-android/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 46e77d34..2921489c 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -14,8 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-core-1.1.0.jar) and cloudinary-android-1.1.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary/1.1.0/cloudinary-android-1.1.0.jar) -and put them in your libs folder. +Download cloudinary-core-1.2.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and cloudinary-android-1.2.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.0/cloudinary-android-1.2.0.jar) and put them in your libs folder. ## Maven Integration ###################################################################### The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: @@ -23,7 +22,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.1.0 + 1.2.0 From e7f247ad0ce689cf3fc52c8ccfe250c7ad8f424d Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 16 Jun 2015 12:40:25 +0300 Subject: [PATCH 072/592] Allow android unsigned upload without api_key --- .../cloudinary/android/UploaderStrategy.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 335aff25..9cbb4db8 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -27,23 +27,24 @@ public Map callApi(String action, Map params, Map options, Objec } boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - if (Boolean.TRUE.equals(options.get("unsigned"))) { // Nothing to do - } else if (options.containsKey("signature") && options.containsKey("timestamp")) { - params.put("timestamp", options.get("timestamp")); - params.put("signature", options.get("signature")); - params.put("api_key", apiKey); } else { - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + if (options.containsKey("signature") && options.containsKey("timestamp")) { + params.put("timestamp", options.get("timestamp")); + params.put("signature", options.get("signature")); + params.put("api_key", apiKey); + } else { + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + } } String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (String) options.get("content_range")); From 34a3b6c640c9f37176152511f1511d2c03394a37 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 16 Jun 2015 12:43:38 +0300 Subject: [PATCH 073/592] Fix HTML escaping --- .../src/main/java/com/cloudinary/utils/HtmlEscape.java | 4 ++-- .../src/main/java/com/cloudinary/utils/StringUtils.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 fa40851e..e628e2fb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java @@ -26,7 +26,7 @@ public class HtmlEscape { */ public static String escapeTextArea(String original) { - return escapeSpecial(escapeTags(original)); + return escapeTags(escapeSpecial(original)); } /** @@ -36,7 +36,7 @@ public static String escapeTextArea(String original) */ public static String escape(String original) { - return escapeSpecial(escapeBr(escapeTags(original))); + return escapeBr(escapeTags(escapeSpecial(original))); } public static String escapeTags(String original) 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 29dcd37b..751820ce 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -71,7 +71,7 @@ public static String encodeHexString(byte[] bytes) { } public static String escapeHtml(String input) { - return HtmlEscape.escape(input); + return HtmlEscape.escapeTextArea(input); } public static boolean isNotBlank(Object input) { From f667d52916d3120b246467f071e105459d22c7cf Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 16 Jun 2015 22:39:54 +0300 Subject: [PATCH 074/592] Fix http44 response closing --- .../com/cloudinary/http44/ApiStrategy.java | 20 +++++++++++-------- .../cloudinary/http44/UploaderStrategy.java | 16 ++++++++++----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index b2640790..d2cf72ec 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit; import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; @@ -118,12 +118,17 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); if (code != 200 && exceptionClass == null) { @@ -146,5 +151,4 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options, Objec } postMethod.setEntity(multipart.build()); - HttpResponse response = client.execute(postMethod); - int code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - String responseData = StringUtils.read(responseStream); + 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(); + } if (code != 200 && code != 400 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); From e2ef79a18cd1d0cc67c918009cc6d4e03a97034d Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 18 Jun 2015 08:43:36 +0300 Subject: [PATCH 075/592] Disable java8 doclint --- pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pom.xml b/pom.xml index 26e78c2c..cd3f7e82 100644 --- a/pom.xml +++ b/pom.xml @@ -147,5 +147,23 @@ + + doclint-java8-disable + + [1.8,) + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + -Xdoclint:none + + + + + From 018574c13f4512211f40b34ce0c5e018b9ff6544 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 18 Jun 2015 08:48:02 +0300 Subject: [PATCH 076/592] Prepare For 1.2.1 --- CHANGES.txt | 3 ++- README.md | 6 +++--- cloudinary-android/README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 79154d50..6c1df5e2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,4 +6,5 @@ 1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. 1.1.2 - 2015-01-15 - fix support for string eager parameters 1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration -1.2.0 - 2015-04-13 - Support httpcomponents 4.4. Support for video tag and transformations. support ftp url upload. support eager_async in explicit. Fix UTF-8 issues in API. Improved parameter support for upload_large. \ No newline at end of file +1.2.0 - 2015-04-13 - Support httpcomponents 4.4. Support for video tag and transformations. support ftp url upload. support eager_async in explicit. Fix UTF-8 issues in API. Improved parameter support for upload_large. +1.2.1 - 2015-06-18 - Fix HTML escaping (fixes upload tags). Allow android unsigned upload without api_key. Fix http44 response closing. \ No newline at end of file diff --git a/README.md b/README.md index b418a8db..c112b6f7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.0 cloudinary-http44 is available. +**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.1 cloudinary-http44 is available. **Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/tree/master/cloudinary-android @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.2.0 + 1.2.1 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.0/cloudinary-http44-1.2.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.1/cloudinary-core-1.2.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.1/cloudinary-http44-1.2.1.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 2921489c..bd0c9a68 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.2.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.0/cloudinary-core-1.2.0.jar) and cloudinary-android-1.2.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.0/cloudinary-android-1.2.0.jar) and put them in your libs folder. +Download cloudinary-core-1.2.1.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.1/cloudinary-core-1.2.1.jar) and cloudinary-android-1.2.1.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.1/cloudinary-android-1.2.1.jar) and put them in your libs folder. ## Maven Integration ###################################################################### The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: @@ -22,7 +22,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.2.0 + 1.2.1 diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 4870c468..3d90ad13 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.2.0"; + public final static String VERSION = "1.2.1"; public final static String USER_AGENT = "cld-java-" + VERSION; public final Configuration config; From 33dfc3b3568003387df046487ee73b2669026c73 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 18 Jun 2015 09:02:50 +0300 Subject: [PATCH 077/592] [maven-release-plugin] prepare release cloudinary-parent-1.2.1 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 9d3357d9..f4d7500d 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 com.cloudinary cloudinary-android-test - 1.2.1-SNAPSHOT + 1.2.1 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.1-SNAPSHOT + 1.2.1 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 88c1c29c..8c894ef1 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index b3b92a84..facc7260 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 35342452..d658ad18 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-http42 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 66dd2209..4f3272cd 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index d222b534..5f1fb8f4 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index a1408d31..74a8d421 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 cloudinary-test-common diff --git a/pom.xml b/pom.xml index cd3f7e82..2f1bf949 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.1-SNAPSHOT + 1.2.1 pom Cloudinary Java Client Library Parent Project From c67e1e28f007b12575867eb03a13507d496ce0b8 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 18 Jun 2015 09:02:54 +0300 Subject: [PATCH 078/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f4d7500d..ce2126fe 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT com.cloudinary cloudinary-android-test - 1.2.1 + 1.2.2-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.1 + 1.2.2-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 8c894ef1..5354562c 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index facc7260..6bae38e3 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index d658ad18..784451ea 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 4f3272cd..a933559d 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 5f1fb8f4..7c5d1ae6 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 74a8d421..e4b7d402 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 2f1bf949..62cd99fe 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.1 + 1.2.2-SNAPSHOT pom Cloudinary Java Client Library Parent Project From e13c8ca14c94b4082a5d5b4bf0442daad4f93943 Mon Sep 17 00:00:00 2001 From: Itay Taragano Date: Sun, 5 Jul 2015 13:04:44 +0300 Subject: [PATCH 079/592] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c112b6f7..8c567095 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Assuming you have your Cloudinary configuration parameters defined (`cloud_name` The following example uploads a local JPG to the cloud: - cloudinary.uploader().upload("my_picture.jpg", Cloudinary.emptyMap()); + cloudinary.uploader().upload("my_picture.jpg", ObjectUtils.emptyMap()); The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: From a50ebe3d88a4a755c9a67ed195ef551f77380975 Mon Sep 17 00:00:00 2001 From: Wagner Tsuchiya Date: Wed, 15 Jul 2015 11:17:36 -0300 Subject: [PATCH 080/592] Fixing typo on exception --- .../src/main/java/com/cloudinary/http44/UploaderStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 48838b2c..5fbfd9ef 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -104,7 +104,7 @@ public Map callApi(String action, Map params, Map options, Objec } else if (file == null) { // no-problem } else { - throw new IOException("Uprecognized file parameter " + file); + throw new IOException("Unrecognized file parameter " + file); } postMethod.setEntity(multipart.build()); From 1693eca7a24aeaa48484a6d9526159bb57113481 Mon Sep 17 00:00:00 2001 From: itaibenari Date: Fri, 18 Sep 2015 13:32:51 +0300 Subject: [PATCH 081/592] Update README. Fixes #28 --- cloudinary-android/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index bd0c9a68..16fce6d4 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -128,7 +128,7 @@ Assuming you have your Cloudinary configuration parameters defined (`cloud_name` The following example uploads a local JPG available as an InputStream to the cloud: - cloudinary.uploader().upload(inputStream, Cloudinary.emptyMap()) + cloudinary.uploader().upload(inputStream, ObjectUtils.emptyMap()) The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: @@ -138,7 +138,7 @@ The uploaded image is assigned a randomly generated public ID. The image is imme You can also specify your own public ID: - cloudinary.uploader().upload("http://www.example.com/image.jpg", Cloudinary.asMap("public_id", "sample_remote")) + cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")) cloudinary.url().generate("sample_remote.jpg") @@ -172,7 +172,7 @@ Your server can use any Cloudinary libraries (Ruby on Rails, PHP, Python & Djang The following code uploads an image to Cloudinary with the parameters generated safely on the server side (e.g., from a JSON as in the example above): - cloudinary.uploader().upload(inputStream, Cloudinary.asMap("public_id", publicId, "signature", signature, "timestamp", timestamp, "api_key", api_key)) + cloudinary.uploader().upload(inputStream, ObjectUtils.asMap("public_id", publicId, "signature", signature, "timestamp", timestamp, "api_key", api_key)) You might want to reference uploaded Cloudinary images and raw files using an identifier string of the following format: From d0be47732bbb7cd75ac1ad819caff352675f496c Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Sun, 20 Sep 2015 20:09:35 +0300 Subject: [PATCH 082/592] support filename in upload options. close response objects in http44 --- .../java/com/cloudinary/android/MultipartUtility.java | 9 +++++++-- .../java/com/cloudinary/android/UploaderStrategy.java | 7 ++++--- .../java/com/cloudinary/http42/UploaderStrategy.java | 6 ++++-- .../src/test/java/com/cloudinary/test/ApiTest.java | 4 ++-- .../java/com/cloudinary/http44/UploaderStrategy.java | 7 +++++-- .../java/com/cloudinary/test/AbstractUploaderTest.java | 6 ++++++ 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 42c9d8cf..a7623c53 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -84,13 +84,18 @@ public void addFormField(String name, String value) { * a File to be uploaded * @throws IOException */ - public void addFilePart(String fieldName, File uploadFile) throws IOException { - String fileName = uploadFile.getName(); + public void addFilePart(String fieldName, File uploadFile, String fileName) throws IOException { + if (fileName == null) fileName = uploadFile.getName(); FileInputStream inputStream = new FileInputStream(uploadFile); addFilePart(fieldName, inputStream, fileName); } + + public void addFilePart(String fieldName, File uploadFile) throws IOException { + addFilePart(fieldName, uploadFile, "file"); + } public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { + if (fileName == null) fileName = "file"; writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED); diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 9cbb4db8..2e32055a 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -65,14 +65,15 @@ public Map callApi(String action, Map params, Map options, Objec if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } + String filename = (String) options.get("filename"); if (file instanceof File) { - multipart.addFilePart("file", (File) file); + multipart.addFilePart("file", (File) file, filename); } else if (file instanceof String) { multipart.addFormField("file", (String) file); } else if (file instanceof InputStream) { - multipart.addFilePart("file", (InputStream) file); + multipart.addFilePart("file", (InputStream) file, filename); } else if (file instanceof byte[]) { - multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file)); + multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); } HttpURLConnection connection = multipart.execute(); int code; diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 4e261cc0..d60a037a 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -84,12 +84,14 @@ public Map callApi(String action, Map params, Map options, Objec if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } + String filename = (String) options.get("filename"); if (file instanceof File) { - multipart.addPart("file", new FileBody((File) 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[]) { - multipart.addPart("file", new ByteArrayBody((byte[]) file, "file")); + if (filename == null) filename = "file"; + multipart.addPart("file", new ByteArrayBody((byte[]) file, filename)); } else if (file == null) { // no-problem } else { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index a0863cb6..0a170b59 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -6,10 +6,10 @@ import org.junit.Test; -import org.apache.http.conn.ConnectTimeoutException; +import java.net.SocketTimeoutException; public class ApiTest extends AbstractApiTest { - @Test(expected = ConnectTimeoutException.class) + @Test(expected = SocketTimeoutException.class) public void testTimeoutException() throws Exception { // should allow listing resources Map options = new HashMap(); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 5fbfd9ef..babea444 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -95,12 +95,15 @@ public Map callApi(String action, Map params, Map options, Objec if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } + String filename = (String) options.get("filename"); if (file instanceof File) { - multipart.addBinaryBody("file", (File) 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); } else if (file instanceof byte[]) { - multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, "file"); + if (filename == null) filename = "file"; + multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, filename); } else if (file == null) { // no-problem } else { 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 4aa26bc6..09bb7a87 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 @@ -413,5 +413,11 @@ public void testUnsignedUpload() throws Exception { assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } + + @Test + public void testFilenameOption() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif")); + assertEquals("emanelif", result.get("original_filename")); + } } From cae822f3c1e2d1ff08ec08ea1ab24e323b360119 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sun, 20 Sep 2015 20:59:06 +0300 Subject: [PATCH 083/592] Revent timeout exception change --- .../src/test/java/com/cloudinary/test/ApiTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 0a170b59..a0863cb6 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -6,10 +6,10 @@ import org.junit.Test; -import java.net.SocketTimeoutException; +import org.apache.http.conn.ConnectTimeoutException; public class ApiTest extends AbstractApiTest { - @Test(expected = SocketTimeoutException.class) + @Test(expected = ConnectTimeoutException.class) public void testTimeoutException() throws Exception { // should allow listing resources Map options = new HashMap(); From e6cc6c2fc49eab264ee4fb1440f4b0ee2f2a552f Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Tue, 29 Sep 2015 13:52:42 +0300 Subject: [PATCH 084/592] Fix encoding issues when JVM default encoding is not UTF-8 --- .../src/main/java/com/cloudinary/Cloudinary.java | 10 +++++++++- cloudinary-core/src/main/java/com/cloudinary/Url.java | 4 ++-- .../java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3d90ad13..597ba46e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -148,7 +148,7 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unexpected exception", e); } - byte[] digest = md.digest((to_sign + apiSecret).getBytes()); + byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); return StringUtils.encodeHexString(digest); } @@ -231,6 +231,14 @@ protected Map parseConfigUrl(String cloudinaryUrl) { return params; } + byte[] getUTF8Bytes(String string) { + try { + return string.getBytes("UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + @Deprecated public static Map asMap(Object... values) { return ObjectUtils.asMap(values); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 285726dd..07c3bdc5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -360,7 +360,7 @@ public String generate(String source) { - byte[] digest = md.digest((toSign + this.config.apiSecret).getBytes()); + byte[] digest = md.digest(cloudinary.getUTF8Bytes(toSign + this.config.apiSecret)); signature = Base64Coder.encodeURLSafeString(digest); signature = "s--" + signature.substring(0, 8) + "--" ; } @@ -484,7 +484,7 @@ public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean private String shard(String input) { CRC32 crc32 = new CRC32(); - crc32.update(input.getBytes()); + crc32.update(cloudinary.getUTF8Bytes(input)); return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); } 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 09bb7a87..c0a057a4 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 @@ -303,7 +303,7 @@ public void testCustomCoordinates() throws Exception { @Test public void testContext() throws Exception { //should allow sending context - Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + Map context = ObjectUtils.asMap("caption", "some cäption", "alt", "alternativè"); Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); From 992c1efca80a63e19bec2f28991375464b4095a3 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Sun, 27 Sep 2015 10:27:30 +0300 Subject: [PATCH 085/592] add filename and complex filename test --- .../java/com/cloudinary/test/UploaderTest.java | 14 ++++++++++++++ .../com/cloudinary/android/MultipartUtility.java | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 81f11e56..18b38e6e 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -13,6 +13,7 @@ import org.cloudinary.json.JSONArray; import org.cloudinary.json.JSONObject; +import org.junit.Test; import android.test.InstrumentationTestCase; import android.util.Log; @@ -323,6 +324,19 @@ public void testAutoTaggingRequest() { } } + @Test + public void testFilenameOption() throws Exception { + JSONObject result = new JSONObject(cloudinary.uploader().upload(TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif"))); + assertEquals("emanelif", result.getString("original_filename")); + } + + @Test + public void testComplexFilenameOption() throws Exception { + String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; + JSONObject result = new JSONObject(cloudinary.uploader().upload(TEST_IMAGE, ObjectUtils.asMap("filename", complexFilename))); + assertEquals(complexFilename, result.getString("original_filename")); + } + @SuppressWarnings("unchecked") public void testUploadLarge() throws Exception { // support uploading large files diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index a7623c53..890ee6c5 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -23,6 +23,7 @@ public class MultipartUtility { private final String boundary; private static final String LINE_FEED = "\r\n"; + private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; private HttpURLConnection httpConn; private String charset; private OutputStream outputStream; @@ -98,7 +99,7 @@ public void addFilePart(String fieldName, InputStream inputStream, String fileNa if (fileName == null) fileName = "file"; writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); - writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED); + writer.append("Content-Type: ").append(APPLICATION_OCTET_STREAM).append(LINE_FEED); writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); writer.append(LINE_FEED); writer.flush(); From d9fe66678694fe3afc162620c3e9418a1e2348de Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 09:55:36 +0300 Subject: [PATCH 086/592] support aspect ratio transformation param --- .../main/java/com/cloudinary/Transformation.java | 13 +++++++++++++ .../java/com/cloudinary/test/CloudinaryTest.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index ad0c4189..8d0000bb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -326,6 +326,18 @@ public Transformation zoom(float value) { public Transformation zoom(double value) { return param("zoom", new Double(value)); } + + public Transformation aspectRatio(double value) { + return param("aspect_ratio", new Double(value)); + } + + public Transformation aspectRatio(String value) { + return param("aspect_ratio", value); + } + + public Transformation aspectRatio(int nom, int denom) { + return aspectRatio(Integer.toString(nom) + ":" + Integer.toString(denom)); + } public Transformation responsiveWidth(boolean value) { return param("responsive_width", value); @@ -469,6 +481,7 @@ public String generate(Map options) { String[] simple_params = new String[] { "ac", "audio_codec", "af", "audio_frequency", + "ar", "aspect_ratio", "bo", "border", "br", "bit_rate", "cs", "color_space", 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 95f18bac..9c24d9d1 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -883,6 +883,19 @@ public void testVideoTagWithPoster() { assertEquals(expectedTag, actualTag); } + + @Test + public void testAspectRatio() { + String actual = cloudinary.url().transformation(new Transformation().aspectRatio("1.5")) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); + actual = cloudinary.url().transformation(new Transformation().aspectRatio(1.5)) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); + actual = cloudinary.url().transformation(new Transformation().aspectRatio(3,2)) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_3:2/test", actual); + } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); From a63b25fe2a7da27ab9fef931615b43ecbf479554 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 09:57:06 +0300 Subject: [PATCH 087/592] add invalidate flag to rename and explicit --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 249ac32d..50380c2b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -166,6 +166,7 @@ public Map rename(String fromPublicId, String toPublicId, Map options) throws IO params.put("overwrite", ObjectUtils.asBoolean(options.get("overwrite"), false).toString()); params.put("from_public_id", fromPublicId); params.put("to_public_id", toPublicId); + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); return callApi("rename", params, options, null); } @@ -190,6 +191,7 @@ public Map explicit(String publicId, Map options) throws IOException { if (options.get("context") != null) { params.put("context", ObjectUtils.encodeMap(options.get("context"))); } + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); return callApi("explicit", params, options, null); } From 3479c243ac36b838c29cc71562f80a6ca9376e60 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 10:33:46 +0300 Subject: [PATCH 088/592] normalize user agent --- .../src/main/java/com/cloudinary/android/MultipartUtility.java | 2 +- .../src/main/java/com/cloudinary/http42/ApiStrategy.java | 2 +- .../src/main/java/com/cloudinary/http42/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/http44/ApiStrategy.java | 2 +- .../src/main/java/com/cloudinary/http44/UploaderStrategy.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 890ee6c5..0456a8e8 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -29,7 +29,7 @@ public class MultipartUtility { private OutputStream outputStream; private PrintWriter writer; - public final static String USER_AGENT = "cld-android-" + Cloudinary.VERSION; + public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; /** 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 74ae2a8f..cbb51776 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -91,7 +91,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options, Objec } HttpPost postMethod = new HttpPost(apiUrl); - postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT); + postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT + " Apache HTTP Components/4.2"); if (options.get("content_range") != null) { postMethod.setHeader("Content-Range", (String) options.get("content_range")); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index d2cf72ec..365299f0 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -42,7 +42,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/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) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index babea444..0e929ace 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -35,7 +35,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/4.4"); // If the configuration specifies a proxy then apply it to the client if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { From b288c1e15ba814e048e13fb14c8d5efaaf8c1f2b Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 10:34:15 +0300 Subject: [PATCH 089/592] support the restore api --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 10 ++++++++++ .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 2 files changed, 11 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 b100681e..a45d7abc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -226,5 +226,15 @@ public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception options = ObjectUtils.emptyMap(); return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); } + + public ApiResponse restore(Iterable publicIds, 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 = new HashMap(); + params.put("public_ids", publicIds); + return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 597ba46e..b05c9f7d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -41,7 +41,7 @@ public class Cloudinary { public final static String SHARED_CDN = AKAMAI_SHARED_CDN; public final static String VERSION = "1.2.1"; - public final static String USER_AGENT = "cld-java-" + VERSION; + public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; From 0b99ab0452fc1d46a269f7738d2105dd3a7ca164 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 11:19:14 +0300 Subject: [PATCH 090/592] support upload mappings api. add missing restore test --- .../src/main/java/com/cloudinary/Api.java | 37 +++++++++++ .../com/cloudinary/test/AbstractApiTest.java | 61 +++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index a45d7abc..eaecbefc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -236,5 +236,42 @@ public ApiResponse restore(Iterable publicIds, Map options) throws Excep params.put("public_ids", publicIds); return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); } + + public ApiResponse uploadMappings(Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), + ObjectUtils.only(options, "next_cursor", "max_results"), options); + } + + public ApiResponse uploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); + } + + public ApiResponse deleteUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.DELETE, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); + } + + public ApiResponse updateUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("folder", name); + params.putAll(ObjectUtils.only(options, "template")); + return callApi(HttpMethod.PUT, Arrays.asList("upload_mappings"), params, options); + } + + public ApiResponse createUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("folder", name); + params.putAll(ObjectUtils.only(options, "template")); + return callApi(HttpMethod.POST, Arrays.asList("upload_mappings"), 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 5529edda..4c9bb2f2 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 @@ -15,6 +15,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.ListIterator; import java.util.Map; import org.junit.Before; @@ -640,6 +641,66 @@ public void testFolderApi() throws Exception { } api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); } + + @Test + public void testRestore() throws Exception { + // should support restoring resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test_restore", "backup", true)); + Map resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + api.deleteResources(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 0); + assertTrue((Boolean) resource.get("placeholder")); + Map response = api.restore(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + Map info = (Map) response.get("api_test_restore"); + assertNotNull(info); + assertEquals(info.get("bytes"), 3381); + resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + } + + @Test + public void testUploadMapping() throws Exception { + try { + api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + } catch (Exception e) { + + } + api.createUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://cloudinary.com")); + Map result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + assertEquals(result.get("template"), "http://cloudinary.com"); + api.updateUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://res.cloudinary.com")); + result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + assertEquals(result.get("template"), "http://res.cloudinary.com"); + result = api.uploadMappings(ObjectUtils.emptyMap()); + ListIterator mappings = ((ArrayList) result.get("mappings")).listIterator(); + boolean found = false; + while (mappings.hasNext()) { + Map mapping = (Map) mappings.next(); + if (mapping.get("folder").equals("api_test_upload_mapping") + && mapping.get("template").equals("http://res.cloudinary.com")) { + found = true; + break; + } + } + assertTrue(found); + api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + result = api.uploadMappings(ObjectUtils.emptyMap()); + found = false; + while (mappings.hasNext()) { + Map mapping = (Map) mappings.next(); + if (mapping.get("folder").equals("api_test_upload_mapping") + && mapping.get("template").equals("http://res.cloudinary.com")) { + found = true; + break; + } + } + assertTrue(!found); + } + + private void assertContains(Object object, Collection list) { assertTrue(list.contains(object)); From 1e348d2ea3329903969f559b78a4b17bf5b48035 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 16:49:15 +0300 Subject: [PATCH 091/592] support easy overlay/underlay construction --- .../java/com/cloudinary/Transformation.java | 15 +- .../transformation/AbstractLayerBuilder.java | 65 ++++++++ .../transformation/LayerBuilder.java | 8 + .../transformation/SubtitlesLayerBuilder.java | 7 + .../transformation/TextLayerBuilder.java | 141 ++++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 46 ++++++ 6 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 8d0000bb..9bd422c6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -9,6 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.cloudinary.transformation.AbstractLayerBuilder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -132,8 +133,16 @@ public Transformation prefix(String value) { public Transformation overlay(String value) { return param("overlay", value); } + + public Transformation overlay(AbstractLayerBuilder value) { + return param("overlay", value); + } - public Transformation underlay(String value) { + public Transformation underlay(Object value) { + return param("underlay", value); + } + + public Transformation underlay(AbstractLayerBuilder value) { return param("underlay", value); } @@ -396,8 +405,8 @@ public String generate(Map options) { } String width = this.htmlWidth = ObjectUtils.asString(options.get("width")); String height = this.htmlHeight = ObjectUtils.asString(options.get("height")); - boolean hasLayer = StringUtils.isNotBlank((String) options.get("overlay")) - || StringUtils.isNotBlank((String) options.get("underlay")); + boolean hasLayer = options.get("overlay") != null && StringUtils.isNotBlank(options.get("overlay").toString()) + || options.get("underlay") != null && StringUtils.isNotBlank(options.get("underlay").toString()); String crop = (String) options.get("crop"); String angle = StringUtils.join(ObjectUtils.asArray(options.get("angle")), "."); diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java new file mode 100644 index 00000000..fa542d2b --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java @@ -0,0 +1,65 @@ +package com.cloudinary.transformation; + +import java.util.ArrayList; + +import com.cloudinary.utils.StringUtils; + +public abstract class AbstractLayerBuilder> { + abstract SELF self(); + + protected String resourceType = null; + protected String type = null; + protected String publicId = null; + protected String format = null; + + public SELF resourceType(String resourceType) { + this.resourceType = resourceType; + return self(); + } + + public SELF type(String type) { + this.type = type; + return self(); + } + + public SELF publicId(String publicId) { + this.publicId = publicId.replace('/', ':'); + return self(); + } + + public SELF format(String format) { + this.format = format; + return self(); + } + + @Override + public String toString() { + ArrayList components = new ArrayList(); + + if (this.resourceType != null && !this.resourceType.equals("image")) { + components.add(this.resourceType); + } + + if (this.type != null && !this.type.equals("upload")) { + components.add(this.type); + } + + if (this.publicId == null) { + throw new IllegalArgumentException("Must supply publicId"); + } + + components.add(formattedPublicId()); + + return StringUtils.join(components, ":"); + } + + protected String formattedPublicId() { + String transientPublicId = this.publicId; + + if (this.format != null) { + transientPublicId = transientPublicId + "." + this.format; + } + + return transientPublicId; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java new file mode 100644 index 00000000..10ac1657 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java @@ -0,0 +1,8 @@ +package com.cloudinary.transformation; + +public class LayerBuilder extends AbstractLayerBuilder { + @Override + LayerBuilder self() { + return this; + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java new file mode 100644 index 00000000..dede6b73 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +public class SubtitlesLayerBuilder extends TextLayerBuilder { + public SubtitlesLayerBuilder() { + this.resourceType = "subtitles"; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java new file mode 100644 index 00000000..775f0c83 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java @@ -0,0 +1,141 @@ +package com.cloudinary.transformation; + +import java.util.ArrayList; + +import com.cloudinary.SmartUrlEncoder; +import com.cloudinary.utils.StringUtils; + +public class TextLayerBuilder extends AbstractLayerBuilder { + protected String resourceType = "text"; + protected String fontFamily = null; + protected Integer fontSize = null; + protected String fontWeight = null; + protected String fontStyle = null; + protected String textDecoration = null; + protected String textAlign = null; + protected String stroke = null; + protected String letterSpacing = null; + protected String text = null; + + @Override + TextLayerBuilder self() { + return this; + } + + public TextLayerBuilder resourceType(String resourceType) { + throw new UnsupportedOperationException("Cannot modify resourceType for text layers"); + } + + public TextLayerBuilder type(String type) { + throw new UnsupportedOperationException("Cannot modify type for text layers"); + } + + public TextLayerBuilder format(String format) { + throw new UnsupportedOperationException("Cannot modify format for text layers"); + } + + public TextLayerBuilder fontFamily(String fontFamily) { + this.fontFamily = fontFamily; + return self(); + } + + public TextLayerBuilder fontSize(int fontSize) { + this.fontSize = fontSize; + return self(); + } + + public TextLayerBuilder fontWeight(String fontWeight) { + this.fontWeight = fontWeight; + return self(); + } + + public TextLayerBuilder fontStyle(String fontStyle) { + this.fontStyle = fontStyle; + return self(); + } + + public TextLayerBuilder textDecoration(String textDecoration) { + this.textDecoration = textDecoration; + return self(); + } + + public TextLayerBuilder textAlign(String textAlign) { + this.textAlign = textAlign; + return self(); + } + + public TextLayerBuilder stroke(String stroke) { + this.stroke = stroke; + return self(); + } + + public TextLayerBuilder letterSpacing(String letterSpacing) { + this.letterSpacing = letterSpacing; + return self(); + } + + public TextLayerBuilder text(String text) { + this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); + return self(); + } + + @Override + public String toString() { + if (this.publicId == null && this.text == null) { + throw new IllegalArgumentException("Must supply either text or public_id."); + } + + ArrayList components = new ArrayList(); + components.add(this.resourceType); + + String styleIdentifier = textStyleIdentifier(); + if (styleIdentifier != null) { + components.add(styleIdentifier); + } + + if (this.publicId != null) { + components.add(this.formattedPublicId()); + } + + if (this.text != null) { + components.add(this.text); + } + + return StringUtils.join(components, ":"); + } + + protected String textStyleIdentifier() { + ArrayList components = new ArrayList(); + + if (this.fontWeight != null && !this.fontWeight.equals("normal")) + components.add(this.fontWeight); + if (this.fontStyle != null && !this.fontStyle.equals("normal")) + components.add(this.fontStyle); + if (this.textDecoration != null && !this.textDecoration.equals("none")) + components.add(this.textDecoration); + if (this.textAlign != null) + components.add(this.textAlign); + if (this.stroke != null && !this.stroke.equals("none")) + components.add(this.stroke); + if (this.letterSpacing != null) + components.add("letter_spacing_" + this.letterSpacing); + + if (this.fontFamily == null && this.fontSize == null && components.isEmpty()) { + return null; + } + + if (this.fontFamily == null) { + throw new IllegalArgumentException("Must supply fontFamily."); + } + + if (this.fontSize == null) { + throw new IllegalArgumentException("Must supply fontSize."); + } + + components.add(0, Integer.toString(this.fontSize)); + components.add(0, this.fontFamily); + + return StringUtils.join(components, "_"); + + } +} 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 9c24d9d1..f684cdae 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -20,6 +20,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Transformation; +import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; public class CloudinaryTest { @@ -266,6 +267,10 @@ public void testOverlay() { assertNull(transformation.getHtmlHeight()); assertNull(transformation.getHtmlWidth()); assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); + + transformation = new Transformation().overlay(new TextLayerBuilder().text("goodbye")); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); } @Test @@ -897,6 +902,47 @@ public void testAspectRatio() { assertEquals(DEFAULT_UPLOAD_PATH + "ar_3:2/test", actual); } + @Test + public void testOverlayOptions() { + Object tests[] = { + new LayerBuilder().publicId("logo"), + "logo", + new LayerBuilder().publicId("folder/logo"), + "folder:logo", + new LayerBuilder().publicId("logo").type("private"), + "private:logo", + new LayerBuilder().publicId("logo").format("png"), + "logo.png", + new LayerBuilder().resourceType("video").publicId("cat"), + "video:cat", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing("4"), + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + "subtitles:Arial_40:sample_sub_he.srt" }; + + for (int i = 0; i < tests.length; i += 2) { + Object layer = tests[i]; + String expected = (String) tests[i + 1]; + assertEquals(expected, layer.toString()); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError1() { + // Must supply font_family for text in overlay + cloudinary.url().transformation(new Transformation().overlay(new TextLayerBuilder().fontStyle("italic"))).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError2() { + // Must supply public_id for for non-text underlay + cloudinary.url().transformation(new Transformation().underlay(new LayerBuilder().resourceType("video"))).generate("test"); + } + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { From 2c7ec121a37567e6af6b27778a02a951e4874db1 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 9 Oct 2015 16:50:58 +0300 Subject: [PATCH 092/592] enable apache http 4.3 strategy --- cloudinary-http43/pom.xml | 42 +++++ .../com/cloudinary/http43/ApiStrategy.java | 154 ++++++++++++++++++ .../cloudinary/http43/UploaderStrategy.java | 149 +++++++++++++++++ .../com/cloudinary/http43/api/Response.java | 69 ++++++++ .../java/com/cloudinary/test/ApiTest.java | 4 + .../com/cloudinary/test/UploaderTest.java | 5 + pom.xml | 1 + 7 files changed, 424 insertions(+) create mode 100644 cloudinary-http43/pom.xml create mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java create mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java create mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml new file mode 100644 index 00000000..84331ddf --- /dev/null +++ b/cloudinary-http43/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + com.cloudinary + cloudinary-parent + 1.2.2-SNAPSHOT + + + cloudinary-http43 + jar + + Cloudinary Apache HTTP 4.3 Library + + + com.cloudinary + cloudinary-core + ${project.version} + + + org.apache.commons + commons-lang3 + 3.1 + + + org.apache.httpcomponents + httpclient + 4.3 + + + org.apache.httpcomponents + httpmime + 4.3 + + + com.cloudinary + cloudinary-test-common + ${project.version} + test + + + diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java new file mode 100644 index 00000000..193add9e --- /dev/null +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -0,0 +1,154 @@ +package com.cloudinary.http43; + +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpHost; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +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.Api; +import com.cloudinary.Uploader; +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; + +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(Cloudinary.USER_AGENT + " Apache HTTP Components/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 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); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + + + + String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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())); + } + } + + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + + 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); + } + } +} diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java new file mode 100644 index 00000000..d60c3c47 --- /dev/null +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -0,0 +1,149 @@ +package com.cloudinary.http43; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.Map; + +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(Cloudinary.USER_AGENT + " Apache HTTP Components/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) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + + HttpPost postMethod = new HttpPost(apiUrl); + + if (options.get("content_range") != null) { + postMethod.setHeader("Content-Range", (String) options.get("content_range")); + } + + MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); + multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) 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); + } 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(); + } + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("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 (result.containsKey("error")) { + Map error = (Map) result.get("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException((String) error.get("message")); + } + } + return result; + } + +} 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 new file mode 100644 index 00000000..67029121 --- /dev/null +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java @@ -0,0 +1,69 @@ +package com.cloudinary.http43.api; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +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)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); + + 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/ApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java new file mode 100644 index 00000000..3cc4ab52 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class ApiTest extends AbstractApiTest { +} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java new file mode 100644 index 00000000..50c2a6ed --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java @@ -0,0 +1,5 @@ +package com.cloudinary.test; + +public class UploaderTest extends AbstractUploaderTest { + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 62cd99fe..f7f33bb7 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ cloudinary-taglib cloudinary-test-common cloudinary-http42 + cloudinary-http43 cloudinary-http44 cloudinary-android-test From 0d0d47f1d030fcf950a517296f4320ff8f58e297 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sun, 11 Oct 2015 09:33:40 +0300 Subject: [PATCH 093/592] Prepare for version 1.2.2 --- CHANGES.txt | 3 ++- README.md | 6 +++--- cloudinary-android/README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- samples/photo_album/pom.xml | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6c1df5e2..8346e9d0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,4 +7,5 @@ 1.1.2 - 2015-01-15 - fix support for string eager parameters 1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration 1.2.0 - 2015-04-13 - Support httpcomponents 4.4. Support for video tag and transformations. support ftp url upload. support eager_async in explicit. Fix UTF-8 issues in API. Improved parameter support for upload_large. -1.2.1 - 2015-06-18 - Fix HTML escaping (fixes upload tags). Allow android unsigned upload without api_key. Fix http44 response closing. \ No newline at end of file +1.2.1 - 2015-06-18 - Fix HTML escaping (fixes upload tags). Allow android unsigned upload without api_key. Fix http44 response closing. +1.2.2 - 2015-10-11 - support apache http 4.3 strategy. support easy overlay/underlay construction. support upload mappings api. support the restore api. normalize user agent. add invalidate flag to rename and explicit. support aspect ratio transformation param. Fix encoding issues when JVM default encoding is not UTF-8. support filename in upload options. close response objects in http44. \ No newline at end of file diff --git a/README.md b/README.md index 8c567095..0bc7df46 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.1 cloudinary-http44 is available. +**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.1 cloudinary-http44 is available. From version 1.2.2 cloudinary-http43 is available. **Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/tree/master/cloudinary-android @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.2.1 + 1.2.2 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.1/cloudinary-core-1.2.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.1/cloudinary-http44-1.2.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.2/cloudinary-core-1.2.2.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.2/cloudinary-http44-1.2.2.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 16fce6d4..0f96d21d 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.2.1.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.1/cloudinary-core-1.2.1.jar) and cloudinary-android-1.2.1.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.1/cloudinary-android-1.2.1.jar) and put them in your libs folder. +Download cloudinary-core-1.2.2.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.2/cloudinary-core-1.2.2.jar) and cloudinary-android-1.2.2.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.2/cloudinary-android-1.2.2.jar) and put them in your libs folder. ## Maven Integration ###################################################################### The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: @@ -22,7 +22,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.2.1 + 1.2.2 diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index b05c9f7d..80fdd388 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.2.1"; + public final static String VERSION = "1.2.2"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index c48fb29b..9441acd9 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,12 +23,12 @@ com.cloudinary cloudinary-taglib - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT com.cloudinary cloudinary-http44 - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT org.springframework From 77f5f0ad24c902112d522846d811014eb79d34e9 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sun, 11 Oct 2015 10:06:10 +0300 Subject: [PATCH 094/592] Fix Android tests --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 18b38e6e..97018e76 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -326,14 +326,14 @@ public void testAutoTaggingRequest() { @Test public void testFilenameOption() throws Exception { - JSONObject result = new JSONObject(cloudinary.uploader().upload(TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif"))); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", "emanelif"))); assertEquals("emanelif", result.getString("original_filename")); } @Test public void testComplexFilenameOption() throws Exception { String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; - JSONObject result = new JSONObject(cloudinary.uploader().upload(TEST_IMAGE, ObjectUtils.asMap("filename", complexFilename))); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); assertEquals(complexFilename, result.getString("original_filename")); } From 1f9d47aecd033927d1c13de0868ed13a2554a16d Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sun, 11 Oct 2015 14:18:25 +0300 Subject: [PATCH 095/592] Fix Android complex filename test --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 97018e76..ed8f6bdc 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -6,6 +6,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URLDecoder; +import java.net.URLEncoder; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -334,6 +336,8 @@ public void testFilenameOption() throws Exception { public void testComplexFilenameOption() throws Exception { String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); + complexFilename = URLEncoder.encode(URLDecoder.decode(complexFilename, "ASCII"), "UTF-8").replace("+", " ").replace(".png", ""); + assertEquals(complexFilename, result.getString("original_filename")); } From fbfd064f626b1b8e079e54bcfb7e6c56bfb0a566 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 12 Oct 2015 11:12:14 +0300 Subject: [PATCH 096/592] [maven-release-plugin] prepare release cloudinary-parent-1.2.2 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index ce2126fe..bb71e071 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 com.cloudinary cloudinary-android-test - 1.2.2-SNAPSHOT + 1.2.2 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.2-SNAPSHOT + 1.2.2 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 5354562c..6bba7a9f 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 6bae38e3..629ff31f 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 784451ea..b5adc437 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 84331ddf..0e5665f8 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index a933559d..24c3643c 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 7c5d1ae6..fbb7c866 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index e4b7d402..b5018cac 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 cloudinary-test-common diff --git a/pom.xml b/pom.xml index f7f33bb7..101584c6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.2-SNAPSHOT + 1.2.2 pom Cloudinary Java Client Library Parent Project From 0eb24b4aafdebeec3799da4b2cedf11f203ec9ad Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 12 Oct 2015 11:12:19 +0300 Subject: [PATCH 097/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index bb71e071..28dd821f 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT com.cloudinary cloudinary-android-test - 1.2.2 + 1.2.3-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.2 + 1.2.3-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 6bba7a9f..619478b3 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 629ff31f..018b3e8e 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index b5adc437..3b3deae4 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 0e5665f8..ac72b32f 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 24c3643c..a4b90578 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index fbb7c866..676675fc 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index b5018cac..a6cd43b5 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 101584c6..2ed5800e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.2 + 1.2.3-SNAPSHOT pom Cloudinary Java Client Library Parent Project From f41ea33a90f9ec9f007b833214d6c0eaf7210d8f Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Tue, 13 Oct 2015 17:42:46 +0300 Subject: [PATCH 098/592] change user agent - remove spaces. stricter layer parameter check. fix underlay method signature --- .../src/main/java/com/cloudinary/Transformation.java | 2 +- .../cloudinary/transformation/TextLayerBuilder.java | 12 ++++++------ .../main/java/com/cloudinary/http42/ApiStrategy.java | 2 +- .../java/com/cloudinary/http42/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/http43/ApiStrategy.java | 2 +- .../java/com/cloudinary/http43/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/http44/ApiStrategy.java | 2 +- .../java/com/cloudinary/http44/UploaderStrategy.java | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 9bd422c6..3ca7c920 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -138,7 +138,7 @@ public Transformation overlay(AbstractLayerBuilder value) { return param("overlay", value); } - public Transformation underlay(Object value) { + public Transformation underlay(String value) { return param("underlay", value); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java index 775f0c83..fdbdc3de 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java @@ -107,17 +107,17 @@ public String toString() { protected String textStyleIdentifier() { ArrayList components = new ArrayList(); - if (this.fontWeight != null && !this.fontWeight.equals("normal")) + if (StringUtils.isNotBlank(this.fontWeight) && !this.fontWeight.equals("normal")) components.add(this.fontWeight); - if (this.fontStyle != null && !this.fontStyle.equals("normal")) + if (StringUtils.isNotBlank(this.fontStyle) && !this.fontStyle.equals("normal")) components.add(this.fontStyle); - if (this.textDecoration != null && !this.textDecoration.equals("none")) + if (StringUtils.isNotBlank(this.textDecoration) && !this.textDecoration.equals("none")) components.add(this.textDecoration); - if (this.textAlign != null) + if (StringUtils.isNotBlank(this.textAlign)) components.add(this.textAlign); - if (this.stroke != null && !this.stroke.equals("none")) + if (StringUtils.isNotBlank(this.stroke) && !this.stroke.equals("none")) components.add(this.stroke); - if (this.letterSpacing != null) + if (StringUtils.isNotBlank(this.letterSpacing)) components.add("letter_spacing_" + this.letterSpacing); if (this.fontFamily == null && this.fontSize == null && components.isEmpty()) { 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 cbb51776..42d20058 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -91,7 +91,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options, Objec } HttpPost postMethod = new HttpPost(apiUrl); - postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT + " Apache HTTP Components/4.2"); + postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); if (options.get("content_range") != null) { postMethod.setHeader("Content-Range", (String) options.get("content_range")); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index 193add9e..6b91e540 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -42,7 +42,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/4.3"); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " 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) { diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index d60c3c47..51cadf50 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -35,7 +35,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/4.3"); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.3"); // If the configuration specifies a proxy then apply it to the client if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index 365299f0..315521a5 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -42,7 +42,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/4.4"); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " 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) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 0e929ace..9ba820fb 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -35,7 +35,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " Apache HTTP Components/4.4"); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.4"); // If the configuration specifies a proxy then apply it to the client if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { From 80278ef88d1406a30668536dc4d8b28fe0fb804c Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 3 Nov 2015 16:15:25 +0200 Subject: [PATCH 099/592] Rename Layer classes. Rename self() to getThis() to match the pattern. * AbstractLayerBuilder -> AbstractLayer * LayerBuilder -> LayerBuilder * SubtitlesLayerBuilder -> SubtitlesLayer * TextLayerBuilder -> TextLayer --- .../java/com/cloudinary/Transformation.java | 6 +-- ...ctLayerBuilder.java => AbstractLayer.java} | 20 ++++---- .../com/cloudinary/transformation/Layer.java | 8 ++++ .../transformation/LayerBuilder.java | 8 ---- .../transformation/SubtitlesLayer.java | 7 +++ .../transformation/SubtitlesLayerBuilder.java | 7 --- .../{TextLayerBuilder.java => TextLayer.java} | 46 +++++++++---------- .../com/cloudinary/test/CloudinaryTest.java | 25 +++++----- 8 files changed, 63 insertions(+), 64 deletions(-) rename cloudinary-core/src/main/java/com/cloudinary/transformation/{AbstractLayerBuilder.java => AbstractLayer.java} (76%) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java rename cloudinary-core/src/main/java/com/cloudinary/transformation/{TextLayerBuilder.java => TextLayer.java} (76%) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 3ca7c920..fdfef064 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -9,7 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.cloudinary.transformation.AbstractLayerBuilder; +import com.cloudinary.transformation.AbstractLayer; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -134,7 +134,7 @@ public Transformation overlay(String value) { return param("overlay", value); } - public Transformation overlay(AbstractLayerBuilder value) { + public Transformation overlay(AbstractLayer value) { return param("overlay", value); } @@ -142,7 +142,7 @@ public Transformation underlay(String value) { return param("underlay", value); } - public Transformation underlay(AbstractLayerBuilder value) { + public Transformation underlay(AbstractLayer value) { return param("underlay", value); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java similarity index 76% rename from cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java rename to cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java index fa542d2b..76805d3e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java @@ -4,32 +4,32 @@ import com.cloudinary.utils.StringUtils; -public abstract class AbstractLayerBuilder> { - abstract SELF self(); +public abstract class AbstractLayer> { + abstract T getThis(); protected String resourceType = null; protected String type = null; protected String publicId = null; protected String format = null; - public SELF resourceType(String resourceType) { + public T resourceType(String resourceType) { this.resourceType = resourceType; - return self(); + return getThis(); } - public SELF type(String type) { + public T type(String type) { this.type = type; - return self(); + return getThis(); } - public SELF publicId(String publicId) { + public T publicId(String publicId) { this.publicId = publicId.replace('/', ':'); - return self(); + return getThis(); } - public SELF format(String format) { + public T format(String format) { this.format = format; - return self(); + return getThis(); } @Override diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java new file mode 100644 index 00000000..d60dfb7b --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java @@ -0,0 +1,8 @@ +package com.cloudinary.transformation; + +public class Layer extends AbstractLayer { + @Override + Layer getThis() { + return this; + } +} \ No newline at end of file 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 10ac1657..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.cloudinary.transformation; - -public class LayerBuilder extends AbstractLayerBuilder { - @Override - LayerBuilder self() { - return this; - } -} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java new file mode 100644 index 00000000..6278da0b --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +public class SubtitlesLayer extends TextLayer { + public SubtitlesLayer() { + this.resourceType = "subtitles"; + } +} 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 dede6b73..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.transformation; - -public class SubtitlesLayerBuilder extends TextLayerBuilder { - public SubtitlesLayerBuilder() { - this.resourceType = "subtitles"; - } -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java similarity index 76% rename from cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java rename to cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index fdbdc3de..dca000c4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -5,7 +5,7 @@ import com.cloudinary.SmartUrlEncoder; import com.cloudinary.utils.StringUtils; -public class TextLayerBuilder extends AbstractLayerBuilder { +public class TextLayer extends AbstractLayer { protected String resourceType = "text"; protected String fontFamily = null; protected Integer fontSize = null; @@ -18,65 +18,65 @@ public class TextLayerBuilder extends AbstractLayerBuilder { protected String text = null; @Override - TextLayerBuilder self() { + TextLayer getThis() { return this; } - public TextLayerBuilder resourceType(String resourceType) { + public TextLayer resourceType(String resourceType) { throw new UnsupportedOperationException("Cannot modify resourceType for text layers"); } - public TextLayerBuilder type(String type) { + public TextLayer type(String type) { throw new UnsupportedOperationException("Cannot modify type for text layers"); } - public TextLayerBuilder format(String format) { + public TextLayer format(String format) { throw new UnsupportedOperationException("Cannot modify format for text layers"); } - public TextLayerBuilder fontFamily(String fontFamily) { + public TextLayer fontFamily(String fontFamily) { this.fontFamily = fontFamily; - return self(); + return getThis(); } - public TextLayerBuilder fontSize(int fontSize) { + public TextLayer fontSize(int fontSize) { this.fontSize = fontSize; - return self(); + return getThis(); } - public TextLayerBuilder fontWeight(String fontWeight) { + public TextLayer fontWeight(String fontWeight) { this.fontWeight = fontWeight; - return self(); + return getThis(); } - public TextLayerBuilder fontStyle(String fontStyle) { + public TextLayer fontStyle(String fontStyle) { this.fontStyle = fontStyle; - return self(); + return getThis(); } - public TextLayerBuilder textDecoration(String textDecoration) { + public TextLayer textDecoration(String textDecoration) { this.textDecoration = textDecoration; - return self(); + return getThis(); } - public TextLayerBuilder textAlign(String textAlign) { + public TextLayer textAlign(String textAlign) { this.textAlign = textAlign; - return self(); + return getThis(); } - public TextLayerBuilder stroke(String stroke) { + public TextLayer stroke(String stroke) { this.stroke = stroke; - return self(); + return getThis(); } - public TextLayerBuilder letterSpacing(String letterSpacing) { + public TextLayer letterSpacing(String letterSpacing) { this.letterSpacing = letterSpacing; - return self(); + return getThis(); } - public TextLayerBuilder text(String text) { + public TextLayer text(String text) { this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); - return self(); + return getThis(); } @Override 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 f684cdae..05cd259b 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -9,7 +9,6 @@ import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -268,7 +267,7 @@ public void testOverlay() { assertNull(transformation.getHtmlWidth()); assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); - transformation = new Transformation().overlay(new TextLayerBuilder().text("goodbye")); + transformation = new Transformation().overlay(new TextLayer().text("goodbye")); result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); } @@ -905,23 +904,23 @@ public void testAspectRatio() { @Test public void testOverlayOptions() { Object tests[] = { - new LayerBuilder().publicId("logo"), + new Layer().publicId("logo"), "logo", - new LayerBuilder().publicId("folder/logo"), + new Layer().publicId("folder/logo"), "folder:logo", - new LayerBuilder().publicId("logo").type("private"), + new Layer().publicId("logo").type("private"), "private:logo", - new LayerBuilder().publicId("logo").format("png"), + new Layer().publicId("logo").format("png"), "logo.png", - new LayerBuilder().resourceType("video").publicId("cat"), + new Layer().resourceType("video").publicId("cat"), "video:cat", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4"), "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", - new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt" }; for (int i = 0; i < tests.length; i += 2) { @@ -934,13 +933,13 @@ public void testOverlayOptions() { @Test(expected = IllegalArgumentException.class) public void testOverlayError1() { // Must supply font_family for text in overlay - cloudinary.url().transformation(new Transformation().overlay(new TextLayerBuilder().fontStyle("italic"))).generate("test"); + cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); } @Test(expected = IllegalArgumentException.class) public void testOverlayError2() { // Must supply public_id for for non-text underlay - cloudinary.url().transformation(new Transformation().underlay(new LayerBuilder().resourceType("video"))).generate("test"); + cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { From 78c5a929dce3c02d42497060a4998c167de629a0 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 3 Nov 2015 17:20:17 +0200 Subject: [PATCH 100/592] Create separate test class for Layer --- .../com/cloudinary/test/CloudinaryTest.java | 72 ---------- .../cloudinary/transformation/LayerTest.java | 132 ++++++++++++++++++ 2 files changed, 132 insertions(+), 72 deletions(-) create mode 100644 cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java 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 05cd259b..50a7fd91 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -254,37 +254,6 @@ public void testAngle() { assertEquals(DEFAULT_UPLOAD_PATH + "a_exif.12/test", result); } - @Test - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); - - transformation = new Transformation().overlay(new TextLayer().text("goodbye")); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); - } - - @Test - 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); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); - } - @Test public void testFetchFormat() { // should support format for fetch urls @@ -901,47 +870,6 @@ public void testAspectRatio() { assertEquals(DEFAULT_UPLOAD_PATH + "ar_3:2/test", actual); } - @Test - public void testOverlayOptions() { - Object tests[] = { - new Layer().publicId("logo"), - "logo", - new Layer().publicId("folder/logo"), - "folder:logo", - new Layer().publicId("logo").type("private"), - "private:logo", - new Layer().publicId("logo").format("png"), - "logo.png", - new Layer().resourceType("video").publicId("cat"), - "video:cat", - new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) - .fontWeight("bold").fontStyle("italic").letterSpacing("4"), - "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", - new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), - "subtitles:Arial_40:sample_sub_he.srt" }; - - for (int i = 0; i < tests.length; i += 2) { - Object layer = tests[i]; - String expected = (String) tests[i + 1]; - assertEquals(expected, layer.toString()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testOverlayError1() { - // Must supply font_family for text in overlay - cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testOverlayError2() { - // Must supply public_id for for non-text underlay - cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); - } - public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java new file mode 100644 index 00000000..34d861be --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -0,0 +1,132 @@ +package com.cloudinary.transformation; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * 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_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"); + } + + @After + public void tearDown() throws Exception { + + } + + @Test + public void testOverlay() { + // should support overlay + Transformation transformation = new Transformation().overlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); + // should not pass width/height to html if overlay + transformation = new Transformation().overlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); + + transformation = new Transformation().overlay(new TextLayer().text("goodbye")); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); + } + + @Test + 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); + // should not pass width/height to html if underlay + transformation = new Transformation().underlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); + } + + + @Test + public void testLayerOptions() { + Object tests[] = { + new Layer().publicId("logo"), + "logo", + new Layer().publicId("folder/logo"), + "folder:logo", + new Layer().publicId("logo").type("private"), + "private:logo", + new Layer().publicId("logo").format("png"), + "logo.png", + new Layer().resourceType("video").publicId("cat"), + "video:cat", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing("4"), + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + "subtitles:Arial_40:sample_sub_he.srt" }; + + for (int i = 0; i < tests.length; i += 2) { + Object layer = tests[i]; + String expected = (String) tests[i + 1]; + assertEquals(expected, layer.toString()); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError1() { + // Must supply font_family for text in overlay + cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError2() { + // Must supply public_id for for non-text underlay + cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); + } + + @Test + public void testResourceType() throws Exception { + + } + + @Test + public void testType() throws Exception { + + } + + @Test + public void testPublicId() throws Exception { + + } + + @Test + public void testFormat() throws Exception { + + } + + @Test + public void testToString() throws Exception { + + } + + @Test + public void testFormattedPublicId() throws Exception { + + } +} \ No newline at end of file From 9d301aa4b3b366e0632a1ca5ff65b2c02b3f28df Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 18 Dec 2015 11:14:50 +0200 Subject: [PATCH 101/592] support responsive breakpoints paramater --- .../com/cloudinary/ResponsiveBreakpoints.java | 95 +++++++++++++++++++ .../main/java/com/cloudinary/Uploader.java | 3 + .../src/main/java/com/cloudinary/Util.java | 3 + .../com/cloudinary/test/CloudinaryTest.java | 32 +++++++ .../cloudinary/test/AbstractUploaderTest.java | 11 +++ 5 files changed, 144 insertions(+) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java new file mode 100644 index 00000000..eca561ea --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java @@ -0,0 +1,95 @@ +package com.cloudinary; + +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; + +public class ResponsiveBreakpoints { + private boolean createDerived = true; + private Transformation transformation = null; + private Integer maxWidth = null; //default 1000 + private Integer minWidth = null; //default 50 + private Integer bytesStep = null; //default 20kb + private Integer maxImages = null; //default 20 + + public boolean isCreateDerived() { + return createDerived; + } + public ResponsiveBreakpoints createDerived(boolean createDerived) { + this.createDerived = createDerived; + return this; + } + + public Transformation transformation() { + return transformation; + } + public ResponsiveBreakpoints transformation(Transformation transformation) { + this.transformation = transformation; + return this; + } + + public Integer maxWidth() { + return maxWidth; + } + public ResponsiveBreakpoints maxWidth(Integer maxWidth) { + this.maxWidth = maxWidth; + return this; + } + + public Integer minWidth() { + return minWidth; + } + public ResponsiveBreakpoints minWidth(Integer minWidth) { + this.minWidth = minWidth; + return this; + } + + public Integer bytesStep() { + return bytesStep; + } + public ResponsiveBreakpoints bytesStep(Integer bytesStep) { + this.bytesStep = bytesStep; + return this; + } + + public Integer maxImages() { + return maxImages; + } + public ResponsiveBreakpoints maxImages(Integer maxImages) { + this.maxImages = maxImages; + return this; + } + + public JSONObject toJson() { + JSONObject json = new JSONObject(); + json.put("create_derived", createDerived); + if (transformation != null) + json.put("transformation", transformation.generate()); + if (maxWidth != null) + json.put("max_width", maxWidth); + if (minWidth != null) + json.put("min_width", minWidth); + if (bytesStep != null) + json.put("bytes_step", bytesStep); + if (maxImages != null) + json.put("max_images", maxImages); + return json; + } + + public static String toJsonString(Object breakpoints) { + if (breakpoints == null) + return null; + + JSONArray arr = new JSONArray(); + if (breakpoints instanceof ResponsiveBreakpoints) { + arr.put(0, ((ResponsiveBreakpoints) breakpoints).toJson()); + } else if (breakpoints instanceof ResponsiveBreakpoints[]) { + for (ResponsiveBreakpoints i : (ResponsiveBreakpoints[]) breakpoints) { + arr.put(i.toJson()); + } + } else { + throw new IllegalArgumentException("breakpoints must be either of type ResponsiveBreakpoints or ResponsiveBreakpoints[]"); + } + return arr.toString(); + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 50380c2b..df1febf0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -191,6 +191,9 @@ public Map explicit(String publicId, Map options) throws IOException { if (options.get("context") != null) { params.put("context", ObjectUtils.encodeMap(options.get("context"))); } + if (options.get("responsive_breakpoints") != null) { + params.put("responsive_breakpoints", ResponsiveBreakpoints.toJsonString(options.get("responsive_breakpoints"))); + } params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); return callApi("explicit", params, options, null); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 82a5a5cd..f3a6074c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Map; +import org.cloudinary.json.JSONArray; + import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -35,6 +37,7 @@ public static final Map buildUploadParams(Map options) { params.put("folder", (String) options.get("folder")); params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); + params.put("responsive_breakpoints", ResponsiveBreakpoints.toJsonString(options.get("responsive_breakpoints"))); params.put("upload_preset", options.get("upload_preset")); if (options.get("signature") == null) { 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 f684cdae..982e9f44 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -19,6 +19,7 @@ import org.junit.rules.TestName; import com.cloudinary.Cloudinary; +import com.cloudinary.ResponsiveBreakpoints; import com.cloudinary.Transformation; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; @@ -942,6 +943,37 @@ public void testOverlayError2() { // Must supply public_id for for non-text underlay cloudinary.url().transformation(new Transformation().underlay(new LayerBuilder().resourceType("video"))).generate("test"); } + + @Test + public void testResponsiveBreakpointsToJson() { + assertEquals( + "[{\"create_derived\":true}]", + ResponsiveBreakpoints.toJsonString( + new ResponsiveBreakpoints() + )); + + assertEquals( + "[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}]", + ResponsiveBreakpoints.toJsonString( + new ResponsiveBreakpoints().createDerived(false) + .transformation(new Transformation().angle(45)) + .maxWidth(500) + .minWidth(100) + .maxImages(5) + )); + assertEquals( + "[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"},{\"create_derived\":true}]", + ResponsiveBreakpoints.toJsonString( + new ResponsiveBreakpoints[]{ + new ResponsiveBreakpoints().createDerived(false) + .transformation(new Transformation().angle(45)) + .maxWidth(500) + .minWidth(100) + .maxImages(5), + new ResponsiveBreakpoints() + } + )); + } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); 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 c0a057a4..e4e49de5 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 @@ -25,6 +25,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; +import com.cloudinary.ResponsiveBreakpoints; import com.cloudinary.Transformation; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; @@ -419,5 +420,15 @@ public void testFilenameOption() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif")); assertEquals("emanelif", result.get("original_filename")); } + + @Test + public void testResponsiveBreakpoints() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + new ResponsiveBreakpoints().maxImages(2).createDerived(false) + )); + java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + java.util.ArrayList breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); + } } From 1597c3ad82b76c1edebc394fb77c245a1f5b6146 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Fri, 18 Dec 2015 11:21:05 +0200 Subject: [PATCH 102/592] line spacng support in text overlay --- .../com/cloudinary/transformation/TextLayerBuilder.java | 8 ++++++++ .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java index fdbdc3de..7eb2d416 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java @@ -15,6 +15,7 @@ public class TextLayerBuilder extends AbstractLayerBuilder { protected String textAlign = null; protected String stroke = null; protected String letterSpacing = null; + protected Integer lineSpacing = null; protected String text = null; @Override @@ -73,6 +74,11 @@ public TextLayerBuilder letterSpacing(String letterSpacing) { this.letterSpacing = letterSpacing; return self(); } + + public TextLayerBuilder lineSpacing(Integer lineSpacing) { + this.lineSpacing = lineSpacing; + return self(); + } public TextLayerBuilder text(String text) { this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); @@ -119,6 +125,8 @@ protected String textStyleIdentifier() { components.add(this.stroke); if (StringUtils.isNotBlank(this.letterSpacing)) components.add("letter_spacing_" + this.letterSpacing); + if (this.lineSpacing != null) + components.add("line_spacing_" + this.lineSpacing.toString()); if (this.fontFamily == null && this.fontSize == null && components.isEmpty()) { return null; 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 982e9f44..491e827e 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -919,8 +919,8 @@ public void testOverlayOptions() { new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) - .fontWeight("bold").fontStyle("italic").letterSpacing("4"), - "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), + "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt" }; From d887deadb464921713a9b01a69d5b4dfb04d5e60 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Wed, 6 Jan 2016 16:12:32 +0200 Subject: [PATCH 103/592] support createArchive --- .../java/com/cloudinary/ArchiveParams.java | 203 ++++++++++++++++++ .../main/java/com/cloudinary/Cloudinary.java | 40 +++- .../com/cloudinary/ResponsiveBreakpoints.java | 2 +- .../main/java/com/cloudinary/Uploader.java | 23 +- .../src/main/java/com/cloudinary/Util.java | 35 +++ .../cloudinary/test/AbstractUploaderTest.java | 70 ++++-- 6 files changed, 352 insertions(+), 21 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java new file mode 100644 index 00000000..548dada2 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -0,0 +1,203 @@ +package com.cloudinary; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class ArchiveParams { + public static final String FORMAT_ZIP = "zip"; + + public static final String MODE_DOWNLOAD = "download"; + public static final String MODE_CREATE = "create"; + + private String resourceType = "image"; + private String type = null; + private String mode = MODE_CREATE; + private String targetFormat = null; + private String targetPublicId = null; + private boolean flattenFolders = false; + private boolean flattenTransformations = false; + private boolean useOriginalFilename = false; + private boolean async = false; + private boolean keepDerived = false; + private String notificationUrl = null; + private String[] targetTags = null; + private String[] tags = null; + private String[] publicIds = null; + private String[] prefixes = null; + private Transformation[] transformations = null; + + public String resourceType() { + return resourceType; + } + + public ArchiveParams resourceType(String resourceType) { + if (resourceType == null) + throw new IllegalArgumentException("resource type must be non-null"); + this.resourceType = resourceType; + return this; + } + + public String type() { + return type; + } + + public ArchiveParams type(String type) { + this.type = type; + return this; + } + + public String mode() { + return mode; + } + + public ArchiveParams mode(String mode) { + this.mode = mode; + return this; + } + + public String targetFormat() { + return targetFormat; + } + + public ArchiveParams targetFormat(String targetFormat) { + this.targetFormat = targetFormat; + return this; + } + + public String targetPublicId() { + return targetPublicId; + } + + public ArchiveParams targetPublicId(String targetPublicId) { + this.targetPublicId = targetPublicId; + return this; + } + + public boolean isFlattenFolders() { + return flattenFolders; + } + + public ArchiveParams flattenFolders(boolean flattenFolders) { + this.flattenFolders = flattenFolders; + return this; + } + + public boolean isFlattenTransformations() { + return flattenTransformations; + } + + public ArchiveParams flattenTransformations(boolean flattenTransformations) { + this.flattenTransformations = flattenTransformations; + return this; + } + + public boolean isUseOriginalFilename() { + return useOriginalFilename; + } + + public ArchiveParams useOriginalFilename(boolean useOriginalFilename) { + this.useOriginalFilename = useOriginalFilename; + return this; + } + + public boolean isAsync() { + return async; + } + + public ArchiveParams async(boolean async) { + this.async = async; + return this; + } + + public boolean isKeepDerived() { + return keepDerived; + } + + public ArchiveParams keepDerived(boolean keepDerived) { + this.keepDerived = keepDerived; + return this; + } + + public String notificationUrl() { + return notificationUrl; + } + + public ArchiveParams notificationUrl(String notificationUrl) { + this.notificationUrl = notificationUrl; + return this; + } + + public String[] targetTags() { + return targetTags; + } + + public ArchiveParams targetTags(String[] targetTags) { + this.targetTags = targetTags; + return this; + } + + public String[] tags() { + return tags; + } + + public ArchiveParams tags(String[] tags) { + this.tags = tags; + return this; + } + + public String[] publicIds() { + return publicIds; + } + + public ArchiveParams publicIds(String[] publicIds) { + this.publicIds = publicIds; + return this; + } + + public String[] prefixes() { + return prefixes; + } + + public ArchiveParams prefixes(String[] prefixes) { + this.prefixes = prefixes; + return this; + } + + public Transformation[] transformations() { + return transformations; + } + + public ArchiveParams transformations(Transformation[] transformations) { + this.transformations = transformations; + return this; + } + + public Map toMap() { + Map params = new HashMap(); + params.put("resource_type", resourceType); + params.put("type", type); + params.put("mode", mode); + if (targetPublicId != null) + params.put("target_public_id", targetPublicId); + params.put("flatten_folders", flattenFolders); + params.put("flatten_transformations", flattenTransformations); + params.put("use_original_filename", useOriginalFilename); + params.put("async", async); + params.put("keep_derived", keepDerived); + if (notificationUrl != null) + params.put("notification_url", notificationUrl); + if (targetTags != null) + params.put("target_tags", targetTags); + if (tags != null) + params.put("tags", tags); + if (publicIds != null) + params.put("public_ids", publicIds); + if (prefixes != null) + params.put("prefixes", prefixes); + if (transformations != null) { + params.put("transformations", Arrays.asList(transformations)); + } + return params; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 80fdd388..e4a66cb3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -135,6 +135,8 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) 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()); @@ -170,14 +172,14 @@ public String privateDownload(String publicId, String format, Map options) throws Exception { Map params = new HashMap(); - params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); + params.put("timestamp", Util.timestamp()); params.put("tag", tag); Object transformation = options.get("transformation"); if (transformation != null) { @@ -191,6 +193,22 @@ public String zipDownload(String tag, Map options) throws Except return buildUrl(cloudinaryApiUrl("download_tag.zip", options), params); } + public String downloadArchive(Map options, String targetFormat) throws UnsupportedEncodingException { + Map params = Util.buildArchiveParams(options, targetFormat); + params.put("mode", ArchiveParams.MODE_DOWNLOAD); + signRequest(params, options); + return buildUrl(cloudinaryApiUrl("generate_archive", options), params); + } + + public String downloadArchive(ArchiveParams params) throws UnsupportedEncodingException { + return downloadArchive(params.toMap(), params.targetFormat()); + } + + public String downloadZip(Map options) throws UnsupportedEncodingException { + return downloadArchive(options, "zip"); + } + + private String buildUrl(String base, Map params) throws UnsupportedEncodingException { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(base); @@ -199,9 +217,23 @@ private String buildUrl(String base, Map params) throws Unsuppor } boolean first = true; for (Map.Entry param : params.entrySet()) { + String keyValue = null; + Object value = param.getValue(); if (!first) urlBuilder.append("&"); - urlBuilder.append(param.getKey()).append("=").append( - URLEncoder.encode(param.getValue().toString(), "UTF-8")); + if (value instanceof Object[]) + value = Arrays.asList(value); + if (value instanceof Collection) { + String key = param.getKey() + "[]="; + Collection items = (Collection) value; + List encodedItems = new ArrayList(); + for (Object item : items) + encodedItems.add(URLEncoder.encode(item.toString(), "UTF-8")); + keyValue = key + StringUtils.join(encodedItems, "&" + key); + } else { + keyValue = param.getKey() + "=" + + URLEncoder.encode(value.toString(), "UTF-8"); + } + urlBuilder.append(keyValue); first = false; } return urlBuilder.toString(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java index eca561ea..cbfa6384 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java @@ -81,7 +81,7 @@ public static String toJsonString(Object breakpoints) { JSONArray arr = new JSONArray(); if (breakpoints instanceof ResponsiveBreakpoints) { - arr.put(0, ((ResponsiveBreakpoints) breakpoints).toJson()); + arr.put(((ResponsiveBreakpoints) breakpoints).toJson()); } else if (breakpoints instanceof ResponsiveBreakpoints[]) { for (ResponsiveBreakpoints i : (ResponsiveBreakpoints[]) breakpoints) { arr.put(i.toJson()); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index df1febf0..44642d3b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -197,8 +197,13 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); 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 = ObjectUtils.emptyMap(); Map params = new HashMap(); @@ -303,9 +308,23 @@ public Map text(String text, Map options) throws IOException { } return callApi("text", params, options, null); } + + public Map createArchive(Map options, String targetFormat) throws IOException { + Map params = Util.buildArchiveParams(options, targetFormat); + return callApi("generate_archive", params, options, null); + } + + public Map createZip(Map options) throws IOException { + return createArchive(options, "zip"); + } + + public Map createArchive(ArchiveParams params) throws IOException { + return createArchive(params.toMap(), params.targetFormat()); + } public void signRequestParams(Map params, Map options) { - params.put("timestamp", new Long(System.currentTimeMillis() / 1000L).toString()); + if (!params.containsKey("timestamp")) + params.put("timestamp", Util.timestamp()); cloudinary.signRequest(params, options); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index f3a6074c..f2d43db4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -145,4 +145,39 @@ public static void clearEmpty(Map params) { } } + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static final Map buildArchiveParams(Map options, String targetFormat) { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", options.get("type")); + params.put("mode", options.get("mode")); + params.put("target_format", targetFormat); + params.put("target_public_id", options.get("target_public_id")); + params.put("flatten_folders", ObjectUtils.asBoolean(options.get("flatten_folders"), false)); + params.put("flatten_transformations", ObjectUtils.asBoolean(options.get("flatten_transformations"), false)); + params.put("use_original_filename", ObjectUtils.asBoolean(options.get("use_original_filename"), false)); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false)); + params.put("keep_derived", ObjectUtils.asBoolean(options.get("keep_derived"), false)); + params.put("notification_url", options.get("notification_url")); + if (options.get("target_tags") != null) + params.put("target_tags", ObjectUtils.asArray(options.get("target_tags"))); + if (options.get("tags") != null) + params.put("tags", ObjectUtils.asArray(options.get("tags"))); + if (options.get("public_ids") != null) + params.put("public_ids", ObjectUtils.asArray(options.get("public_ids"))); + if (options.get("prefixes") != null) + params.put("prefixes", ObjectUtils.asArray(options.get("prefixes"))); + if (options.get("transformations") != null) + params.put("transformations", buildEager((List) options.get("transformations"))); + if (options.get("timestamp") != null) + params.put("timestamp", options.get("timestamp")); + else + params.put("timestamp", Util.timestamp()); + return params; + } + + protected static String timestamp() { + return new Long(System.currentTimeMillis() / 1000L).toString(); + } } 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 e4e49de5..1003e6ce 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 @@ -4,8 +4,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; -import static org.junit.Assert.assertArrayEquals; - +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -16,13 +15,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.zip.ZipInputStream; +import java.net.*; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.AfterClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import com.cloudinary.ArchiveParams; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; import com.cloudinary.ResponsiveBreakpoints; @@ -35,15 +38,27 @@ abstract public class AbstractUploaderTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; + private static final String ARCHIVE_TAG = "archive_tag_" + String.valueOf(System.currentTimeMillis()); private Cloudinary cloudinary; @BeforeClass - public static void setUpClass() { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - } - } + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", ARCHIVE_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("tags", ARCHIVE_TAG, + "transformation", new Transformation().crop("scale").width(10))); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Cloudinary cloudinary = new Cloudinary(); + cloudinary.api().deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); + } @Rule public TestName currentTest = new TestName(); @@ -62,7 +77,7 @@ public void testUpload() throws IOException { assertNotNull(result.get("colors")); assertNotNull(result.get("predominant")); Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); + 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); assertEquals(result.get("signature"), expected_signature); @@ -74,7 +89,7 @@ public void testUploadUrl() throws IOException { assertEquals(result.get("width"), 241); assertEquals(result.get("height"), 51); Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); + 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); assertEquals(result.get("signature"), expected_signature); @@ -86,7 +101,7 @@ public void testUploadDataUri() throws IOException { assertEquals(result.get("width"), 16); assertEquals(result.get("height"), 16); Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); + 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); assertEquals(result.get("signature"), expected_signature); @@ -122,7 +137,7 @@ public void testUniqueFilename() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals((String) result.get("public_id"), "old_logo"); + assertEquals(result.get("public_id"), "old_logo"); } @Test public void testExplicit() throws IOException { @@ -318,8 +333,8 @@ public void testContext() throws Exception { public void testModerationRequest() throws Exception { //should support requesting manual moderation Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - assertEquals("manual", ((Map) ((List) result.get("moderation")).get(0)).get("kind")); - assertEquals("pending", ((Map) ((List) result.get("moderation")).get(0)).get("status")); + assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); + assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); } @@ -430,5 +445,32 @@ public void testResponsiveBreakpoints() throws Exception { java.util.ArrayList breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); assertEquals(2, breakpoints.size()); } + + @Test + public void testCreateArchive() throws Exception { + Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[] { ARCHIVE_TAG })); + assertEquals(2, result.get("file_count")); + result = cloudinary.uploader().createArchive( + new ArchiveParams().tags(new String[] { ARCHIVE_TAG }).transformations( + new Transformation[] { new Transformation().width(0.5), new Transformation().width(2.0) })); + assertEquals(4, result.get("file_count")); + } + + @Test + public void testDownloadArchive() throws Exception { + String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[] { ARCHIVE_TAG })); + URL url = new java.net.URL(result); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + ZipInputStream in = new ZipInputStream(new BufferedInputStream(urlConnection.getInputStream())); + int files = 0; + try { + while ((in.getNextEntry()) != null) { + files += 1; + } + } finally { + in.close(); + } + assertEquals(2, files); + } } From 2b19b5ec1bdf1a39f15efdc1541f20bb7e1411d4 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 18 Jan 2016 17:24:07 +0200 Subject: [PATCH 104/592] Remove redundant `deleteConflictingFiles`. --- cloudinary-android/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 619478b3..fec11dfc 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -41,7 +41,6 @@ 19 - true true true From cf0f77072b2735d3e053c2f771a44fb2850d84c8 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 14:11:52 +0200 Subject: [PATCH 105/592] Support cloudinary credentials URL that has an API_KEY but no API_SECRET This is required for mobile applications that are not supposed to use the API_SECRET. --- .../main/java/com/cloudinary/Cloudinary.java | 4 ++- .../cloudinary/test/AbstractUploaderTest.java | 32 +++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index e4a66cb3..3a5f4996 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -246,7 +246,9 @@ protected Map parseConfigUrl(String cloudinaryUrl) { if (cloudinaryUri.getUserInfo() != null) { String[] creds = cloudinaryUri.getUserInfo().split(":"); params.put("api_key", creds[0]); - params.put("api_secret", creds[1]); + if (creds.length > 1) { + params.put("api_secret", creds[1]); + } } params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); params.put("secure_distribution", cloudinaryUri.getPath()); 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 1003e6ce..c28d1bb7 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 @@ -18,6 +18,8 @@ import java.util.zip.ZipInputStream; import java.net.*; +import com.cloudinary.*; +import org.cloudinary.json.JSONArray; import org.junit.Before; import org.junit.BeforeClass; import org.junit.AfterClass; @@ -25,11 +27,7 @@ import org.junit.Test; import org.junit.rules.TestName; -import com.cloudinary.ArchiveParams; -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.ResponsiveBreakpoints; -import com.cloudinary.Transformation; +import com.cloudinary.ResponsiveBreakpoint; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; @@ -438,12 +436,32 @@ public void testFilenameOption() throws Exception { @Test public void testResponsiveBreakpoints() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", - new ResponsiveBreakpoints().maxImages(2).createDerived(false) + ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false); + + // A single breakpoint + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + breakpoint )); java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); java.util.ArrayList breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); assertEquals(2, breakpoints.size()); + + // an array of breakpoints + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + new ResponsiveBreakpoint [] {breakpoint} + )); + breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); + + // a JSONArray of breakpoints + JSONArray array = new JSONArray(); + array.put(breakpoint); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array + )); + breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); } @Test From 6c1e9d92909aa38549d43ba88a3fc3b47efffe2d Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 14:23:32 +0200 Subject: [PATCH 106/592] Update SDK versions in Android projects. --- cloudinary-android-test/pom.xml | 2 +- cloudinary-android-test/project.properties | 2 +- cloudinary-android/AndroidManifest.xml | 2 +- cloudinary-android/pom.xml | 2 +- cloudinary-android/project.properties | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 28dd821f..1c6e8a74 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -30,7 +30,7 @@ com.google.android android-test - 2.3.1 + 4.1.1.4 provided diff --git a/cloudinary-android-test/project.properties b/cloudinary-android-test/project.properties index ec086c39..518fade5 100644 --- a/cloudinary-android-test/project.properties +++ b/cloudinary-android-test/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-19 +target=android-23 android.library.reference.1=../cloudinary-android diff --git a/cloudinary-android/AndroidManifest.xml b/cloudinary-android/AndroidManifest.xml index df33f43e..99d7ce4e 100644 --- a/cloudinary-android/AndroidManifest.xml +++ b/cloudinary-android/AndroidManifest.xml @@ -5,6 +5,6 @@ + android:targetSdkVersion="22" /> diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index fec11dfc..afb370e4 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -39,7 +39,7 @@ 4.0.0-rc.2 - 19 + 23 true diff --git a/cloudinary-android/project.properties b/cloudinary-android/project.properties index 484dab07..362a0a30 100644 --- a/cloudinary-android/project.properties +++ b/cloudinary-android/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-22 android.library=true From 91158790a78f52256f96580ce3fa220e5529ac28 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 14:25:22 +0200 Subject: [PATCH 107/592] Use constant and meaningful name for upload preset. Rearrange imports. --- .../com/cloudinary/test/UploaderTest.java | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index ed8f6bdc..84ff6f34 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -1,6 +1,16 @@ package com.cloudinary.test; -import static org.junit.Assert.assertEquals; +import android.test.InstrumentationTestCase; +import android.util.Log; +import com.cloudinary.Cloudinary; +import com.cloudinary.Coordinates; +import com.cloudinary.Transformation; +import com.cloudinary.android.Utils; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; +import org.junit.Test; import java.io.File; import java.io.FileOutputStream; @@ -13,25 +23,12 @@ import java.util.HashMap; import java.util.Map; -import org.cloudinary.json.JSONArray; -import org.cloudinary.json.JSONObject; -import org.junit.Test; - -import android.test.InstrumentationTestCase; -import android.util.Log; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; -import com.cloudinary.android.Utils; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.Rectangle; - public class UploaderTest extends InstrumentationTestCase { public static final String TEST_IMAGE = "images/old_logo.png"; - private Cloudinary cloudinary; + public static final String TEST_PRESET = "cloudinary_java_test"; + private Cloudinary cloudinary; private static boolean first = true; public void setUp() throws Exception { @@ -67,7 +64,7 @@ public void testUpload() throws Exception { public void testUnsignedUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), "sample_preset_dhfjhriu", + JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), TEST_PRESET, ObjectUtils.emptyMap())); assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); From 2e528c72541e4b309a32307b4096d0423300a0a7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 14:26:37 +0200 Subject: [PATCH 108/592] Use `TextLayer` instead of `TextLayerBuilder`. Use `getThis()` instead of `self()`. --- .../main/java/com/cloudinary/transformation/TextLayer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index 46038fe1..954eac64 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -75,9 +75,9 @@ public TextLayer letterSpacing(String letterSpacing) { return getThis(); } - public TextLayerBuilder lineSpacing(Integer lineSpacing) { + public TextLayer lineSpacing(Integer lineSpacing) { this.lineSpacing = lineSpacing; - return self(); + return getThis(); } public TextLayer text(String text) { From a953c3c5bf6a5c165cbca174572c9e2c80ed92e8 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 14:27:47 +0200 Subject: [PATCH 109/592] Rename `ResponsiveBreakpoints` to `ResponsiveBreakpoint` and have it extend JSONObject. --- .../com/cloudinary/ResponsiveBreakpoint.java | 64 +++++++++++++ .../com/cloudinary/ResponsiveBreakpoints.java | 95 ------------------- .../main/java/com/cloudinary/Uploader.java | 2 +- .../src/main/java/com/cloudinary/Util.java | 7 +- .../com/cloudinary/test/CloudinaryTest.java | 84 ++++++++-------- 5 files changed, 109 insertions(+), 143 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java new file mode 100644 index 00000000..670cbf51 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java @@ -0,0 +1,64 @@ +package com.cloudinary; + +import org.cloudinary.json.JSONObject; + +public class ResponsiveBreakpoint extends JSONObject { + public ResponsiveBreakpoint() { + put("create_derived", true); + } + + public boolean isCreateDerived() { + return optBoolean("create_derived"); + } + + public ResponsiveBreakpoint createDerived(boolean createDerived) { + put("create_derived", createDerived); + return this; + } + + public Transformation transformation() { + return (Transformation) opt("transformation"); + } + + public ResponsiveBreakpoint transformation(Transformation transformation) { + put("transformation", transformation); + return this; + } + + public int maxWidth() { + return optInt("max_width"); + } + + public ResponsiveBreakpoint maxWidth(int maxWidth) { + put("max_width", maxWidth); + return this; + } + + public int minWidth() { + return optInt("min_width"); + } + + public ResponsiveBreakpoint minWidth(Integer minWidth) { + put("min_width", minWidth); + return this; + } + + public int bytesStep() { + return optInt("bytes_step"); + } + + public ResponsiveBreakpoint bytesStep(Integer bytesStep) { + put("bytes_step", bytesStep); + return this; + } + + public int maxImages() { + return optInt("max_images"); + } + + public ResponsiveBreakpoint maxImages(Integer maxImages) { + put("max_images", maxImages); + return this; + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java deleted file mode 100644 index cbfa6384..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoints.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.cloudinary; - -import org.cloudinary.json.JSONArray; -import org.cloudinary.json.JSONObject; - -public class ResponsiveBreakpoints { - private boolean createDerived = true; - private Transformation transformation = null; - private Integer maxWidth = null; //default 1000 - private Integer minWidth = null; //default 50 - private Integer bytesStep = null; //default 20kb - private Integer maxImages = null; //default 20 - - public boolean isCreateDerived() { - return createDerived; - } - public ResponsiveBreakpoints createDerived(boolean createDerived) { - this.createDerived = createDerived; - return this; - } - - public Transformation transformation() { - return transformation; - } - public ResponsiveBreakpoints transformation(Transformation transformation) { - this.transformation = transformation; - return this; - } - - public Integer maxWidth() { - return maxWidth; - } - public ResponsiveBreakpoints maxWidth(Integer maxWidth) { - this.maxWidth = maxWidth; - return this; - } - - public Integer minWidth() { - return minWidth; - } - public ResponsiveBreakpoints minWidth(Integer minWidth) { - this.minWidth = minWidth; - return this; - } - - public Integer bytesStep() { - return bytesStep; - } - public ResponsiveBreakpoints bytesStep(Integer bytesStep) { - this.bytesStep = bytesStep; - return this; - } - - public Integer maxImages() { - return maxImages; - } - public ResponsiveBreakpoints maxImages(Integer maxImages) { - this.maxImages = maxImages; - return this; - } - - public JSONObject toJson() { - JSONObject json = new JSONObject(); - json.put("create_derived", createDerived); - if (transformation != null) - json.put("transformation", transformation.generate()); - if (maxWidth != null) - json.put("max_width", maxWidth); - if (minWidth != null) - json.put("min_width", minWidth); - if (bytesStep != null) - json.put("bytes_step", bytesStep); - if (maxImages != null) - json.put("max_images", maxImages); - return json; - } - - public static String toJsonString(Object breakpoints) { - if (breakpoints == null) - return null; - - JSONArray arr = new JSONArray(); - if (breakpoints instanceof ResponsiveBreakpoints) { - arr.put(((ResponsiveBreakpoints) breakpoints).toJson()); - } else if (breakpoints instanceof ResponsiveBreakpoints[]) { - for (ResponsiveBreakpoints i : (ResponsiveBreakpoints[]) breakpoints) { - arr.put(i.toJson()); - } - } else { - throw new IllegalArgumentException("breakpoints must be either of type ResponsiveBreakpoints or ResponsiveBreakpoints[]"); - } - return arr.toString(); - } - -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 44642d3b..63297803 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -192,7 +192,7 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("context", ObjectUtils.encodeMap(options.get("context"))); } if (options.get("responsive_breakpoints") != null) { - params.put("responsive_breakpoints", ResponsiveBreakpoints.toJsonString(options.get("responsive_breakpoints"))); + params.put("responsive_breakpoints", JSONObject.wrap(options.get("responsive_breakpoints"))); } params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); return callApi("explicit", params, options, null); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index f2d43db4..9a259fd2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -6,10 +6,9 @@ import java.util.List; import java.util.Map; -import org.cloudinary.json.JSONArray; - import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import org.cloudinary.json.JSONObject; public class Util { static final String[] BOOLEAN_UPLOAD_OPTIONS = new String[] { "backup", "exif", "faces", "colors", "image_metadata", "use_filename", "unique_filename", @@ -37,7 +36,9 @@ public static final Map buildUploadParams(Map options) { params.put("folder", (String) options.get("folder")); params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); - params.put("responsive_breakpoints", ResponsiveBreakpoints.toJsonString(options.get("responsive_breakpoints"))); + Object responsive_breakpoints = options.get("responsive_breakpoints"); + if (responsive_breakpoints != null){ + params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints));} params.put("upload_preset", options.get("upload_preset")); if (options.get("signature") == null) { 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 369ba4bc..5c91cba7 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1,29 +1,29 @@ package com.cloudinary.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.cloudinary.ResponsiveBreakpoint; +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import com.cloudinary.Cloudinary; -import com.cloudinary.ResponsiveBreakpoints; import com.cloudinary.Transformation; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; +import static org.junit.Assert.*; + public class CloudinaryTest { private static final String DEFAULT_ROOT_PATH = "http://res.cloudinary.com/test123/"; private static final String DEFAULT_UPLOAD_PATH = DEFAULT_ROOT_PATH + "image/upload/"; @@ -269,7 +269,7 @@ public void testOverlay() { assertNull(transformation.getHtmlWidth()); assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); - transformation = new Transformation().overlay(new TextLayerBuilder().text("goodbye")); + transformation = new Transformation().overlay(new TextLayer().text("goodbye")); result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); } @@ -906,23 +906,23 @@ public void testAspectRatio() { @Test public void testOverlayOptions() { Object tests[] = { - new LayerBuilder().publicId("logo"), + new Layer().publicId("logo"), "logo", - new LayerBuilder().publicId("folder/logo"), + new Layer().publicId("folder/logo"), "folder:logo", - new LayerBuilder().publicId("logo").type("private"), + new Layer().publicId("logo").type("private"), "private:logo", - new LayerBuilder().publicId("logo").format("png"), + new Layer().publicId("logo").format("png"), "logo.png", - new LayerBuilder().resourceType("video").publicId("cat"), + new Layer().resourceType("video").publicId("cat"), "video:cat", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", - new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt" }; for (int i = 0; i < tests.length; i += 2) { @@ -935,44 +935,40 @@ public void testOverlayOptions() { @Test(expected = IllegalArgumentException.class) public void testOverlayError1() { // Must supply font_family for text in overlay - cloudinary.url().transformation(new Transformation().overlay(new TextLayerBuilder().fontStyle("italic"))).generate("test"); + cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); } @Test(expected = IllegalArgumentException.class) public void testOverlayError2() { // Must supply public_id for for non-text underlay - cloudinary.url().transformation(new Transformation().underlay(new LayerBuilder().resourceType("video"))).generate("test"); + cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); } @Test public void testResponsiveBreakpointsToJson() { - assertEquals( + assertEquals("an empty ResponsiveBreakpoint should have create_derived=true", "[{\"create_derived\":true}]", - ResponsiveBreakpoints.toJsonString( - new ResponsiveBreakpoints() - )); - - assertEquals( - "[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}]", - ResponsiveBreakpoints.toJsonString( - new ResponsiveBreakpoints().createDerived(false) - .transformation(new Transformation().angle(45)) - .maxWidth(500) - .minWidth(100) - .maxImages(5) - )); - assertEquals( - "[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"},{\"create_derived\":true}]", - ResponsiveBreakpoints.toJsonString( - new ResponsiveBreakpoints[]{ - new ResponsiveBreakpoints().createDerived(false) - .transformation(new Transformation().angle(45)) - .maxWidth(500) - .minWidth(100) - .maxImages(5), - new ResponsiveBreakpoints() - } - )); + new ResponsiveBreakpoint().toString() + ); + JSONObject expected = new JSONObject("{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}"); + JSONObject actual = new ResponsiveBreakpoint().createDerived(false) + .transformation(new Transformation().angle(45)) + .maxWidth(500) + .minWidth(100) + .maxImages(5) + ; + assertTrue(actual.similar(expected)); + + JSONArray actualArray = new JSONArray(Arrays.asList( + new ResponsiveBreakpoint().createDerived(false) + .transformation(new Transformation().angle(45)) + .maxWidth(500) + .minWidth(100) + .maxImages(5), + new ResponsiveBreakpoint() + ));; + JSONArray expectedArray = new JSONArray("[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"},{\"create_derived\":true}]"); + assertTrue(actualArray.similar(expectedArray)); } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { From 6301268a9e4075307f4f41cbcd491659603767bc Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 16:09:23 +0200 Subject: [PATCH 110/592] Add Layer classes named `*Builder` to provide backward compatibility. --- .../transformation/AbstractLayerBuilder.java | 7 +++++ .../transformation/LayerBuilder.java | 7 +++++ .../transformation/SubtitlesLayerBuilder.java | 7 +++++ .../transformation/TextLayerBuilder.java | 7 +++++ .../com/cloudinary/test/CloudinaryTest.java | 30 +++++++++++++++++++ 5 files changed, 58 insertions(+) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java new file mode 100644 index 00000000..bcc2cfea --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +/** + * @deprecated + */ +public abstract class AbstractLayerBuilder extends AbstractLayer { +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java new file mode 100644 index 00000000..84198805 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +/** + * @deprecated + */ +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 new file mode 100644 index 00000000..099bb3b3 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +/** + * @deprecated + */ +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 new file mode 100644 index 00000000..777f12b5 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java @@ -0,0 +1,7 @@ +package com.cloudinary.transformation; + +/** + * @deprecated + */ +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 5c91cba7..439e6abe 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -932,6 +932,36 @@ public void testOverlayOptions() { } } + + @Test + public void testBackwardCampatibleOverlayOptions() { + Object tests[] = { + new LayerBuilder().publicId("logo"), + "logo", + new LayerBuilder().publicId("folder/logo"), + "folder:logo", + new LayerBuilder().publicId("logo").type("private"), + "private:logo", + new LayerBuilder().publicId("logo").format("png"), + "logo.png", + new LayerBuilder().resourceType("video").publicId("cat"), + "video:cat", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing("4"), + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + "subtitles:Arial_40:sample_sub_he.srt" }; + + for (int i = 0; i < tests.length; i += 2) { + Object layer = tests[i]; + String expected = (String) tests[i + 1]; + assertEquals(expected, layer.toString()); + } + } + @Test(expected = IllegalArgumentException.class) public void testOverlayError1() { // Must supply font_family for text in overlay From fab87a5a2f92b1f7f5bf1f347d71037aebe5bb2d Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 16:11:58 +0200 Subject: [PATCH 111/592] Update and rename `CHANGES.txt` to `CHANGELOG.md`. Update version. --- CHANGELOG.md | 313 ++++++++++++++++++ CHANGES.txt | 11 - .../main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 314 insertions(+), 12 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 CHANGES.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..0859f56b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,313 @@ + +cloudinary-parent-1.3.0 / 2016-01-19 +==================================== + + * Add `responsive_breakpoints` paramater + * Use `TextLayer` instead of `TextLayerBuilder`. Use `getThis()` instead of `self()`. + * Use constant and meaningful name for upload preset. Rearrange imports. + * Update SDK versions in Android projects. + * Support cloudinary credentials URL that has an API_KEY but no API_SECRET + * Remove redundant `deleteConflictingFiles`. + * Merge branch 'master' of github.com:cloudinary/cloudinary_java + * support createArchive + * line spacng support in text overlay + * Create separate test class for Layer + * Rename Layer classes. Rename self() to getThis() to match the pattern. + * Merge branch 'master' of github.com:cloudinary/cloudinary_java + * change user agent - remove spaces. stricter layer parameter check. fix underlay method signature + * Merge branch 'master' of github.com:cloudinary/cloudinary_java + * Fix Android complex filename test + +cloudinary-parent-1.2.2 / 2016-01-19 +==================================== + + * Fix Android tests + * Enable apache http 4.3 strategy + * Support easy overlay/underlay construction + * Support upload mappings api. add missing restore test + * Support the restore api + * Normalize user agent + * Add invalidate flag to rename and explicit + * Support aspect ratio transformation param + * Add filename and complex filename test + * Fix encoding issues when JVM default encoding is not UTF-8 + * Revent timeout exception change + * Support filename in upload options. close response objects in http44 + * Update README. Fixes #28 + * Merge pull request #26 from wagaun/master + * Fixing typo on exception + * Update README.md + +cloudinary-parent-1.2.1 / 2015-06-18 +==================================== + + * Disable java8 doclint + * Fix references to 1.1.4-SNAPSHOT. Fix wrong URLs in README.md + * Fix documentation and imports + * Modify exception message to say that Admin API is not supported. + * Fix HTML escaping (fixes upload tags) + * Allow android unsigned upload without api_key + * Fix http44 response closing. + +cloudinary-parent-1.2.2 / 2015-10-11 +==================================== + + * Support apache http 4.3 strategy + * Support easy overlay/underlay construction + * Support upload mappings api + * Support the restore api + * Normalize user agent + * Add invalidate flag to rename and explicit + * Support aspect ratio transformation param + * Fix encoding issues when JVM default encoding is not UTF-8 + * Support filename in upload options + * Close response objects in http44. + +cloudinary-parent-1.2.0 / 2015-04-13 +==================================== + + * Support httpcomponents 4.4 + * Support for video tag and transformations + * Add video transformation parameters and zoom transformation + * Support ftp url upload + * Support eager_async in explicit + * Fix UTF-8 issues in API + * Add support for video tag. refactor Url based tags + * Scrub UrlBuilderStrategy + * Enable crippled core mode without loading strategies + * Move core test to core + * Use URLEncoder instead of AbstractUrlBuilderStrategy. + * Use upload_chuncked endpoint for upload large + * Improved parameter support for upload_large. + * support byte[] file input for upload + +cloudinary-parent-1.1.3 / 2015-02-24 +==================================== + + * Fix test after file name change + * Added timeout parameter to admin api and Fixed test and configuration issues + +cloudinary-parent-1.1.2 / 2015-01-15 +==================================== + + * Fix support for string eager parameters e.g. for safe mobile flow + * Merge pull request #17 from cloudinary/eager_upload_params + * merged android signature fix + * eager upload params can be both string or List + +cloudinary-parent-1.1.1 / 2014-12-22 +==================================== + + * Support secure domain sharding + * Don't sign version component + * Support url suffix and use root path + * renamed urlSuffix to suffix + * Support tags in upload large. + * Change log and version update + * added new options to url tag + * added invalidate to bulk deletes + * Add missing tests in adnroid-test. fix signing tests in android-test. be more specific with exception class in http42 Cloudinary tests. + * updated Url.generate method (b4 tests) + * bug fixes + +cloudinary-parent-1.1.0 / 2014-11-18 +==================================== + + * Merge branch 'globalize' of github.com:codeinvain/cloudinary_java + * Remove redundant depndencies + * - changed org.json to org.cloudinary.json due to Android optimization issues . - removed dependency on SimpleJSON from tablib + * Update CHANGES.txt + * Merge branch 'globalize' + * Fix documentation. Fix dependencies + * Fix modules artifactId + * promoted minor version (1.0.x -> 1.1.x) & fixed documentation external links + * added deprecated asMap method to Cloudinary (support old api) + * updated documentation , fixed sample projects + * promoted minor version (1.0.x -> 1.1.x) & fixed documentation external links + * added deprecated asMap method to Cloudinary (support old api) + * updated documentation , fixed sample projects + * add support for signed urls in tag helpers (image and url) + * Git ignore cloudinary-android-test/src/main/AndroidManifest.xml. Fix tag lib dependency + * Remove httpclient dependencies from cloudinary-core. Use main version in both http42 and android versions. Remove getRawResponse from ApiResponse + * Merge branch 'globalize' of github.com:codeinvain/cloudinary_java + * merged config & builder + * Update README.md + * cloudinary credentials removed + * http42 + android tests pass + * changed architecture to core + strategies + * removed shared classes + * android jar + * maven build , project dependency core -> http42 -> taglib + * unified Java API and created basic implementation + * custom StringUtils + * support folder listing API + +cloudinary-parent-1.0.14 / 2014-07-29 +===================================== + + * Add background_removal + * Support return_delete_token in upload/update params + * Support responsive and hidpi + * Support custom coordinates. + +cloudinary-parent-1.0.13 / 2014-04-29 +===================================== + + * Add support for opacity + * Support upload_presets + * Support unsigned uploads + * Support start_at for resource listing + * Support phash for upload and resource details + * Support rate limit header in Api calls + * Initial commit Google App engine sample + * Merge remote master + * Allow passing ClientConnectionManager + +cloudinary-parent-1.0.12 / 2014-03-04 +===================================== + + * Increment version to 1.0.12 + * Fix uploader API calls handling of non-string parameters e.g. Booleans + +cloudinary-parent-1.0.11 / 2014-03-04 +===================================== + + * Document releases in CHANGES.txt + * Fix test - raw upload parts must be > 5m + * better large raw upload support + * Merge branch 'master' of https://github.com/cloudinary/cloudinary_java + * new update method + * Add listing by moderation kind and status + * Add moderation status in listing + * Add moderation flag in upload + * Add moderation_status in update + * Add ocr, raw_conversion, categorization, detection, similarity_search and auto_tagging parameters in update and upload + * Add support for uploading large raw files + +cloudinary-parent-1.0.10 / 2014-01-27 +===================================== + + * add discard_original_filename upload flag. Formatting in tests + * support setting context in explicit + * Add direction support to resource listing. + +cloudinary-parent-1.0.9 / 2014-01-10 +==================================== + + * remove delete_all from tests. fix face coordinates in explicit + * Merge branch 'master' of https://github.com/cloudinary/cloudinary_java + * add user agent. fix api test + * refactor Map encoding for upload + * Merge branch 'signedurl' + * Update README.md + * Merge branch 'master' of https://github.com/cloudinary/cloudinary_java + * support multiple face coordinates in upload and explicit. optionaly use Coordinates as a wrapper of multiple rectangles + * add support for overwrite in taglib + * add support for overwrite boolean in upload + * support signed urls + * delete all + cursors, tag and context flags in lists, list by public ids, add support in upload for: face_coordinates, alowed_formats, context + * change dependency to published 1.0.8 and change installation instructions accordingly + +cloudinary-parent-1.0.8 / 2013-12-20 +==================================== + + * Fix implementation of SmartUrlEncoder in case of non-ascii characters + * fix callback when servlet is not at root + * better handling of raw files + * add subsections in README. Add this to memeber assignments + * move most of stored file logic to core. support stored file in url and url and image tags. add a readme to the sample project + * add support for named transformations as tag attribute + * add support for local secure (and implicit from request) and cdn_subdomain + * cleanup and upload parameters completeness + * change images to use inline transformations when possible. fix image link in list + * fix inline transformation in image. add inline trnaformation in url + * initial commit of photo album sample. added additional or modified existing tag helpers to taglib to enable more robust transformations and to allow cloufdinary URLs outside of images and to allow specifying images from facebook/twitter and support jQuery direct upload. + +cloudinary-parent-1.0.7 / 2013-11-02 +==================================== + + * Support the color parameter + * Merge branch 'master' of https://github.com/cloudinary/cloudinary_java + * add support for unique_filename and added a test for use_filename + * Merge pull request #9 from AssuredLabor/transformationAttr + * add transformation attribute to cloudinary upload tag + * Fix handling of boolean parameters on upload + +cloudinary-parent-1.0.6 / 2013-08-07 +==================================== + + * Rename prepareUploadTagParams to uploadTagParams + * Escape all public_ids including non-http ones. + * Merge pull request #7 from AssuredLabor/extractUploadParams + * Updated so we don't escapeHTML unless necessary for the server side. This allows the client-side to receive a JS hash / object directly. This is useful, depending on how the input is rendered. + * Extracted upload tagParams and upload url functionality into their functions, this will facilitate frameworks like Angular fetching the server-side params + +cloudinary-parent-1.0.5 / 2013-07-31 +==================================== + + * Support folder and proxy upload parameters + * Fix string comparison of secureDistribution + * Change secure urls to use *res.cloudinary.com + * Support Admin API ping + * Support generateSpriteCss + +cloudinary-parent-1.0.4 / 2013-07-15 +==================================== + + * Issue #6 - add instructions on using as a maven dependency + * Support raw data URI + * Support zipDownload. Cleanup signing code + * Support s3 and data:uri urls + +cloudinary-parent-1.0.3 / 2013-06-04 +==================================== + + * Cleanup pom.xml, Fix imageUploadTag test, Fix imports + * Introduced a new image tag for jsps, you can use it like this: + * don't track eclipse resources + * Add the callback and the signature to the image tag + * In the tag lib, use the Uploader's tag generator * Allow null file parameters + * enhancements to the HTML processing + * cleaned up the tag rendering. There is some more flexibility that needs to be added to the tag, but it looks like the core of it is working ok. + * correctly located the cloudinary tld and updated to use the new classname of the tag Added a singleton manager to ease spring support. + * renamed tag to make more sense + * First pass at an upload tag and support code + * Refactored Cloudinary Java into multiple modules without breaking the module naming convention already established. * Created a -taglib module to support constructing file input tags on the server side, since it requires some server side API signing. * Separate modules allow users who are writing stand-alone applications (not depending on the Servlet API) not to have a dependency on it. + * Fixing code sample, referencing Android + +cloudinary-1.0.2 / 2013-04-08 +============================= + + * Upgrade version to 1.0.2-SNAPSHOT + * Don't fail api tests if api_secret is not given + * Don't fail api tests if api_secret is not given + * pom fixes + * Preparation for Maven repository submission + * Merge Maven preperation by shakiba + * Missing file for rename test + * Invalidate flags in upload and destroy + * Private download link generator + * Support for short urls for image/upload + * Support for folders + * Support rename + * Support unsafe transformation update + * Fix tags api support of multiple public ids + * ready for maven central + * Fixing URLs in readme + * Support akamai + * Support for sprite genreation, multi and explode. Support new async/notification flags + * Merge git://github.com/andershedstrom/cloudinary_java + * Support for usage API call + * Support image_metadata flag in upload and API + * Update README.md + * fixed regexp bug, regexp didn't work + * Updated pom.xml to handle custom src and test-src directories + * Allow giving pages flag to resource details API + * Fix check for limit. Fix htmlWidth visibility + * Support for info flags in upload + * Support for transformation flags + * Support deleteResourcesByTag Support keep_original in resource deletion + * Uploader.imageUploadTag - helper for create input tag for direct upload to Cloudinary via JS + * Added README + * Java naming conventions. Map utility methods + * Initial commit diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index 8346e9d0..00000000 --- a/CHANGES.txt +++ /dev/null @@ -1,11 +0,0 @@ -1.0.11 - 2014-03-04 - new update method. add listing by moderation kind and status. add moderation status in listing. add moderation flag in upload. add moderation_status in update. add ocr, raw_conversion, categorization, detection, similarity_search and auto_tagging parameters in update and upload. add support for uploading large raw files -1.0.12 - 2014-03-04 - Fix handling of Booleans in uploader API -1.0.13 - 2014-04-29 - Allow passing ClientConnectionManager. Add support for opacity. Support upload_presets. Support unsigned uploads. Support start_at for resource listing. Support phash for upload and resource details. Support rate limit header in Api calls. -1.0.14 - 2014-07-29 - Add background_removal. Support return_delete_token in upload/update params. Support responsive and hidpi. Support custom coordinates. -1.1.0 - 2014-11-17 - Support folder listing API. Consolidate Android and Java client libraries. Support signed urls in tag helpers (image and url) -1.1.1 - 2014-12-18 - Support secure domain sharding. Don't sign version component. Support url suffix and use root path. Support tags in upload large. -1.1.2 - 2015-01-15 - fix support for string eager parameters -1.1.3 - 2015-02-24 - Added timeout parameter to admin api and Fixed test and configuration -1.2.0 - 2015-04-13 - Support httpcomponents 4.4. Support for video tag and transformations. support ftp url upload. support eager_async in explicit. Fix UTF-8 issues in API. Improved parameter support for upload_large. -1.2.1 - 2015-06-18 - Fix HTML escaping (fixes upload tags). Allow android unsigned upload without api_key. Fix http44 response closing. -1.2.2 - 2015-10-11 - support apache http 4.3 strategy. support easy overlay/underlay construction. support upload mappings api. support the restore api. normalize user agent. add invalidate flag to rename and explicit. support aspect ratio transformation param. Fix encoding issues when JVM default encoding is not UTF-8. support filename in upload options. close response objects in http44. \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3a5f4996..e9e8e2ef 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.2.2"; + public final static String VERSION = "1.3.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 7cdfef339afea55b7496de927569655463183ab6 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 19 Jan 2016 16:46:30 +0200 Subject: [PATCH 112/592] Modify version template to `x.y.z``. This matches other repositories. --- pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index 2ed5800e..c6fe2ca0 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,15 @@ + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + @{project.version} + + + From ccacc6a5e79c8438fc30e6cbd4d9e2806fee4f42 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 21 Jan 2016 14:40:43 +0200 Subject: [PATCH 113/592] Add `Transformation::toString()` --- .../main/java/com/cloudinary/Transformation.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index fdfef064..93b3fdf2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -382,10 +382,23 @@ public Transformation param(String key, Object value) { return this; } + /** + * Serialize this transformation object as a string + * + * {@code + * Transformation().width(100).height(101).generate(); // produces "h_101,w_100" + * } + * @return a String representation of the transformation + */ public String generate() { return generate(transformations); } + @Override + public String toString() { + return generate(); + } + public String generate(Iterable optionsList) { List components = new ArrayList(); for (Map options : optionsList) { From 0727e563ebb55df698beb8c9eb625022439b6115 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 21 Jan 2016 14:41:52 +0200 Subject: [PATCH 114/592] Fix `testResponsiveBreakpointsToJson()` --- .../com/cloudinary/test/CloudinaryTest.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) 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 439e6abe..db8ebece 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -3,9 +3,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -977,28 +975,20 @@ public void testOverlayError2() { @Test public void testResponsiveBreakpointsToJson() { assertEquals("an empty ResponsiveBreakpoint should have create_derived=true", - "[{\"create_derived\":true}]", + "{\"create_derived\":true}", new ResponsiveBreakpoint().toString() ); - JSONObject expected = new JSONObject("{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}"); + String[] expectedArr = "{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}".split("[{}]")[1].split(",(?=\")"); + Arrays.sort(expectedArr); JSONObject actual = new ResponsiveBreakpoint().createDerived(false) .transformation(new Transformation().angle(45)) .maxWidth(500) .minWidth(100) .maxImages(5) ; - assertTrue(actual.similar(expected)); - - JSONArray actualArray = new JSONArray(Arrays.asList( - new ResponsiveBreakpoint().createDerived(false) - .transformation(new Transformation().angle(45)) - .maxWidth(500) - .minWidth(100) - .maxImages(5), - new ResponsiveBreakpoint() - ));; - JSONArray expectedArray = new JSONArray("[{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"},{\"create_derived\":true}]"); - assertTrue(actualArray.similar(expectedArray)); + String[] actualArr = actual.toString().split("[{}]")[1].split(",(?=\")"); + Arrays.sort(actualArr); + assertArrayEquals(expectedArr, actualArr); } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { From 1b317a76e7209acc8b2b1378a71c06aadbdb14b0 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 23 Jan 2016 12:10:43 +0200 Subject: [PATCH 115/592] Fix testComplexFilenameOption to match server behavior --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 84ff6f34..1a9d3745 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -333,7 +333,7 @@ public void testFilenameOption() throws Exception { public void testComplexFilenameOption() throws Exception { String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); - complexFilename = URLEncoder.encode(URLDecoder.decode(complexFilename, "ASCII"), "UTF-8").replace("+", " ").replace(".png", ""); + complexFilename = complexFilename.replace(".png", ""); assertEquals(complexFilename, result.getString("original_filename")); } From 8a1887cc316e9ee6fc91c69a4bfbdaa1ed0e1816 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 23 Jan 2016 13:24:34 +0200 Subject: [PATCH 116/592] [maven-release-plugin] prepare release 1.3.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 5 +++-- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 1c6e8a74..88951d31 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 com.cloudinary cloudinary-android-test - 1.2.3-SNAPSHOT + 1.3.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.2.3-SNAPSHOT + 1.3.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index afb370e4..6d4a4379 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 018b3e8e..6c3756dd 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 3b3deae4..57260c7f 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index ac72b32f..5749fcd5 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index a4b90578..9b7fcc43 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 676675fc..279a1c8a 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index a6cd43b5..13dd66b8 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index c6fe2ca0..07930588 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.2.3-SNAPSHOT + 1.3.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,8 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - + 1.3.0 + UTF-8 From c1083482ccfd527f4bb4ff727701258298e03033 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 23 Jan 2016 13:24:39 +0200 Subject: [PATCH 117/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 88951d31..bee7596b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.3.0 + 1.3.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.3.0 + 1.3.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 6d4a4379..70305a72 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 6c3756dd..fcd0c6bc 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 57260c7f..26f36f37 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 5749fcd5..b937527d 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 9b7fcc43..3e75f9e4 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 279a1c8a..b99f1646 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 13dd66b8..a7ba1c25 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 07930588..e033ecf0 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.3.0 + 1.3.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.3.0 + HEAD From 296253216e2a1e31a310710f2f9fbd2084d2831c Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Mon, 25 Jan 2016 15:37:29 +0200 Subject: [PATCH 118/592] Fix support for non-ascii chars in upload URL --- .../src/main/java/com/cloudinary/http43/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/http44/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/test/AbstractUploaderTest.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 51cadf50..f609bc62 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -100,7 +100,7 @@ public Map callApi(String action, Map params, Map options, Objec 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); + 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); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 9ba820fb..7f0d4ab9 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -100,7 +100,7 @@ public Map callApi(String action, Map params, Map options, Objec 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); + 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); 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 c28d1bb7..2a9c2442 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 @@ -109,6 +109,7 @@ public void testUploadDataUri() throws IOException { public void testUploadUTF8() throws IOException { Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); + cloudinary.uploader().upload(result.get("url"), ObjectUtils.emptyMap()); } @Test From 29270d44b3a756d20ea2ab047bde0d3cb99bdd51 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 6 Feb 2016 21:22:42 +0200 Subject: [PATCH 119/592] Fix uploadLarge to use X-Unique-Upload-Id instead of updating params. Solves https://github.com/cloudinary/cloudinary_android/issues/18 --- .../com/cloudinary/android/MultipartUtility.java | 9 +++++++-- .../com/cloudinary/android/UploaderStrategy.java | 2 +- .../src/main/java/com/cloudinary/Uploader.java | 14 ++++++-------- .../com/cloudinary/http42/UploaderStrategy.java | 7 +++++-- .../com/cloudinary/http43/UploaderStrategy.java | 7 +++++-- .../com/cloudinary/http44/UploaderStrategy.java | 7 +++++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 76d3ccad..8d344ba3 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -10,6 +10,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.Map; import com.cloudinary.Cloudinary; @@ -40,7 +41,7 @@ public class MultipartUtility { * @param charset * @throws IOException */ - public MultipartUtility(String requestURL, String charset, String boundary, String contentRange) throws IOException { + public MultipartUtility(String requestURL, String charset, String boundary, Map headers) throws IOException { this.charset = charset; this.boundary = boundary; @@ -48,7 +49,11 @@ public MultipartUtility(String requestURL, String charset, String boundary, Stri httpConn = (HttpURLConnection) url.openConnection(); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); - if (contentRange != null) httpConn.setRequestProperty("Content-Range", contentRange); + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + httpConn.setRequestProperty(header.getKey(), header.getValue()); + } + } httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); httpConn.setRequestProperty("User-Agent", USER_AGENT); outputStream = httpConn.getOutputStream(); diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 2e32055a..0ddc14fc 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -47,7 +47,7 @@ public Map callApi(String action, Map params, Map options, Objec } } String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (String) options.get("content_range")); + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); // Remove blank parameters for (Map.Entry param : params.entrySet()) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 63297803..1cb59dc7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -98,12 +98,12 @@ public Map uploadLarge(Object file, Map options, int bufferSize) throws IOExcept private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length) throws IOException { Map params = buildUploadParams(options); - Map nextParams = new HashMap(); - nextParams.putAll(params); - Map sentParams = new HashMap(); Map sentOptions = new HashMap(); sentOptions.putAll(options); + Map extraHeaders = new HashMap(); + extraHeaders.put("X-Unique-Upload-Id", cloudinary().randomPublicId()); + sentOptions.put("extra_headers", extraHeaders); byte[] buffer = new byte[bufferSize]; byte[] nibbleBuffer = new byte[1]; @@ -120,8 +120,6 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon if (atEnd || fullBuffer) { totalBytes += currentBufferSize; - sentParams.clear(); - sentParams.putAll(nextParams); int currentLoc = bufferSize * partNumber; if (!atEnd) { //verify not on end - try read another byte @@ -135,10 +133,10 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon buffer = finalBuffer; } String range = String.format("bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); - sentOptions.put("content_range", range); + extraHeaders.put("Content-Range", range); + Map sentParams = new HashMap(); + sentParams.putAll(params); response = callApi("upload", sentParams, sentOptions, buffer); - nextParams.put("public_id", response.get("public_id")); - nextParams.put("upload_id", response.get("upload_id")); if (atEnd) break; buffer[0] = nibbleBuffer[0]; currentBufferSize = 1; diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index aeebd7a3..d9e1e769 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -60,8 +60,11 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); - if (options.get("content_range") != null) { - postMethod.setHeader("Content-Range", (String) options.get("content_range")); + 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"); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index f609bc62..b4168e0d 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -71,8 +71,11 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); - if (options.get("content_range") != null) { - postMethod.setHeader("Content-Range", (String) options.get("content_range")); + 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(); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 7f0d4ab9..ade0e157 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -71,8 +71,11 @@ public Map callApi(String action, Map params, Map options, Objec HttpPost postMethod = new HttpPost(apiUrl); - if (options.get("content_range") != null) { - postMethod.setHeader("Content-Range", (String) options.get("content_range")); + 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(); From 25416e78e02b46531c754f68aaff91037aa359ac Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 16 Mar 2016 11:45:23 +0200 Subject: [PATCH 120/592] Use variables for public_id's in rename tests. --- .../com/cloudinary/test/AbstractUploaderTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 2a9c2442..7a497b83 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 @@ -116,19 +116,21 @@ public void testUploadUTF8() throws IOException { public void testRename() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - cloudinary.uploader().rename((String) result.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); - assertNotNull(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap())); + Object publicId = result.get("public_id"); + String publicId2 = "folder/" + publicId + "2"; + cloudinary.uploader().rename((String) publicId, publicId2, ObjectUtils.emptyMap()); + assertNotNull(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap())); Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", ObjectUtils.emptyMap()); boolean error_found=false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.emptyMap()); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.emptyMap()); } catch(Exception e) { error_found=true; } assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id")+"2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); - assertEquals(cloudinary.api().resource(result.get("public_id")+"2", ObjectUtils.emptyMap()).get("format"), "ico"); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("overwrite", true)); + assertEquals(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap()).get("format"), "ico"); } @Test From 779b185abbb369f825a849743ffd6be239bb314c Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 2 Mar 2016 14:43:10 +0200 Subject: [PATCH 121/592] Whitespace Also removed redundant semicolons. --- .../src/main/java/com/cloudinary/Api.java | 173 +- .../java/com/cloudinary/ArchiveParams.java | 390 ++-- .../main/java/com/cloudinary/Cloudinary.java | 508 ++--- .../java/com/cloudinary/Configuration.java | 192 +- .../main/java/com/cloudinary/Coordinates.java | 118 +- .../com/cloudinary/EagerTransformation.java | 30 +- .../java/com/cloudinary/SmartUrlEncoder.java | 14 +- .../main/java/com/cloudinary/StoredFile.java | 238 +- .../java/com/cloudinary/Transformation.java | 1260 +++++------ .../main/java/com/cloudinary/Uploader.java | 744 +++---- .../src/main/java/com/cloudinary/Url.java | 1292 ++++++----- .../src/main/java/com/cloudinary/Util.java | 323 +-- .../java/com/cloudinary/api/ApiResponse.java | 5 +- .../cloudinary/api/AuthorizationRequired.java | 8 +- .../java/com/cloudinary/api/RateLimit.java | 48 +- .../api/exceptions/AlreadyExists.java | 8 +- .../api/exceptions/ApiException.java | 8 +- .../cloudinary/api/exceptions/BadRequest.java | 8 +- .../api/exceptions/GeneralError.java | 5 +- .../cloudinary/api/exceptions/NotAllowed.java | 5 +- .../cloudinary/api/exceptions/NotFound.java | 5 +- .../api/exceptions/RateLimited.java | 5 +- .../strategies/AbstractApiStrategy.java | 14 +- .../strategies/AbstractUploaderStrategy.java | 23 +- .../cloudinary/strategies/StrategyLoader.java | 48 +- .../transformation/AbstractLayer.java | 114 +- .../com/cloudinary/transformation/Layer.java | 8 +- .../transformation/LayerBuilder.java | 2 +- .../transformation/SubtitlesLayer.java | 6 +- .../cloudinary/transformation/TextLayer.java | 280 +-- .../com/cloudinary/utils/Base64Coder.java | 522 +++-- .../java/com/cloudinary/utils/HtmlEscape.java | 355 +-- .../com/cloudinary/utils/ObjectUtils.java | 282 +-- .../java/com/cloudinary/utils/Rectangle.java | 20 +- .../com/cloudinary/utils/StringUtils.java | 209 +- .../java/org/cloudinary/json/JSONArray.java | 1703 +++++++------- .../org/cloudinary/json/JSONException.java | 6 +- .../java/org/cloudinary/json/JSONObject.java | 501 ++--- .../java/org/cloudinary/json/JSONString.java | 1 + .../java/org/cloudinary/json/JSONTokener.java | 171 +- .../com/cloudinary/test/CloudinaryTest.java | 1959 ++++++++--------- .../cloudinary/transformation/LayerTest.java | 2 +- 42 files changed, 5717 insertions(+), 5896 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index eaecbefc..ac45b1b2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -17,11 +17,12 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) public class Api { - public enum HttpMethod { GET, POST, PUT, DELETE } - + public enum HttpMethod {GET, POST, PUT, DELETE} + public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); + static { CLOUDINARY_API_ERROR_CLASSES.put(400, BadRequest.class); CLOUDINARY_API_ERROR_CLASSES.put(401, AuthorizationRequired.class); @@ -32,14 +33,14 @@ public enum HttpMethod { GET, POST, PUT, DELETE } CLOUDINARY_API_ERROR_CLASSES.put(500, GeneralError.class); } - public final Cloudinary cloudinary; - private AbstractApiStrategy strategy; - + public final Cloudinary cloudinary; + private AbstractApiStrategy strategy; + protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - return this.strategy.callApi(method,uri,params,options); + return this.strategy.callApi(method, uri, params, options); } - - public Api(Cloudinary cloudinary,AbstractApiStrategy strategy) { + + public Api(Cloudinary cloudinary, AbstractApiStrategy strategy) { this.cloudinary = cloudinary; this.strategy = strategy; this.strategy.init(this); @@ -71,13 +72,13 @@ public ApiResponse resources(Map options) throws Exception { uri.add(type); return callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); } - + public ApiResponse resourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); } - + public ApiResponse resourcesByIds(Iterable publicIds, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); @@ -86,7 +87,7 @@ public ApiResponse resourcesByIds(Iterable publicIds, Map options) throw params.put("public_ids", publicIds); return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); } - + 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"); @@ -98,10 +99,10 @@ public ApiResponse resource(String public_id, Map options) throws Exception { String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); String type = ObjectUtils.asString(options.get("type"), "upload"); return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), - ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", - "image_metadata", "pages", "phash", "max_results"), options); + ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", + "image_metadata", "pages", "phash", "max_results"), options); } - + 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"); @@ -109,15 +110,15 @@ public ApiResponse update(String public_id, Map options) throws Exception { Map params = new HashMap(); Util.processWriteParameters(options, params); params.put("moderation_status", options.get("moderation_status")); - return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), - params, options); + return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), + params, options); } public ApiResponse deleteResources(Iterable publicIds, 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 = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); + Map params = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor"); params.put("public_ids", publicIds); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } @@ -126,7 +127,7 @@ public ApiResponse deleteResourcesByPrefix(String prefix, Map options) throws Ex 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 = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); + Map params = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor"); params.put("prefix", prefix); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } @@ -134,14 +135,14 @@ public ApiResponse deleteResourcesByPrefix(String prefix, Map options) throws Ex public ApiResponse deleteResourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"), options); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor"), options); } - + public ApiResponse deleteAllResources(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 filtered = ObjectUtils.only(options, "keep_original","invalidate", "next_cursor"); + Map filtered = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor"); filtered.put("all", true); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), filtered, options); } @@ -183,7 +184,7 @@ public ApiResponse updateTransformation(String transformation, Map updates, Map public ApiResponse createTransformation(String name, String definition, Map options) throws Exception { return callApi(HttpMethod.POST, Arrays.asList("transformations", name), ObjectUtils.asMap("transformation", definition), options); } - + public ApiResponse uploadPresets(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); return callApi(HttpMethod.GET, Arrays.asList("upload_presets"), ObjectUtils.only(options, "next_cursor", "max_results"), options); @@ -208,70 +209,70 @@ public ApiResponse updateUploadPreset(String name, Map options) throws Exception } public ApiResponse createUploadPreset(Map options) throws Exception { - if (options == null) options = ObjectUtils.emptyMap(); + if (options == null) options = ObjectUtils.emptyMap(); Map params = Util.buildUploadParams(options); Util.clearEmpty(params); params.putAll(ObjectUtils.only(options, "name", "unsigned", "disallow_public_id")); - return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); - } - - public ApiResponse rootFolders(Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders"), ObjectUtils.emptyMap(), options); - } - - public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); - } - - public ApiResponse restore(Iterable publicIds, 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 = new HashMap(); - params.put("public_ids", publicIds); - return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); - } - - public ApiResponse uploadMappings(Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), - ObjectUtils.only(options, "next_cursor", "max_results"), options); - } - - public ApiResponse uploadMapping(String name, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); - } - - public ApiResponse deleteUploadMapping(String name, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.DELETE, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); - } - - public ApiResponse updateUploadMapping(String name, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("folder", name); - params.putAll(ObjectUtils.only(options, "template")); - return callApi(HttpMethod.PUT, Arrays.asList("upload_mappings"), params, options); - } - - public ApiResponse createUploadMapping(String name, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("folder", name); - params.putAll(ObjectUtils.only(options, "template")); - return callApi(HttpMethod.POST, Arrays.asList("upload_mappings"), params, options); - } - + return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); + } + + public ApiResponse rootFolders(Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("folders"), ObjectUtils.emptyMap(), options); + } + + public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); + } + + public ApiResponse restore(Iterable publicIds, 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 = new HashMap(); + params.put("public_ids", publicIds); + return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); + } + + public ApiResponse uploadMappings(Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), + ObjectUtils.only(options, "next_cursor", "max_results"), options); + } + + public ApiResponse uploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.GET, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); + } + + public ApiResponse deleteUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.DELETE, Arrays.asList("upload_mappings"), ObjectUtils.asMap("folder", name), options); + } + + public ApiResponse updateUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("folder", name); + params.putAll(ObjectUtils.only(options, "template")); + return callApi(HttpMethod.PUT, Arrays.asList("upload_mappings"), params, options); + } + + public ApiResponse createUploadMapping(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("folder", name); + params.putAll(ObjectUtils.only(options, "template")); + return callApi(HttpMethod.POST, Arrays.asList("upload_mappings"), params, options); + } + } diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java index 548dada2..e0bb5b78 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -5,199 +5,199 @@ import java.util.Map; public class ArchiveParams { - public static final String FORMAT_ZIP = "zip"; - - public static final String MODE_DOWNLOAD = "download"; - public static final String MODE_CREATE = "create"; - - private String resourceType = "image"; - private String type = null; - private String mode = MODE_CREATE; - private String targetFormat = null; - private String targetPublicId = null; - private boolean flattenFolders = false; - private boolean flattenTransformations = false; - private boolean useOriginalFilename = false; - private boolean async = false; - private boolean keepDerived = false; - private String notificationUrl = null; - private String[] targetTags = null; - private String[] tags = null; - private String[] publicIds = null; - private String[] prefixes = null; - private Transformation[] transformations = null; - - public String resourceType() { - return resourceType; - } - - public ArchiveParams resourceType(String resourceType) { - if (resourceType == null) - throw new IllegalArgumentException("resource type must be non-null"); - this.resourceType = resourceType; - return this; - } - - public String type() { - return type; - } - - public ArchiveParams type(String type) { - this.type = type; - return this; - } - - public String mode() { - return mode; - } - - public ArchiveParams mode(String mode) { - this.mode = mode; - return this; - } - - public String targetFormat() { - return targetFormat; - } - - public ArchiveParams targetFormat(String targetFormat) { - this.targetFormat = targetFormat; - return this; - } - - public String targetPublicId() { - return targetPublicId; - } - - public ArchiveParams targetPublicId(String targetPublicId) { - this.targetPublicId = targetPublicId; - return this; - } - - public boolean isFlattenFolders() { - return flattenFolders; - } - - public ArchiveParams flattenFolders(boolean flattenFolders) { - this.flattenFolders = flattenFolders; - return this; - } - - public boolean isFlattenTransformations() { - return flattenTransformations; - } - - public ArchiveParams flattenTransformations(boolean flattenTransformations) { - this.flattenTransformations = flattenTransformations; - return this; - } - - public boolean isUseOriginalFilename() { - return useOriginalFilename; - } - - public ArchiveParams useOriginalFilename(boolean useOriginalFilename) { - this.useOriginalFilename = useOriginalFilename; - return this; - } - - public boolean isAsync() { - return async; - } - - public ArchiveParams async(boolean async) { - this.async = async; - return this; - } - - public boolean isKeepDerived() { - return keepDerived; - } - - public ArchiveParams keepDerived(boolean keepDerived) { - this.keepDerived = keepDerived; - return this; - } - - public String notificationUrl() { - return notificationUrl; - } - - public ArchiveParams notificationUrl(String notificationUrl) { - this.notificationUrl = notificationUrl; - return this; - } - - public String[] targetTags() { - return targetTags; - } - - public ArchiveParams targetTags(String[] targetTags) { - this.targetTags = targetTags; - return this; - } - - public String[] tags() { - return tags; - } - - public ArchiveParams tags(String[] tags) { - this.tags = tags; - return this; - } - - public String[] publicIds() { - return publicIds; - } - - public ArchiveParams publicIds(String[] publicIds) { - this.publicIds = publicIds; - return this; - } - - public String[] prefixes() { - return prefixes; - } - - public ArchiveParams prefixes(String[] prefixes) { - this.prefixes = prefixes; - return this; - } - - public Transformation[] transformations() { - return transformations; - } - - public ArchiveParams transformations(Transformation[] transformations) { - this.transformations = transformations; - return this; - } - - public Map toMap() { - Map params = new HashMap(); - params.put("resource_type", resourceType); - params.put("type", type); - params.put("mode", mode); - if (targetPublicId != null) - params.put("target_public_id", targetPublicId); - params.put("flatten_folders", flattenFolders); - params.put("flatten_transformations", flattenTransformations); - params.put("use_original_filename", useOriginalFilename); - params.put("async", async); - params.put("keep_derived", keepDerived); - if (notificationUrl != null) - params.put("notification_url", notificationUrl); - if (targetTags != null) - params.put("target_tags", targetTags); - if (tags != null) - params.put("tags", tags); - if (publicIds != null) - params.put("public_ids", publicIds); - if (prefixes != null) - params.put("prefixes", prefixes); - if (transformations != null) { - params.put("transformations", Arrays.asList(transformations)); - } - return params; - } + public static final String FORMAT_ZIP = "zip"; + + public static final String MODE_DOWNLOAD = "download"; + public static final String MODE_CREATE = "create"; + + private String resourceType = "image"; + private String type = null; + private String mode = MODE_CREATE; + private String targetFormat = null; + private String targetPublicId = null; + private boolean flattenFolders = false; + private boolean flattenTransformations = false; + private boolean useOriginalFilename = false; + private boolean async = false; + private boolean keepDerived = false; + private String notificationUrl = null; + private String[] targetTags = null; + private String[] tags = null; + private String[] publicIds = null; + private String[] prefixes = null; + private Transformation[] transformations = null; + + public String resourceType() { + return resourceType; + } + + public ArchiveParams resourceType(String resourceType) { + if (resourceType == null) + throw new IllegalArgumentException("resource type must be non-null"); + this.resourceType = resourceType; + return this; + } + + public String type() { + return type; + } + + public ArchiveParams type(String type) { + this.type = type; + return this; + } + + public String mode() { + return mode; + } + + public ArchiveParams mode(String mode) { + this.mode = mode; + return this; + } + + public String targetFormat() { + return targetFormat; + } + + public ArchiveParams targetFormat(String targetFormat) { + this.targetFormat = targetFormat; + return this; + } + + public String targetPublicId() { + return targetPublicId; + } + + public ArchiveParams targetPublicId(String targetPublicId) { + this.targetPublicId = targetPublicId; + return this; + } + + public boolean isFlattenFolders() { + return flattenFolders; + } + + public ArchiveParams flattenFolders(boolean flattenFolders) { + this.flattenFolders = flattenFolders; + return this; + } + + public boolean isFlattenTransformations() { + return flattenTransformations; + } + + public ArchiveParams flattenTransformations(boolean flattenTransformations) { + this.flattenTransformations = flattenTransformations; + return this; + } + + public boolean isUseOriginalFilename() { + return useOriginalFilename; + } + + public ArchiveParams useOriginalFilename(boolean useOriginalFilename) { + this.useOriginalFilename = useOriginalFilename; + return this; + } + + public boolean isAsync() { + return async; + } + + public ArchiveParams async(boolean async) { + this.async = async; + return this; + } + + public boolean isKeepDerived() { + return keepDerived; + } + + public ArchiveParams keepDerived(boolean keepDerived) { + this.keepDerived = keepDerived; + return this; + } + + public String notificationUrl() { + return notificationUrl; + } + + public ArchiveParams notificationUrl(String notificationUrl) { + this.notificationUrl = notificationUrl; + return this; + } + + public String[] targetTags() { + return targetTags; + } + + public ArchiveParams targetTags(String[] targetTags) { + this.targetTags = targetTags; + return this; + } + + public String[] tags() { + return tags; + } + + public ArchiveParams tags(String[] tags) { + this.tags = tags; + return this; + } + + public String[] publicIds() { + return publicIds; + } + + public ArchiveParams publicIds(String[] publicIds) { + this.publicIds = publicIds; + return this; + } + + public String[] prefixes() { + return prefixes; + } + + public ArchiveParams prefixes(String[] prefixes) { + this.prefixes = prefixes; + return this; + } + + public Transformation[] transformations() { + return transformations; + } + + public ArchiveParams transformations(Transformation[] transformations) { + this.transformations = transformations; + return this; + } + + public Map toMap() { + Map params = new HashMap(); + params.put("resource_type", resourceType); + params.put("type", type); + params.put("mode", mode); + if (targetPublicId != null) + params.put("target_public_id", targetPublicId); + params.put("flatten_folders", flattenFolders); + params.put("flatten_transformations", flattenTransformations); + params.put("use_original_filename", useOriginalFilename); + params.put("async", async); + params.put("keep_derived", keepDerived); + if (notificationUrl != null) + params.put("notification_url", notificationUrl); + if (targetTags != null) + params.put("target_tags", targetTags); + if (tags != null) + params.put("tags", tags); + if (publicIds != null) + params.put("public_ids", publicIds); + if (prefixes != null) + params.put("prefixes", prefixes); + if (transformations != null) { + params.put("transformations", Arrays.asList(transformations)); + } + return params; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index e9e8e2ef..312103d4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -21,260 +21,260 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) 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")); - private static List API_STRATEGIES = new ArrayList(Arrays.asList( - "com.cloudinary.android.ApiStrategy", - "com.cloudinary.http42.ApiStrategy", - "com.cloudinary.http43.ApiStrategy", - "com.cloudinary.http44.ApiStrategy" )); - - public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; - public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; - 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.3.0"; - public final static String USER_AGENT = "CloudinaryJava/" + VERSION; - - public final Configuration config; - private AbstractUploaderStrategy uploaderStrategy; - private AbstractApiStrategy apiStrategy; - - public Uploader uploader(){ - return new Uploader(this,uploaderStrategy); - - }; - - public Api api(){ - return new Api(this,apiStrategy); - }; - - public static void registerUploaderStrategy(String className){ - if (!UPLOAD_STRATEGIES.contains(className)){ - UPLOAD_STRATEGIES.add(className); - } - - } - - public static void registerAPIStrategy(String className){ - if (!API_STRATEGIES.contains(className)){ - API_STRATEGIES.add(className); - } - } - - private void loadStrategies() { - if (!this.config.loadStrategies) return; - uploaderStrategy= StrategyLoader.find(UPLOAD_STRATEGIES); - - if (uploaderStrategy==null){ - throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); - } - - apiStrategy= StrategyLoader.find(API_STRATEGIES); - if (apiStrategy==null){ - throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); - } - } - - public Cloudinary(Map config) { - this.config = new Configuration(config); - loadStrategies(); - } - - public Cloudinary(String cloudinaryUrl) { - this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); - loadStrategies(); - } - - public Cloudinary() { - String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); - if (cloudinaryUrl != null) { - this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); - }else { - this.config = new Configuration(); - } - loadStrategies(); - } - - public Url url() { - return new Url(this); - } - - public String cloudinaryApiUrl(String action, Map options) { - String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), - ObjectUtils.asString(this.config.uploadPrefix, "https://api.cloudinary.com")); - String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.cloudName)); - if (cloud_name == null) - throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); - String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); - return StringUtils.join(new String[] { cloudinary, "v1_1", cloud_name, resource_type, action }, "/"); - } - - private final static SecureRandom RND = new SecureRandom(); - - public String randomPublicId() { - byte[] bytes = new byte[8]; - RND.nextBytes(bytes); - return StringUtils.encodeHexString(bytes); - } - - public String signedPreloadedImage(Map result) { - return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") - + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); - } - - public String apiSignRequest(Map paramsToSign, String apiSecret) { - 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()); - } - } - } - String to_sign = StringUtils.join(params, "&"); - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); - return StringUtils.encodeHexString(digest); - } - - public void signRequest(Map params, Map options) { - String apiKey = ObjectUtils.asString(options.get("api_key"), this.config.apiKey); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - Util.clearEmpty(params); - params.put("signature", this.apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); - } - - public String privateDownload(String publicId, String format, Map options) throws Exception { - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("format", format); - params.put("attachment", options.get("attachment")); - params.put("type", options.get("type")); - params.put("timestamp", Util.timestamp()); - signRequest(params, options); - return buildUrl(cloudinaryApiUrl("download", options), params); - } - - public String zipDownload(String tag, Map options) throws Exception { - Map params = new HashMap(); - params.put("timestamp", Util.timestamp()); - params.put("tag", tag); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("transformation", transformation); - signRequest(params, options); - return buildUrl(cloudinaryApiUrl("download_tag.zip", options), params); - } - - public String downloadArchive(Map options, String targetFormat) throws UnsupportedEncodingException { - Map params = Util.buildArchiveParams(options, targetFormat); - params.put("mode", ArchiveParams.MODE_DOWNLOAD); - signRequest(params, options); - return buildUrl(cloudinaryApiUrl("generate_archive", options), params); - } - - public String downloadArchive(ArchiveParams params) throws UnsupportedEncodingException { - return downloadArchive(params.toMap(), params.targetFormat()); - } - - public String downloadZip(Map options) throws UnsupportedEncodingException { - return downloadArchive(options, "zip"); - } - - - private String buildUrl(String base, Map params) throws UnsupportedEncodingException { - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(base); - if (!params.isEmpty()) { - urlBuilder.append("?"); - } - boolean first = true; - for (Map.Entry param : params.entrySet()) { - String keyValue = null; - Object value = param.getValue(); - if (!first) urlBuilder.append("&"); - if (value instanceof Object[]) - value = Arrays.asList(value); - if (value instanceof Collection) { - String key = param.getKey() + "[]="; - Collection items = (Collection) value; - List encodedItems = new ArrayList(); - for (Object item : items) - encodedItems.add(URLEncoder.encode(item.toString(), "UTF-8")); - keyValue = key + StringUtils.join(encodedItems, "&" + key); - } else { - keyValue = param.getKey() + "=" + - URLEncoder.encode(value.toString(), "UTF-8"); - } - urlBuilder.append(keyValue); - first = false; - } - return urlBuilder.toString(); - } - - protected Map parseConfigUrl(String cloudinaryUrl) { - Map params = new HashMap(); - URI cloudinaryUri = URI.create(cloudinaryUrl); - params.put("cloud_name", cloudinaryUri.getHost()); - if (cloudinaryUri.getUserInfo() != null) { - String[] creds = cloudinaryUri.getUserInfo().split(":"); - params.put("api_key", creds[0]); - if (creds.length > 1) { - params.put("api_secret", creds[1]); - } - } - params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); - params.put("secure_distribution", cloudinaryUri.getPath()); - if (cloudinaryUri.getQuery() != null) { - for (String param : cloudinaryUri.getQuery().split("&")) { - String[] keyValue = param.split("="); - try { - params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - } - return params; - } - - byte[] getUTF8Bytes(String string) { - try { - return string.getBytes("UTF-8"); - } catch (java.io.UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - - @Deprecated - public static Map asMap(Object... values) { - return ObjectUtils.asMap(values); - } + 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")); + private static List API_STRATEGIES = new ArrayList(Arrays.asList( + "com.cloudinary.android.ApiStrategy", + "com.cloudinary.http42.ApiStrategy", + "com.cloudinary.http43.ApiStrategy", + "com.cloudinary.http44.ApiStrategy")); + + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; + public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; + 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.3.0"; + public final static String USER_AGENT = "CloudinaryJava/" + VERSION; + + public final Configuration config; + private AbstractUploaderStrategy uploaderStrategy; + private AbstractApiStrategy apiStrategy; + + public Uploader uploader() { + return new Uploader(this, uploaderStrategy); + + } + + public Api api() { + return new Api(this, apiStrategy); + } + + public static void registerUploaderStrategy(String className) { + if (!UPLOAD_STRATEGIES.contains(className)) { + UPLOAD_STRATEGIES.add(className); + } + + } + + public static void registerAPIStrategy(String className) { + if (!API_STRATEGIES.contains(className)) { + API_STRATEGIES.add(className); + } + } + + private void loadStrategies() { + if (!this.config.loadStrategies) return; + uploaderStrategy = StrategyLoader.find(UPLOAD_STRATEGIES); + + if (uploaderStrategy == null) { + throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(UPLOAD_STRATEGIES, ",") + "]"); + } + + apiStrategy = StrategyLoader.find(API_STRATEGIES); + if (apiStrategy == null) { + throw new UnknownError("Can't find Cloudinary platform adapter [" + StringUtils.join(API_STRATEGIES, ",") + "]"); + } + } + + public Cloudinary(Map config) { + this.config = new Configuration(config); + loadStrategies(); + } + + public Cloudinary(String cloudinaryUrl) { + this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + loadStrategies(); + } + + public Cloudinary() { + String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); + if (cloudinaryUrl != null) { + this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + } else { + this.config = new Configuration(); + } + loadStrategies(); + } + + public Url url() { + return new Url(this); + } + + public String cloudinaryApiUrl(String action, Map options) { + String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), + ObjectUtils.asString(this.config.uploadPrefix, "https://api.cloudinary.com")); + String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(this.config.cloudName)); + if (cloud_name == null) + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); + return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, resource_type, action}, "/"); + } + + private final static SecureRandom RND = new SecureRandom(); + + public String randomPublicId() { + byte[] bytes = new byte[8]; + RND.nextBytes(bytes); + return StringUtils.encodeHexString(bytes); + } + + public String signedPreloadedImage(Map result) { + return result.get("resource_type") + "/upload/v" + result.get("version") + "/" + result.get("public_id") + + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); + } + + public String apiSignRequest(Map paramsToSign, String apiSecret) { + 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()); + } + } + } + String to_sign = StringUtils.join(params, "&"); + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); + return StringUtils.encodeHexString(digest); + } + + public void signRequest(Map params, Map options) { + String apiKey = ObjectUtils.asString(options.get("api_key"), this.config.apiKey); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + Util.clearEmpty(params); + params.put("signature", this.apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + } + + public String privateDownload(String publicId, String format, Map options) throws Exception { + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("format", format); + params.put("attachment", options.get("attachment")); + params.put("type", options.get("type")); + params.put("timestamp", Util.timestamp()); + signRequest(params, options); + return buildUrl(cloudinaryApiUrl("download", options), params); + } + + public String zipDownload(String tag, Map options) throws Exception { + Map params = new HashMap(); + params.put("timestamp", Util.timestamp()); + params.put("tag", tag); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("transformation", transformation); + signRequest(params, options); + return buildUrl(cloudinaryApiUrl("download_tag.zip", options), params); + } + + public String downloadArchive(Map options, String targetFormat) throws UnsupportedEncodingException { + Map params = Util.buildArchiveParams(options, targetFormat); + params.put("mode", ArchiveParams.MODE_DOWNLOAD); + signRequest(params, options); + return buildUrl(cloudinaryApiUrl("generate_archive", options), params); + } + + public String downloadArchive(ArchiveParams params) throws UnsupportedEncodingException { + return downloadArchive(params.toMap(), params.targetFormat()); + } + + public String downloadZip(Map options) throws UnsupportedEncodingException { + return downloadArchive(options, "zip"); + } + + + private String buildUrl(String base, Map params) throws UnsupportedEncodingException { + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(base); + if (!params.isEmpty()) { + urlBuilder.append("?"); + } + boolean first = true; + for (Map.Entry param : params.entrySet()) { + String keyValue = null; + Object value = param.getValue(); + if (!first) urlBuilder.append("&"); + if (value instanceof Object[]) + value = Arrays.asList(value); + if (value instanceof Collection) { + String key = param.getKey() + "[]="; + Collection items = (Collection) value; + List encodedItems = new ArrayList(); + for (Object item : items) + encodedItems.add(URLEncoder.encode(item.toString(), "UTF-8")); + keyValue = key + StringUtils.join(encodedItems, "&" + key); + } else { + keyValue = param.getKey() + "=" + + URLEncoder.encode(value.toString(), "UTF-8"); + } + urlBuilder.append(keyValue); + first = false; + } + return urlBuilder.toString(); + } + + protected Map parseConfigUrl(String cloudinaryUrl) { + Map params = new HashMap(); + URI cloudinaryUri = URI.create(cloudinaryUrl); + params.put("cloud_name", cloudinaryUri.getHost()); + if (cloudinaryUri.getUserInfo() != null) { + String[] creds = cloudinaryUri.getUserInfo().split(":"); + params.put("api_key", creds[0]); + if (creds.length > 1) { + params.put("api_secret", creds[1]); + } + } + params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); + params.put("secure_distribution", cloudinaryUri.getPath()); + if (cloudinaryUri.getQuery() != null) { + for (String param : cloudinaryUri.getQuery().split("&")) { + String[] keyValue = param.split("="); + try { + params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + } + return params; + } + + byte[] getUTF8Bytes(String string) { + try { + return string.getBytes("UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + + @Deprecated + public static Map asMap(Object... values) { + return ObjectUtils.asMap(values); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index b38a1dba..10d22752 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -10,16 +10,16 @@ import com.cloudinary.utils.StringUtils; /** -* Configuration object for a {@link Cloudinary} instance -*/ + * Configuration object for a {@link Cloudinary} instance + */ public class Configuration { - public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; - public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; - 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.0.2"; - public final static String USER_AGENT = "cld-android-" + VERSION; - + public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; + public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; + 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.0.2"; + public final static String USER_AGENT = "cld-android-" + VERSION; + public String cloudName; public String apiKey; public String apiSecret; @@ -30,16 +30,16 @@ public class Configuration { public boolean privateCdn; public boolean cdnSubdomain; public boolean shorten; - public String callback; - public String proxyHost; - public int proxyPort; + public String callback; + public String proxyHost; + public int proxyPort; public Map properties = new HashMap(); - public Boolean secureCdnSubdomain; - public boolean useRootPath; + public Boolean secureCdnSubdomain; + public boolean useRootPath; public int timeout; public boolean loadStrategies = true; - public Configuration(){ + public Configuration() { } private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath) { @@ -66,57 +66,54 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.loadStrategies = loadStrategies; } - @SuppressWarnings("rawtypes") - public Configuration(Map config) { - update(config); - } - - @SuppressWarnings("rawtypes") - public void update(Map config) { - this.cloudName = (String) config.get("cloud_name"); - this.apiKey = (String) config.get("api_key"); - 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.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); - this.uploadPrefix = (String) config.get("upload_prefix"); - this.callback = (String) config.get("callback"); - this.proxyHost = (String) config.get("proxy_host"); - this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"),0); - this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); - this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); - this.loadStrategies = ObjectUtils.asBoolean(config.get("load_strategies"), true); + public Configuration(Map config) { + update(config); + } + + @SuppressWarnings("rawtypes") + public void update(Map config) { + this.cloudName = (String) config.get("cloud_name"); + this.apiKey = (String) config.get("api_key"); + 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.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); + this.uploadPrefix = (String) config.get("upload_prefix"); + this.callback = (String) config.get("callback"); + this.proxyHost = (String) config.get("proxy_host"); + this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"), 0); + this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); + this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); + this.loadStrategies = ObjectUtils.asBoolean(config.get("load_strategies"), true); this.timeout = ObjectUtils.asInteger(config.get("timeout"), 0); - } - - - - public Configuration(Configuration other) { - this.cloudName = other.cloudName; - this.apiKey = other.apiKey; - this.apiSecret = other.apiSecret; - this.secureDistribution = other.secureDistribution; - this.cname = other.cname; - this.uploadPrefix = other.uploadPrefix; - this.secure = other.secure; - this.privateCdn = other.privateCdn; - this.cdnSubdomain = other.cdnSubdomain; - this.shorten = other.shorten; - this.callback = other.callback; - this.proxyHost = other.proxyHost; - this.proxyPort = other.proxyPort; - this.secureCdnSubdomain = other.secureCdnSubdomain; - this.useRootPath = other.useRootPath; - this.timeout = other.timeout; - } + } + public Configuration(Configuration other) { + this.cloudName = other.cloudName; + this.apiKey = other.apiKey; + this.apiSecret = other.apiSecret; + this.secureDistribution = other.secureDistribution; + this.cname = other.cname; + this.uploadPrefix = other.uploadPrefix; + this.secure = other.secure; + this.privateCdn = other.privateCdn; + this.cdnSubdomain = other.cdnSubdomain; + this.shorten = other.shorten; + this.callback = other.callback; + this.proxyHost = other.proxyHost; + this.proxyPort = other.proxyPort; + this.secureCdnSubdomain = other.secureCdnSubdomain; + this.useRootPath = other.useRootPath; + this.timeout = other.timeout; + } - /** + /** * Create a new Configuration from an existing one + * * @param other * @return a new configuration with the arguments supplied by another configuration object */ @@ -126,7 +123,7 @@ public static Configuration from(Configuration other) { /** * Create a Configuration from a cloudinary url - * + *

* Example url: cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7 * * @param cloudinaryUrl configuration url @@ -136,7 +133,6 @@ public static Configuration from(String cloudinaryUrl) { return from(parseConfigUrl(cloudinaryUrl)); } - private static Configuration parseConfigUrl(String cloudinaryUrl) { Builder builder = new Builder(); @@ -159,40 +155,38 @@ private static Configuration parseConfigUrl(String cloudinaryUrl) { } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Error decoding cloudinaryUrl", e); } - + String key = keyValue[0]; - if (key.equals("cname")){ + if (key.equals("cname")) { builder.setCname(val); - }else if (key.equals("upload_prefix")){ - builder.setUploadPrefix(val); - }else if (key.equals("secure")){ - builder.setSecure(ObjectUtils.asBoolean(val, false)); - }else if (key.equals("cdn_subdomain")){ - builder.setCdnSubdomain(ObjectUtils.asBoolean(val, false)); - }else if (key.equals("shorten")){ - builder.setShorten(ObjectUtils.asBoolean(val, false)); - } else if (key.equals("load_strategies")){ - builder.setLoadStrategies(ObjectUtils.asBoolean(val, true)); + } else if (key.equals("upload_prefix")) { + builder.setUploadPrefix(val); + } else if (key.equals("secure")) { + builder.setSecure(ObjectUtils.asBoolean(val, false)); + } else if (key.equals("cdn_subdomain")) { + builder.setCdnSubdomain(ObjectUtils.asBoolean(val, false)); + } else if (key.equals("shorten")) { + builder.setShorten(ObjectUtils.asBoolean(val, false)); + } else if (key.equals("load_strategies")) { + builder.setLoadStrategies(ObjectUtils.asBoolean(val, true)); } else { // Log.w("Cloudinary", "ignoring invalid parameter " + val); } - } } return builder.build(); } - /** * Build a new {@link Configuration} */ public static class Builder { - private String cloudName; - private String apiKey; - private String apiSecret; - private String secureDistribution; - private String cname; - private String uploadPrefix; + private String cloudName; + private String apiKey; + private String apiSecret; + private String secureDistribution; + private String cname; + private String uploadPrefix; private boolean secure; private boolean privateCdn; private boolean cdnSubdomain; @@ -207,6 +201,7 @@ public static class Builder { /** * Set the HTTP connection timeout. + * * @param timeout time in milliseconds, or 0 to use the default platform value * @return builder for chaining */ @@ -215,11 +210,12 @@ public Builder setTimeout(int timeout) { return this; } - /** * Creates a {@link Configuration} with the arguments supplied to this builder */ - public Configuration build() { return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten,callback,proxyHost,proxyPort,secureCdnSubdomain,useRootPath, timeout, loadStrategies); } + public Configuration build() { + return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies); + } /** * The unique name of your cloud at Cloudinary @@ -287,7 +283,7 @@ public Builder setSecureCdnSubdomain(Boolean secureCdnSubdomain) { return this; } - + /** * Whether to automatically build URLs with multiple CDN sub-domains. */ @@ -300,7 +296,7 @@ public Builder setShorten(boolean shorten) { this.shorten = shorten; return this; } - + public Builder setCallback(String callback) { this.callback = callback; return this; @@ -310,35 +306,34 @@ public Builder setUploadPrefix(String uploadPrefix) { this.uploadPrefix = uploadPrefix; return this; } - + public Builder setUseRootPath(boolean useRootPath) { this.useRootPath = useRootPath; return this; } - + public Builder setLoadStrategies(boolean loadStrategies) { this.loadStrategies = loadStrategies; return this; } - - - + /** * Initialize builder from existing {@link Configuration} + * * @param other a different configuration object * @return an initialized builder configured with other */ public Builder from(Configuration other) { - this.cloudName = other.cloudName; - this.apiKey = other.apiKey; - this.apiSecret = other.apiSecret; + this.cloudName = other.cloudName; + this.apiKey = other.apiKey; + this.apiSecret = other.apiSecret; this.secureDistribution = other.secureDistribution; - this.cname = other.cname; + this.cname = other.cname; this.uploadPrefix = other.uploadPrefix; - this.secure = other.secure; + this.secure = other.secure; this.privateCdn = other.privateCdn; this.cdnSubdomain = other.cdnSubdomain; - this.shorten = other.shorten; + this.shorten = other.shorten; this.callback = other.callback; this.proxyHost = other.proxyHost; this.proxyPort = other.proxyPort; @@ -348,6 +343,5 @@ public Builder from(Configuration other) { this.timeout = other.timeout; return this; } - } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java index 3c08b4a6..92749cfa 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java @@ -8,73 +8,73 @@ public class Coordinates { - Collection coordinates = new ArrayList(); + Collection coordinates = new ArrayList(); - public Coordinates() { - } + public Coordinates() { + } - public Coordinates(Collection coordinates) { - this.coordinates = coordinates; - } + public Coordinates(Collection coordinates) { + this.coordinates = coordinates; + } - public Coordinates(int[] rect) { - Collection coordinates = new ArrayList(); - if (rect.length != 4) { - throw new IllegalArgumentException("Must supply exactly 4 values for coordinates (x,y,width,height)"); - } - coordinates.add(new Rectangle(rect[0], rect[1], rect[2], rect[3])); - this.coordinates = coordinates; - } + public Coordinates(int[] rect) { + Collection coordinates = new ArrayList(); + if (rect.length != 4) { + throw new IllegalArgumentException("Must supply exactly 4 values for coordinates (x,y,width,height)"); + } + coordinates.add(new Rectangle(rect[0], rect[1], rect[2], rect[3])); + this.coordinates = coordinates; + } - public Coordinates(Rectangle rect) { - Collection coordinates = new ArrayList(); - coordinates.add(rect); - this.coordinates = coordinates; - } + public Coordinates(Rectangle rect) { + Collection coordinates = new ArrayList(); + coordinates.add(rect); + this.coordinates = coordinates; + } - public Coordinates(String stringCoords) throws IllegalArgumentException { - Collection coordinates = new ArrayList(); - for (String stringRect : stringCoords.split("\\|")) { - if (StringUtils.isEmpty(stringRect)) - continue; - String[] elements = stringRect.split(","); - if (elements.length != 4) { - throw new IllegalArgumentException(String.format("Must supply exactly 4 values for coordinates (x,y,width,height) %d supplied: %s", - elements.length, stringRect)); - } - coordinates.add(new Rectangle(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2]), Integer - .parseInt(elements[3]))); - } - this.coordinates = coordinates; - } + public Coordinates(String stringCoords) throws IllegalArgumentException { + Collection coordinates = new ArrayList(); + for (String stringRect : stringCoords.split("\\|")) { + if (StringUtils.isEmpty(stringRect)) + continue; + String[] elements = stringRect.split(","); + if (elements.length != 4) { + throw new IllegalArgumentException(String.format("Must supply exactly 4 values for coordinates (x,y,width,height) %d supplied: %s", + elements.length, stringRect)); + } + coordinates.add(new Rectangle(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2]), Integer + .parseInt(elements[3]))); + } + this.coordinates = coordinates; + } - public static Coordinates parseCoordinates(Object coordinates) throws IllegalArgumentException { - if (coordinates instanceof Coordinates) { - return (Coordinates) coordinates; - } else if (coordinates instanceof int[]) { - return new Coordinates((int[]) coordinates); - } else if (coordinates instanceof Rectangle) { - return new Coordinates((Rectangle) coordinates); - } else { - return new Coordinates(coordinates.toString()); - } - } + public static Coordinates parseCoordinates(Object coordinates) throws IllegalArgumentException { + if (coordinates instanceof Coordinates) { + return (Coordinates) coordinates; + } else if (coordinates instanceof int[]) { + return new Coordinates((int[]) coordinates); + } else if (coordinates instanceof Rectangle) { + return new Coordinates((Rectangle) coordinates); + } else { + return new Coordinates(coordinates.toString()); + } + } - public void addRect(Rectangle rect) { - this.coordinates.add(rect); - } + public void addRect(Rectangle rect) { + this.coordinates.add(rect); + } - public Collection underlaying() { - return this.coordinates; - } + public Collection underlaying() { + return this.coordinates; + } - @Override - public String toString() { - ArrayList rects = new ArrayList(); - for (Rectangle rect : this.coordinates) { - rects.add(rect.x + "," + rect.y + "," + rect.width + "," + rect.height); - } - return StringUtils.join(rects, "|"); - } + @Override + public String toString() { + ArrayList rects = new ArrayList(); + for (Rectangle rect : this.coordinates) { + rects.add(rect.x + "," + rect.y + "," + rect.width + "," + rect.height); + } + return StringUtils.join(rects, "|"); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index d774e6e5..1f7ebc0b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -4,23 +4,23 @@ import java.util.Map; public class EagerTransformation extends Transformation { - protected String format; + protected String format; - @SuppressWarnings("rawtypes") - public EagerTransformation(List transformations) { - super(transformations); - } + @SuppressWarnings("rawtypes") + public EagerTransformation(List transformations) { + super(transformations); + } - public EagerTransformation() { - super(); - } + public EagerTransformation() { + super(); + } - public EagerTransformation format(String format) { - this.format = format; - return this; - } + public EagerTransformation format(String format) { + this.format = format; + return this; + } - public String getFormat() { - return format; - } + public String getFormat() { + return format; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java index 26de3239..bcd8f654 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java @@ -4,11 +4,11 @@ import java.net.URLEncoder; public class SmartUrlEncoder { - public static String encode(String input) { - try { - return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } + public static String encode(String input) { + try { + return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java index 3f1cae8c..04e5ccca 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java +++ b/cloudinary-core/src/main/java/com/cloudinary/StoredFile.java @@ -6,123 +6,123 @@ import java.util.regex.Pattern; public class StoredFile { - protected Long version; - - protected String publicId; - - protected String format; - - protected String signature; - - protected String type = "upload"; - - protected String resourceType = "image"; - - private static final String IMAGE_RESOURCE_TYPE = "image"; - - private static final String VIDEO_RESOURCE_TYPE = "video"; - - private static final String AUTO_RESOURCE_TYPE = "auto"; - - private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); - - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - public String getPublicId() { - return publicId; - } - - public void setPublicId(String publicId) { - this.publicId = publicId; - } - - protected String getPublicIdForSigning() { - return publicId + ((format != null && !format.isEmpty() && resourceType.equals("raw")) ? "." + format : ""); - } - - public String getFormat() { - return format; - } - - public void setFormat(String format) { - this.format = format; - } - - public String getSignature() { - return signature; - } - - public void setSignature(String signature) { - this.signature = signature; - } - - public String getResourceType() { - return resourceType; - } - - public void setResourceType(String resourceType) { - this.resourceType = resourceType; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getPreloadedFile() { - StringBuilder sb = new StringBuilder(); - sb.append(resourceType).append("/").append(type).append("/v").append(version).append("/").append(publicId); - if (format != null && !format.isEmpty()) { - sb.append(".").append(format); - } - if (signature != null && !signature.isEmpty()) { - sb.append("#").append(signature); - } - return sb.toString(); - } - - public void setPreloadedFile(String uri) { - if (uri.matches(PRELOADED_PATTERN.pattern())) { - Matcher match = PRELOADED_PATTERN.matcher(uri); - match.find(); - resourceType = match.group(1); - type = match.group(2); - version = Long.parseLong(match.group(3)); - String filename = match.group(4); - if (match.groupCount() == 5) - signature = match.group(5); - int lastDotIndex = filename.lastIndexOf('.'); - if (lastDotIndex == -1) { - publicId = filename; - } else { - publicId = filename.substring(0, lastDotIndex); - format = filename.substring(lastDotIndex + 1); - } - } - } - - public String getComputedSignature(Cloudinary cloudinary) { - Map params = new HashMap(); - params.put("version", getVersion().toString()); - params.put("public_id", getPublicIdForSigning()); - cloudinary.signRequest(params, new HashMap()); - return params.get("signature").toString(); - } - - public boolean getIsImage() { - return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); - } - - public boolean getIsVideo() { - return VIDEO_RESOURCE_TYPE.equals(resourceType); - } + protected Long version; + + protected String publicId; + + protected String format; + + protected String signature; + + protected String type = "upload"; + + protected String resourceType = "image"; + + private static final String IMAGE_RESOURCE_TYPE = "image"; + + private static final String VIDEO_RESOURCE_TYPE = "video"; + + private static final String AUTO_RESOURCE_TYPE = "auto"; + + private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getPublicId() { + return publicId; + } + + public void setPublicId(String publicId) { + this.publicId = publicId; + } + + protected String getPublicIdForSigning() { + return publicId + ((format != null && !format.isEmpty() && resourceType.equals("raw")) ? "." + format : ""); + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPreloadedFile() { + StringBuilder sb = new StringBuilder(); + sb.append(resourceType).append("/").append(type).append("/v").append(version).append("/").append(publicId); + if (format != null && !format.isEmpty()) { + sb.append(".").append(format); + } + if (signature != null && !signature.isEmpty()) { + sb.append("#").append(signature); + } + return sb.toString(); + } + + public void setPreloadedFile(String uri) { + if (uri.matches(PRELOADED_PATTERN.pattern())) { + Matcher match = PRELOADED_PATTERN.matcher(uri); + match.find(); + resourceType = match.group(1); + type = match.group(2); + version = Long.parseLong(match.group(3)); + String filename = match.group(4); + if (match.groupCount() == 5) + signature = match.group(5); + int lastDotIndex = filename.lastIndexOf('.'); + if (lastDotIndex == -1) { + publicId = filename; + } else { + publicId = filename.substring(0, lastDotIndex); + format = filename.substring(lastDotIndex + 1); + } + } + } + + public String getComputedSignature(Cloudinary cloudinary) { + Map params = new HashMap(); + params.put("version", getVersion().toString()); + params.put("public_id", getPublicIdForSigning()); + cloudinary.signRequest(params, new HashMap()); + return params.get("signature").toString(); + } + + public boolean getIsImage() { + return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); + } + + public boolean getIsVideo() { + return VIDEO_RESOURCE_TYPE.equals(resourceType); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 93b3fdf2..5c47558d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -13,635 +13,637 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) public class Transformation { - protected Map transformation; - protected List transformations; - protected String htmlWidth; - protected String htmlHeight; - protected boolean hiDPI = false; - protected boolean isResponsive = false; - protected static boolean defaultIsResponsive = false; - protected static Object defaultDPR = null; - - private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); - protected static Map responsiveWidthTransformation = null; - private static final Pattern RANGE_VALUE_RE = Pattern.compile("^((?:\\d+\\.)?\\d+)([%pP])?$"); - private static final Pattern RANGE_RE = Pattern.compile("^(\\d+\\.)?\\d+[%pP]?\\.\\.(\\d+\\.)?\\d+[%pP]?$"); - - public Transformation(Transformation transformation) { - this(dup(transformation.transformations)); - this.hiDPI = transformation.isHiDPI(); - this.isResponsive = transformation.isResponsive(); - } - - // Warning: options will destructively updated! - public Transformation(List transformations) { - this.transformations = transformations; - if (transformations.isEmpty()) { - chain(); - } else { - this.transformation = transformations.get(transformations.size() - 1); - } - } - - public Transformation() { - this.transformations = new ArrayList(); - chain(); - } - - public Transformation width(Object value) { - return param("width", value); - } - - public Transformation height(Object value) { - return param("height", value); - } - - public Transformation named(String... value) { - return param("transformation", value); - } - - public Transformation crop(String value) { - return param("crop", value); - } - - public Transformation background(String value) { - return param("background", value); - } - - public Transformation color(String value) { - return param("color", value); - } - - public Transformation effect(String value) { - return param("effect", value); - } - - public Transformation effect(String effect, Object param) { - return param("effect", effect + ":" + param); - } - - public Transformation angle(int value) { - return param("angle", value); - } - - public Transformation angle(String... value) { - return param("angle", value); - } - - public Transformation border(String value) { - return param("border", value); - } - - public Transformation border(int width, String color) { - return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); - } - - public Transformation x(Object value) { - return param("x", value); - } - - public Transformation y(Object value) { - return param("y", value); - } - - public Transformation radius(Object value) { - return param("radius", value); - } - - public Transformation quality(Object value) { - return param("quality", value); - } - - public Transformation defaultImage(String value) { - return param("default_image", value); - } - - public Transformation gravity(String value) { - return param("gravity", value); - } - - public Transformation colorSpace(String value) { - return param("color_space", value); - } - - public Transformation prefix(String value) { - return param("prefix", value); - } - - public Transformation overlay(String value) { - return param("overlay", value); - } - - public Transformation overlay(AbstractLayer value) { - return param("overlay", value); - } - - public Transformation underlay(String value) { - return param("underlay", value); - } - - public Transformation underlay(AbstractLayer value) { - return param("underlay", value); - } - - public Transformation fetchFormat(String value) { - return param("fetch_format", value); - } - - public Transformation density(Object value) { - return param("density", value); - } - - public Transformation page(Object value) { - return param("page", value); - } - - public Transformation delay(Object value) { - return param("delay", value); - } - - public Transformation opacity(int value) { - return param("opacity", value); - } - - public Transformation rawTransformation(String value) { - return param("raw_transformation", value); - } - - public Transformation flags(String... value) { - return param("flags", value); - } - - public Transformation dpr(float value) { - return param("dpr", value); - } - - public Transformation dpr(int value) { - return param("dpr", value); - } - - public Transformation dpr(String value) { - return param("dpr", value); - } - - public Transformation duration(String value) { - return param("duration", value); - } - - public Transformation duration(float value) { - return param("duration", new Float(value)); - } - - public Transformation duration(double value) { - return param("duration", new Double(value)); - } - - public Transformation durationPercent(float value) { - return param("duration", new Float(value).toString() + "p"); - } - - public Transformation durationPercent(double value) { - return param("duration", new Double(value).toString() + "p"); - } - - public Transformation startOffset(String value) { - return param("start_offset", value); - } - - public Transformation startOffset(float value) { - return param("start_offset", new Float(value)); - } - - public Transformation startOffset(double value) { - return param("start_offset", new Double(value)); - } - - public Transformation startOffsetPercent(float value) { - return param("start_offset", new Float(value).toString() + "p"); - } - - public Transformation startOffsetPercent(double value) { - return param("start_offset", new Double(value).toString() + "p"); - } - - public Transformation endOffset(String value) { - return param("end_offset", value); - } - - public Transformation endOffset(float value) { - return param("end_offset", new Float(value)); - } - - public Transformation endOffset(double value) { - return param("end_offset", new Double(value)); - } - - public Transformation endOffsetPercent(float value) { - return param("end_offset", new Float(value).toString() + "p"); - } - - public Transformation endOffsetPercent(double value) { - return param("end_offset", new Double(value).toString() + "p"); - } - - public Transformation offset(String value) { - return param("offset", value); - } - - public Transformation offset(String[] value) { - if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); - return param("offset", value); - } - - public Transformation offset(float[] value) { - if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); - Number[] numberArray = new Number[]{value[0], value[1]}; - return offset(numberArray); - } - - public Transformation offset(double[] value) { - if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); - Number[] numberArray = new Number[]{value[0], value[1]}; - return offset(numberArray); - } - - public Transformation offset(Number[] value) { - if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); - return param("offset", value); - } - - public Transformation videoCodec(String value) { - return param("video_codec", value); - } - - public Transformation videoCodec(Map value) { - return param("video_codec", value); - } - - public Transformation audioCodec(String value) { - return param("audio_codec", value); - } - - public Transformation audioFrequency(String value) { - return param("audio_frequency", value); - } - - public Transformation audioFrequency(int value) { - return param("audio_frequency", value); - } - - public Transformation bitRate(String value) { - return param("bit_rate", value); - } - - public Transformation bitRate(int value) { - return param("bit_rate", new Integer(value)); - } - - public Transformation videoSampling(String value) { - return param("video_sampling", value); - } - - public Transformation videoSamplingFrames(int value) { - return param("video_sampling", value); - } - - public Transformation videoSamplingSeconds(Number value) { - return param("video_sampling", value.toString() + "s"); - } - - public Transformation videoSamplingSeconds(int value) { - return videoSamplingSeconds(new Integer(value)); - } - - public Transformation videoSamplingSeconds(float value) { - return videoSamplingSeconds(new Float(value)); - } - - public Transformation videoSamplingSeconds(double value) { - return videoSamplingSeconds(new Double(value)); - } - - public Transformation zoom(String value) { - return param("zoom", value); - } - - public Transformation zoom(float value) { - return param("zoom", new Float(value)); - } - - public Transformation zoom(double value) { - return param("zoom", new Double(value)); - } - - public Transformation aspectRatio(double value) { - return param("aspect_ratio", new Double(value)); - } - - public Transformation aspectRatio(String value) { - return param("aspect_ratio", value); - } - - public Transformation aspectRatio(int nom, int denom) { - return aspectRatio(Integer.toString(nom) + ":" + Integer.toString(denom)); - } - - public Transformation responsiveWidth(boolean value) { - return param("responsive_width", value); - } - - public boolean isResponsive() { - return this.isResponsive; - } - - public boolean isHiDPI() { - return this.hiDPI; - } - - // Warning: options will destructively updated! - public Transformation params(Map transformation) { - this.transformation = transformation; - transformations.add(transformation); - return this; - } - - public Transformation chain() { - return params(new HashMap()); - } - - public Transformation chainWith(Transformation transformation) { - List transformations = dup(this.transformations); - transformations.addAll(dup(transformation.transformations)); - return new Transformation(transformations); - } - - public Transformation param(String key, Object value) { - transformation.put(key, value); - return this; - } - - /** - * Serialize this transformation object as a string - * - * {@code - * Transformation().width(100).height(101).generate(); // produces "h_101,w_100" - * } - * @return a String representation of the transformation - */ - public String generate() { - return generate(transformations); - } - - @Override - public String toString() { - return generate(); - } - - public String generate(Iterable optionsList) { - List components = new ArrayList(); - for (Map options : optionsList) { - components.add(generate(options)); - } - return StringUtils.join(components, "/"); - } - - public String generate(Map options) { - boolean isResponsive = ObjectUtils.asBoolean(options.get("responsive_width"), defaultIsResponsive); - - String size = (String) options.get("size"); - if (size != null) { - String[] size_components = size.split("x"); - options.put("width", size_components[0]); - options.put("height", size_components[1]); - } - String width = this.htmlWidth = ObjectUtils.asString(options.get("width")); - String height = this.htmlHeight = ObjectUtils.asString(options.get("height")); - boolean hasLayer = options.get("overlay") != null && StringUtils.isNotBlank(options.get("overlay").toString()) - || options.get("underlay") != null && StringUtils.isNotBlank(options.get("underlay").toString()); - - String crop = (String) options.get("crop"); - String angle = StringUtils.join(ObjectUtils.asArray(options.get("angle")), "."); - - boolean noHtmlSizes = hasLayer || StringUtils.isNotBlank(angle) || "fit".equals(crop) || "limit".equals(crop); - if (width != null && (width.equals("auto") || Float.parseFloat(width) < 1 || noHtmlSizes || isResponsive)) { - this.htmlWidth = null; - } - if (height != null && (Float.parseFloat(height) < 1 || noHtmlSizes || isResponsive)) { - this.htmlHeight = null; - } - - String background = (String) options.get("background"); - if (background != null) { - background = background.replaceFirst("^#", "rgb:"); - } - - String color = (String) options.get("color"); - if (color != null) { - color = color.replaceFirst("^#", "rgb:"); - } - - List transformations = ObjectUtils.asArray(options.get("transformation")); - boolean allNamed = true; - for (Object baseTransformation : transformations) { - if (baseTransformation instanceof Map) { - allNamed = false; - break; - } - } - String namedTransformation = null; - if (allNamed) { - namedTransformation = StringUtils.join(transformations,"."); - transformations = new ArrayList(); - } else { - List ts = transformations; - transformations = new ArrayList(); - for (Object baseTransformation : ts) { - String transformationString; - if (baseTransformation instanceof Map) { - transformationString = generate((Map) baseTransformation); - } else { - Map map = new HashMap(); - map.put("transformation", baseTransformation); - transformationString = generate(map); - } - transformations.add(transformationString); - } - } - - - String flags = StringUtils.join(ObjectUtils.asArray(options.get("flags")), "."); - - String duration = normRangeValue(options.get("duration")); - String startOffset = normRangeValue(options.get("start_offset")); - String endOffset = normRangeValue(options.get("end_offset")); - String[] offset = splitRange(options.get("offset")); - if (offset != null) { - startOffset = normRangeValue(offset[0]); - endOffset = normRangeValue(offset[1]); - } - - String videoCodec = processVideoCodecParam(options.get("video_codec")); - String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); - - SortedMap params = new TreeMap(); - params.put("a", angle); - params.put("b", background); - params.put("c", crop); - params.put("co", color); - params.put("dpr", dpr); - params.put("du", duration); - params.put("eo", endOffset); - params.put("fl", flags); - params.put("h", height); - params.put("so", startOffset); - params.put("t", namedTransformation); - params.put("vc", videoCodec); - params.put("w", width); - - String[] simple_params = new String[] { - "ac", "audio_codec", - "af", "audio_frequency", - "ar", "aspect_ratio", - "bo", "border", - "br", "bit_rate", - "cs", "color_space", - "d", "default_image", - "dl", "delay", - "dn", "density", - "e", "effect", - "f", "fetch_format", - "g", "gravity", - "l", "overlay", - "o", "opacity", - "p", "prefix", - "pg", "page", - "q", "quality", - "r", "radius", - "u", "underlay", - "vs", "video_sampling", - "x", "x", - "y", "y", - "z", "zoom" }; - - for (int i = 0; i < simple_params.length; i += 2) { - params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); - } - List components = new ArrayList(); - for (Map.Entry param : params.entrySet()) { - if (StringUtils.isNotBlank(param.getValue())) { - components.add(param.getKey() + "_" + param.getValue()); - } - } - String raw_transformation = (String) options.get("raw_transformation"); - if (raw_transformation != null) { - components.add(raw_transformation); - } - if (!components.isEmpty()) { - transformations.add(StringUtils.join(components, ",")); - } - - if (isResponsive) { - transformations.add(generate(getResponsiveWidthTransformation())); - } - - if ("auto".equals(width) || isResponsive) { - this.isResponsive = true; - } - - if ("auto".equals(dpr)) { - this.hiDPI = true; - } - - return StringUtils.join(transformations, "/"); - } - - public String getHtmlWidth() { - return htmlWidth; - } - - public String getHtmlHeight() { - return htmlHeight; - } - - private static List dup(List transformations) { - List result = new ArrayList(); - for (Map params : transformations) { - result.add(new HashMap(params)); - } - return result; - } - - public static void setResponsiveWidthTransformation(Map transformation) { - responsiveWidthTransformation = transformation; - } - - private static Map getResponsiveWidthTransformation() { - Map result = new HashMap(); - if (null == responsiveWidthTransformation) { - result.putAll(DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION); - } else { - result.putAll(responsiveWidthTransformation); - } - return result; - } - - public static void setDefaultIsResponsive(boolean isResponsive) { - defaultIsResponsive = isResponsive; - } - - public static void setDefaultDPR(Object dpr) { - defaultDPR = dpr; - } - - private static String[] splitRange(Object range) { - if (range instanceof String[] && ((String[]) range).length >= 2) { - String[] stringArrayRange = ((String[]) range); - return new String[]{stringArrayRange[0], stringArrayRange[1]}; - } else if (range instanceof Number[] && ((Number[]) range).length >= 2) { - Number[] numberArrayRange = ((Number[]) range); - return new String[]{numberArrayRange[0].toString(), numberArrayRange[1].toString()}; - } else if (range instanceof String && RANGE_RE.matcher((String) range).matches()) { - return ((String) range).split("\\.\\.", 2); - } else { - return null; - } - } - - private static String normRangeValue(Object objectValue) { - if (objectValue == null) return null; - String value = objectValue.toString(); - if (StringUtils.isEmpty(value)) return null; - - Matcher matcher = RANGE_VALUE_RE.matcher(value); - - if (!matcher.matches()) { - return null; - } - - String modifier = ""; - if (matcher.groupCount() == 2 && !StringUtils.isEmpty(matcher.group(2))) { - modifier = "p"; - } - return matcher.group(1) + modifier; - } - - private static String processVideoCodecParam(Object param) { - StringBuilder outParam = new StringBuilder(); - if (param instanceof String) { - outParam.append(param); - } if (param instanceof Map) { - Map paramMap = ( Map ) param; - outParam.append(paramMap.get("codec")); - if (paramMap.containsKey("profile")) { - outParam.append(":").append(paramMap.get("profile")); - if (paramMap.containsKey("level")) { - outParam.append(":").append(paramMap.get("level")); - } - } - } - return outParam.toString(); - } + protected Map transformation; + protected List transformations; + protected String htmlWidth; + protected String htmlHeight; + protected boolean hiDPI = false; + protected boolean isResponsive = false; + protected static boolean defaultIsResponsive = false; + protected static Object defaultDPR = null; + + private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); + protected static Map responsiveWidthTransformation = null; + private static final Pattern RANGE_VALUE_RE = Pattern.compile("^((?:\\d+\\.)?\\d+)([%pP])?$"); + private static final Pattern RANGE_RE = Pattern.compile("^(\\d+\\.)?\\d+[%pP]?\\.\\.(\\d+\\.)?\\d+[%pP]?$"); + + public Transformation(Transformation transformation) { + this(dup(transformation.transformations)); + this.hiDPI = transformation.isHiDPI(); + this.isResponsive = transformation.isResponsive(); + } + + // Warning: options will destructively updated! + public Transformation(List transformations) { + this.transformations = transformations; + if (transformations.isEmpty()) { + chain(); + } else { + this.transformation = transformations.get(transformations.size() - 1); + } + } + + public Transformation() { + this.transformations = new ArrayList(); + chain(); + } + + public Transformation width(Object value) { + return param("width", value); + } + + public Transformation height(Object value) { + return param("height", value); + } + + public Transformation named(String... value) { + return param("transformation", value); + } + + public Transformation crop(String value) { + return param("crop", value); + } + + public Transformation background(String value) { + return param("background", value); + } + + public Transformation color(String value) { + return param("color", value); + } + + public Transformation effect(String value) { + return param("effect", value); + } + + public Transformation effect(String effect, Object param) { + return param("effect", effect + ":" + param); + } + + public Transformation angle(int value) { + return param("angle", value); + } + + public Transformation angle(String... value) { + return param("angle", value); + } + + public Transformation border(String value) { + return param("border", value); + } + + public Transformation border(int width, String color) { + return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); + } + + public Transformation x(Object value) { + return param("x", value); + } + + public Transformation y(Object value) { + return param("y", value); + } + + public Transformation radius(Object value) { + return param("radius", value); + } + + public Transformation quality(Object value) { + return param("quality", value); + } + + public Transformation defaultImage(String value) { + return param("default_image", value); + } + + public Transformation gravity(String value) { + return param("gravity", value); + } + + public Transformation colorSpace(String value) { + return param("color_space", value); + } + + public Transformation prefix(String value) { + return param("prefix", value); + } + + public Transformation overlay(String value) { + return param("overlay", value); + } + + public Transformation overlay(AbstractLayer value) { + return param("overlay", value); + } + + public Transformation underlay(String value) { + return param("underlay", value); + } + + public Transformation underlay(AbstractLayer value) { + return param("underlay", value); + } + + public Transformation fetchFormat(String value) { + return param("fetch_format", value); + } + + public Transformation density(Object value) { + return param("density", value); + } + + public Transformation page(Object value) { + return param("page", value); + } + + public Transformation delay(Object value) { + return param("delay", value); + } + + public Transformation opacity(int value) { + return param("opacity", value); + } + + public Transformation rawTransformation(String value) { + return param("raw_transformation", value); + } + + public Transformation flags(String... value) { + return param("flags", value); + } + + public Transformation dpr(float value) { + return param("dpr", value); + } + + public Transformation dpr(int value) { + return param("dpr", value); + } + + public Transformation dpr(String value) { + return param("dpr", value); + } + + public Transformation duration(String value) { + return param("duration", value); + } + + public Transformation duration(float value) { + return param("duration", new Float(value)); + } + + public Transformation duration(double value) { + return param("duration", new Double(value)); + } + + public Transformation durationPercent(float value) { + return param("duration", new Float(value).toString() + "p"); + } + + public Transformation durationPercent(double value) { + return param("duration", new Double(value).toString() + "p"); + } + + public Transformation startOffset(String value) { + return param("start_offset", value); + } + + public Transformation startOffset(float value) { + return param("start_offset", new Float(value)); + } + + public Transformation startOffset(double value) { + return param("start_offset", new Double(value)); + } + + public Transformation startOffsetPercent(float value) { + return param("start_offset", new Float(value).toString() + "p"); + } + + public Transformation startOffsetPercent(double value) { + return param("start_offset", new Double(value).toString() + "p"); + } + + public Transformation endOffset(String value) { + return param("end_offset", value); + } + + public Transformation endOffset(float value) { + return param("end_offset", new Float(value)); + } + + public Transformation endOffset(double value) { + return param("end_offset", new Double(value)); + } + + public Transformation endOffsetPercent(float value) { + return param("end_offset", new Float(value).toString() + "p"); + } + + public Transformation endOffsetPercent(double value) { + return param("end_offset", new Double(value).toString() + "p"); + } + + public Transformation offset(String value) { + return param("offset", value); + } + + public Transformation offset(String[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + return param("offset", value); + } + + public Transformation offset(float[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + Number[] numberArray = new Number[]{value[0], value[1]}; + return offset(numberArray); + } + + public Transformation offset(double[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + Number[] numberArray = new Number[]{value[0], value[1]}; + return offset(numberArray); + } + + public Transformation offset(Number[] value) { + if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); + return param("offset", value); + } + + public Transformation videoCodec(String value) { + return param("video_codec", value); + } + + public Transformation videoCodec(Map value) { + return param("video_codec", value); + } + + public Transformation audioCodec(String value) { + return param("audio_codec", value); + } + + public Transformation audioFrequency(String value) { + return param("audio_frequency", value); + } + + public Transformation audioFrequency(int value) { + return param("audio_frequency", value); + } + + public Transformation bitRate(String value) { + return param("bit_rate", value); + } + + public Transformation bitRate(int value) { + return param("bit_rate", new Integer(value)); + } + + public Transformation videoSampling(String value) { + return param("video_sampling", value); + } + + public Transformation videoSamplingFrames(int value) { + return param("video_sampling", value); + } + + public Transformation videoSamplingSeconds(Number value) { + return param("video_sampling", value.toString() + "s"); + } + + public Transformation videoSamplingSeconds(int value) { + return videoSamplingSeconds(new Integer(value)); + } + + public Transformation videoSamplingSeconds(float value) { + return videoSamplingSeconds(new Float(value)); + } + + public Transformation videoSamplingSeconds(double value) { + return videoSamplingSeconds(new Double(value)); + } + + public Transformation zoom(String value) { + return param("zoom", value); + } + + public Transformation zoom(float value) { + return param("zoom", new Float(value)); + } + + public Transformation zoom(double value) { + return param("zoom", new Double(value)); + } + + public Transformation aspectRatio(double value) { + return param("aspect_ratio", new Double(value)); + } + + public Transformation aspectRatio(String value) { + return param("aspect_ratio", value); + } + + public Transformation aspectRatio(int nom, int denom) { + return aspectRatio(Integer.toString(nom) + ":" + Integer.toString(denom)); + } + + public Transformation responsiveWidth(boolean value) { + return param("responsive_width", value); + } + + public boolean isResponsive() { + return this.isResponsive; + } + + public boolean isHiDPI() { + return this.hiDPI; + } + + // Warning: options will destructively updated! + public Transformation params(Map transformation) { + this.transformation = transformation; + transformations.add(transformation); + return this; + } + + public Transformation chain() { + return params(new HashMap()); + } + + public Transformation chainWith(Transformation transformation) { + List transformations = dup(this.transformations); + transformations.addAll(dup(transformation.transformations)); + return new Transformation(transformations); + } + + public Transformation param(String key, Object value) { + transformation.put(key, value); + return this; + } + + /** + * Serialize this transformation object as a string + *

+ * {@code + * Transformation().width(100).height(101).generate(); // produces "h_101,w_100" + * } + * + * @return a String representation of the transformation + */ + public String generate() { + return generate(transformations); + } + + @Override + public String toString() { + return generate(); + } + + public String generate(Iterable optionsList) { + List components = new ArrayList(); + for (Map options : optionsList) { + components.add(generate(options)); + } + return StringUtils.join(components, "/"); + } + + public String generate(Map options) { + boolean isResponsive = ObjectUtils.asBoolean(options.get("responsive_width"), defaultIsResponsive); + + String size = (String) options.get("size"); + if (size != null) { + String[] size_components = size.split("x"); + options.put("width", size_components[0]); + options.put("height", size_components[1]); + } + String width = this.htmlWidth = ObjectUtils.asString(options.get("width")); + String height = this.htmlHeight = ObjectUtils.asString(options.get("height")); + boolean hasLayer = options.get("overlay") != null && StringUtils.isNotBlank(options.get("overlay").toString()) + || options.get("underlay") != null && StringUtils.isNotBlank(options.get("underlay").toString()); + + String crop = (String) options.get("crop"); + String angle = StringUtils.join(ObjectUtils.asArray(options.get("angle")), "."); + + boolean noHtmlSizes = hasLayer || StringUtils.isNotBlank(angle) || "fit".equals(crop) || "limit".equals(crop); + if (width != null && (width.equals("auto") || Float.parseFloat(width) < 1 || noHtmlSizes || isResponsive)) { + this.htmlWidth = null; + } + if (height != null && (Float.parseFloat(height) < 1 || noHtmlSizes || isResponsive)) { + this.htmlHeight = null; + } + + String background = (String) options.get("background"); + if (background != null) { + background = background.replaceFirst("^#", "rgb:"); + } + + String color = (String) options.get("color"); + if (color != null) { + color = color.replaceFirst("^#", "rgb:"); + } + + List transformations = ObjectUtils.asArray(options.get("transformation")); + boolean allNamed = true; + for (Object baseTransformation : transformations) { + if (baseTransformation instanceof Map) { + allNamed = false; + break; + } + } + String namedTransformation = null; + if (allNamed) { + namedTransformation = StringUtils.join(transformations, "."); + transformations = new ArrayList(); + } else { + List ts = transformations; + transformations = new ArrayList(); + for (Object baseTransformation : ts) { + String transformationString; + if (baseTransformation instanceof Map) { + transformationString = generate((Map) baseTransformation); + } else { + Map map = new HashMap(); + map.put("transformation", baseTransformation); + transformationString = generate(map); + } + transformations.add(transformationString); + } + } + + + String flags = StringUtils.join(ObjectUtils.asArray(options.get("flags")), "."); + + String duration = normRangeValue(options.get("duration")); + String startOffset = normRangeValue(options.get("start_offset")); + String endOffset = normRangeValue(options.get("end_offset")); + String[] offset = splitRange(options.get("offset")); + if (offset != null) { + startOffset = normRangeValue(offset[0]); + endOffset = normRangeValue(offset[1]); + } + + String videoCodec = processVideoCodecParam(options.get("video_codec")); + String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); + + SortedMap params = new TreeMap(); + params.put("a", angle); + params.put("b", background); + params.put("c", crop); + params.put("co", color); + params.put("dpr", dpr); + params.put("du", duration); + params.put("eo", endOffset); + params.put("fl", flags); + params.put("h", height); + params.put("so", startOffset); + params.put("t", namedTransformation); + params.put("vc", videoCodec); + params.put("w", width); + + String[] simple_params = new String[]{ + "ac", "audio_codec", + "af", "audio_frequency", + "ar", "aspect_ratio", + "bo", "border", + "br", "bit_rate", + "cs", "color_space", + "d", "default_image", + "dl", "delay", + "dn", "density", + "e", "effect", + "f", "fetch_format", + "g", "gravity", + "l", "overlay", + "o", "opacity", + "p", "prefix", + "pg", "page", + "q", "quality", + "r", "radius", + "u", "underlay", + "vs", "video_sampling", + "x", "x", + "y", "y", + "z", "zoom"}; + + for (int i = 0; i < simple_params.length; i += 2) { + params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); + } + List components = new ArrayList(); + for (Map.Entry param : params.entrySet()) { + if (StringUtils.isNotBlank(param.getValue())) { + components.add(param.getKey() + "_" + param.getValue()); + } + } + String raw_transformation = (String) options.get("raw_transformation"); + if (raw_transformation != null) { + components.add(raw_transformation); + } + if (!components.isEmpty()) { + transformations.add(StringUtils.join(components, ",")); + } + + if (isResponsive) { + transformations.add(generate(getResponsiveWidthTransformation())); + } + + if ("auto".equals(width) || isResponsive) { + this.isResponsive = true; + } + + if ("auto".equals(dpr)) { + this.hiDPI = true; + } + + return StringUtils.join(transformations, "/"); + } + + public String getHtmlWidth() { + return htmlWidth; + } + + public String getHtmlHeight() { + return htmlHeight; + } + + private static List dup(List transformations) { + List result = new ArrayList(); + for (Map params : transformations) { + result.add(new HashMap(params)); + } + return result; + } + + public static void setResponsiveWidthTransformation(Map transformation) { + responsiveWidthTransformation = transformation; + } + + private static Map getResponsiveWidthTransformation() { + Map result = new HashMap(); + if (null == responsiveWidthTransformation) { + result.putAll(DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION); + } else { + result.putAll(responsiveWidthTransformation); + } + return result; + } + + public static void setDefaultIsResponsive(boolean isResponsive) { + defaultIsResponsive = isResponsive; + } + + public static void setDefaultDPR(Object dpr) { + defaultDPR = dpr; + } + + private static String[] splitRange(Object range) { + if (range instanceof String[] && ((String[]) range).length >= 2) { + String[] stringArrayRange = ((String[]) range); + return new String[]{stringArrayRange[0], stringArrayRange[1]}; + } else if (range instanceof Number[] && ((Number[]) range).length >= 2) { + Number[] numberArrayRange = ((Number[]) range); + return new String[]{numberArrayRange[0].toString(), numberArrayRange[1].toString()}; + } else if (range instanceof String && RANGE_RE.matcher((String) range).matches()) { + return ((String) range).split("\\.\\.", 2); + } else { + return null; + } + } + + private static String normRangeValue(Object objectValue) { + if (objectValue == null) return null; + String value = objectValue.toString(); + if (StringUtils.isEmpty(value)) return null; + + Matcher matcher = RANGE_VALUE_RE.matcher(value); + + if (!matcher.matches()) { + return null; + } + + String modifier = ""; + if (matcher.groupCount() == 2 && !StringUtils.isEmpty(matcher.group(2))) { + modifier = "p"; + } + return matcher.group(1) + modifier; + } + + private static String processVideoCodecParam(Object param) { + StringBuilder outParam = new StringBuilder(); + if (param instanceof String) { + outParam.append(param); + } + if (param instanceof Map) { + Map paramMap = (Map) param; + outParam.append(paramMap.get("codec")); + if (paramMap.containsKey("profile")) { + outParam.append(":").append(paramMap.get("profile")); + if (paramMap.containsKey("level")) { + outParam.append(":").append(paramMap.get("level")); + } + } + } + return outParam.toString(); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 1cb59dc7..4244842b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -16,378 +16,378 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) public class Uploader { - public Map callApi(String action, Map params, Map options, Object file) throws IOException{ - return strategy.callApi(action,params,options,file); - } - - private Cloudinary cloudinary; - private AbstractUploaderStrategy strategy; - - public Uploader(Cloudinary cloudinary,AbstractUploaderStrategy strategy) { - this.cloudinary = cloudinary; - this.strategy = strategy; - strategy.init(this); - } - - public Cloudinary cloudinary(){ - return this.cloudinary; - } - - public Map buildUploadParams(Map options) { - return Util.buildUploadParams(options); - } - - public Map unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - HashMap nextOptions = new HashMap(options); - nextOptions.put("unsigned", true); - nextOptions.put("upload_preset", uploadPreset); - return upload(file, nextOptions); - } - - public Map upload(Object file, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = buildUploadParams(options); - return callApi("upload", params, options, file); - } - - public Map uploadLargeRaw(Object file, Map options) throws IOException { - return uploadLargeRaw(file, options, 20000000); - } - - public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { - Map sentOptions = new HashMap(); - sentOptions.putAll(options); - sentOptions.put("resource_type", "raw"); - return uploadLarge(file, sentOptions, bufferSize); - } - - public Map uploadLarge(Object file, Map options) throws IOException { - int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), 20000000); - return uploadLarge(file, options, bufferSize); - } - - @SuppressWarnings("resource") - public Map uploadLarge(Object file, Map options, int bufferSize) throws IOException { - InputStream input; - long length = -1; - if (file instanceof InputStream) { - input = (InputStream) file; - } else if (file instanceof File) { - length = ((File) file).length(); - input = new FileInputStream((File) file); - } else if (file instanceof byte[]) { - length = ( (byte[]) file ).length; - input = new ByteArrayInputStream((byte[]) file); - } else { - File f = new File(file.toString()); - length = f.length(); - input = new FileInputStream(f); - } - try { - Map result = uploadLargeParts(input, options, bufferSize, length); - return result; - } finally { - input.close(); - } - } - - private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length) throws IOException { - Map params = buildUploadParams(options); - - Map sentOptions = new HashMap(); - sentOptions.putAll(options); - Map extraHeaders = new HashMap(); - extraHeaders.put("X-Unique-Upload-Id", cloudinary().randomPublicId()); - sentOptions.put("extra_headers", extraHeaders); - - byte[] buffer = new byte[bufferSize]; - byte[] nibbleBuffer = new byte[1]; - int bytesRead = 0; - int currentBufferSize = 0; - int partNumber = 0; - long totalBytes = 0; - Map response = null; - while (true) { - bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize); - boolean atEnd = bytesRead == -1; - boolean fullBuffer = !atEnd && (bytesRead + currentBufferSize) == bufferSize; - if (!atEnd) currentBufferSize += bytesRead; - - if (atEnd || fullBuffer) { - totalBytes += currentBufferSize; - int currentLoc = bufferSize * partNumber; - if (!atEnd) { - //verify not on end - try read another byte - bytesRead = input.read(nibbleBuffer, 0, 1); - atEnd = bytesRead == -1; - } - if (atEnd) { - if (length == -1) length = totalBytes; - byte[] finalBuffer = new byte[currentBufferSize]; - System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); - buffer = finalBuffer; - } - String range = String.format("bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); - extraHeaders.put("Content-Range", range); - Map sentParams = new HashMap(); - sentParams.putAll(params); - response = callApi("upload", sentParams, sentOptions, buffer); - if (atEnd) break; - buffer[0] = nibbleBuffer[0]; - currentBufferSize = 1; - partNumber++; - } - } - return response; - } - - public Map destroy(String publicId, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("public_id", publicId); - params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); - return callApi("destroy", params, options, null); - } - - public Map rename(String fromPublicId, String toPublicId, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("type", (String) options.get("type")); - params.put("overwrite", ObjectUtils.asBoolean(options.get("overwrite"), false).toString()); - params.put("from_public_id", fromPublicId); - params.put("to_public_id", toPublicId); - params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); - return callApi("rename", params, options, null); - } - - public Map explicit(String publicId, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("callback", (String) options.get("callback")); - params.put("type", (String) options.get("type")); - params.put("eager", Util.buildEager((List) options.get("eager"))); - params.put("eager_async", ObjectUtils.asBoolean(options.get("eager_async"), false).toString()); - params.put("eager_notification_url", (String) options.get("eager_notification_url")); - params.put("headers", Util.buildCustomHeaders(options.get("headers"))); - params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); - if (options.get("face_coordinates") != null) { - params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); - } - if (options.get("custom_coordinates") != null) { - params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); - } - if (options.get("context") != null) { - params.put("context", ObjectUtils.encodeMap(options.get("context"))); - } - if (options.get("responsive_breakpoints") != null) { - params.put("responsive_breakpoints", JSONObject.wrap(options.get("responsive_breakpoints"))); - } - params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); - 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 = ObjectUtils.emptyMap(); - Map params = new HashMap(); - Object transParam = options.get("transformation"); - Transformation transformation = null; - if (transParam instanceof Transformation) { - transformation = new Transformation((Transformation) transParam); - } else if (transParam instanceof String) { - transformation = new Transformation().rawTransformation((String) transParam); - } else { - transformation = new Transformation(); - } - String format = (String) options.get("format"); - if (format != null) { - transformation.fetchFormat(format); - } - params.put("transformation", transformation.generate()); - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); - return callApi("sprite", params, options, null); - } - - public Map multi(String tag, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); - return callApi("multi", params, options, null); - } - - public Map explode(String public_id, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - params.put("public_id", public_id); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - return callApi("explode", params, options, null); - } - - // options may include 'exclusive' (boolean) which causes clearing this tag - // from all other resources - public Map addTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - boolean exclusive = ObjectUtils.asBoolean(options.get("exclusive"), false); - String command = exclusive ? "set_exclusive" : "add"; - return callTagsApi(tag, command, publicIds, options); - } - - public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - return callTagsApi(tag, "remove", publicIds, options); - } - - public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - return callTagsApi(tag, "replace", publicIds, options); - } - - public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("tag", tag); - params.put("command", command); - params.put("type", (String) options.get("type")); - params.put("public_ids", Arrays.asList(publicIds)); - return callApi("tags", params, options, null); - } - - private final static String[] TEXT_PARAMS = { "public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", - "background", "opacity", "text_decoration" }; - - public Map text(String text, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("text", text); - for (String param : TEXT_PARAMS) { - params.put(param, ObjectUtils.asString(options.get(param))); - } - return callApi("text", params, options, null); - } - - public Map createArchive(Map options, String targetFormat) throws IOException { - Map params = Util.buildArchiveParams(options, targetFormat); - return callApi("generate_archive", params, options, null); - } - - public Map createZip(Map options) throws IOException { - return createArchive(options, "zip"); - } - - public Map createArchive(ArchiveParams params) throws IOException { - return createArchive(params.toMap(), params.targetFormat()); - } - - public void signRequestParams(Map params, Map options) { - if (!params.containsKey("timestamp")) - params.put("timestamp", Util.timestamp()); - cloudinary.signRequest(params, options); - } - - public String uploadTagParams(Map options) { - if (options == null) - options = new HashMap(); - if (options.get("resource_type") == null) { - options = new HashMap(options); - options.put("resource_type", "auto"); - } - - String callback = ObjectUtils.asString(options.get("callback"), this.cloudinary.config.callback); - if (callback == null) { - throw new IllegalArgumentException("Must supply callback"); - } - options.put("callback", callback); - - Map params = this.buildUploadParams(options); - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - return JSONObject.valueToString(params); - } - - public String getUploadUrl(Map options) { - if (options == null) - options = new HashMap(); - return this.cloudinary.cloudinaryApiUrl("upload", options); - } - - public String unsignedImageUploadTag(String field, String uploadPreset, Map options, Map htmlOptions) { - Map nextOptions = new HashMap(options); - nextOptions.put("upload_preset", uploadPreset); - nextOptions.put("unsigned", true); - return imageUploadTag(field, nextOptions, htmlOptions); - } - - public String imageUploadTag(String field, Map options, Map htmlOptions) { - if (htmlOptions == null) - htmlOptions = ObjectUtils.emptyMap(); - - String tagParams = StringUtils.escapeHtml(uploadTagParams(options)); - - String cloudinaryUploadUrl = getUploadUrl(options); - - StringBuilder builder = new StringBuilder(); - builder.append(""); - return builder.toString(); - } + public Map callApi(String action, Map params, Map options, Object file) throws IOException { + return strategy.callApi(action, params, options, file); + } + + private Cloudinary cloudinary; + private AbstractUploaderStrategy strategy; + + public Uploader(Cloudinary cloudinary, AbstractUploaderStrategy strategy) { + this.cloudinary = cloudinary; + this.strategy = strategy; + strategy.init(this); + } + + public Cloudinary cloudinary() { + return this.cloudinary; + } + + public Map buildUploadParams(Map options) { + return Util.buildUploadParams(options); + } + + public Map unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + HashMap nextOptions = new HashMap(options); + nextOptions.put("unsigned", true); + nextOptions.put("upload_preset", uploadPreset); + return upload(file, nextOptions); + } + + public Map upload(Object file, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = buildUploadParams(options); + return callApi("upload", params, options, file); + } + + public Map uploadLargeRaw(Object file, Map options) throws IOException { + return uploadLargeRaw(file, options, 20000000); + } + + public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { + Map sentOptions = new HashMap(); + sentOptions.putAll(options); + sentOptions.put("resource_type", "raw"); + return uploadLarge(file, sentOptions, bufferSize); + } + + public Map uploadLarge(Object file, Map options) throws IOException { + int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), 20000000); + return uploadLarge(file, options, bufferSize); + } + + @SuppressWarnings("resource") + public Map uploadLarge(Object file, Map options, int bufferSize) throws IOException { + InputStream input; + long length = -1; + if (file instanceof InputStream) { + input = (InputStream) file; + } else if (file instanceof File) { + length = ((File) file).length(); + input = new FileInputStream((File) file); + } else if (file instanceof byte[]) { + length = ((byte[]) file).length; + input = new ByteArrayInputStream((byte[]) file); + } else { + File f = new File(file.toString()); + length = f.length(); + input = new FileInputStream(f); + } + try { + Map result = uploadLargeParts(input, options, bufferSize, length); + return result; + } finally { + input.close(); + } + } + + private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length) throws IOException { + Map params = buildUploadParams(options); + + Map sentOptions = new HashMap(); + sentOptions.putAll(options); + Map extraHeaders = new HashMap(); + extraHeaders.put("X-Unique-Upload-Id", cloudinary().randomPublicId()); + sentOptions.put("extra_headers", extraHeaders); + + byte[] buffer = new byte[bufferSize]; + byte[] nibbleBuffer = new byte[1]; + int bytesRead = 0; + int currentBufferSize = 0; + int partNumber = 0; + long totalBytes = 0; + Map response = null; + while (true) { + bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize); + boolean atEnd = bytesRead == -1; + boolean fullBuffer = !atEnd && (bytesRead + currentBufferSize) == bufferSize; + if (!atEnd) currentBufferSize += bytesRead; + + if (atEnd || fullBuffer) { + totalBytes += currentBufferSize; + int currentLoc = bufferSize * partNumber; + if (!atEnd) { + //verify not on end - try read another byte + bytesRead = input.read(nibbleBuffer, 0, 1); + atEnd = bytesRead == -1; + } + if (atEnd) { + if (length == -1) length = totalBytes; + byte[] finalBuffer = new byte[currentBufferSize]; + System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); + buffer = finalBuffer; + } + String range = String.format("bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); + extraHeaders.put("Content-Range", range); + Map sentParams = new HashMap(); + sentParams.putAll(params); + response = callApi("upload", sentParams, sentOptions, buffer); + if (atEnd) break; + buffer[0] = nibbleBuffer[0]; + currentBufferSize = 1; + partNumber++; + } + } + return response; + } + + public Map destroy(String publicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("public_id", publicId); + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + return callApi("destroy", params, options, null); + } + + public Map rename(String fromPublicId, String toPublicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", (String) options.get("type")); + params.put("overwrite", ObjectUtils.asBoolean(options.get("overwrite"), false).toString()); + params.put("from_public_id", fromPublicId); + params.put("to_public_id", toPublicId); + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + return callApi("rename", params, options, null); + } + + public Map explicit(String publicId, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("public_id", publicId); + params.put("callback", (String) options.get("callback")); + params.put("type", (String) options.get("type")); + params.put("eager", Util.buildEager((List) options.get("eager"))); + params.put("eager_async", ObjectUtils.asBoolean(options.get("eager_async"), false).toString()); + params.put("eager_notification_url", (String) options.get("eager_notification_url")); + params.put("headers", Util.buildCustomHeaders(options.get("headers"))); + params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); + if (options.get("face_coordinates") != null) { + params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); + } + if (options.get("custom_coordinates") != null) { + params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); + } + if (options.get("context") != null) { + params.put("context", ObjectUtils.encodeMap(options.get("context"))); + } + if (options.get("responsive_breakpoints") != null) { + params.put("responsive_breakpoints", JSONObject.wrap(options.get("responsive_breakpoints"))); + } + params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + 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 = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transParam = options.get("transformation"); + Transformation transformation = null; + if (transParam instanceof Transformation) { + transformation = new Transformation((Transformation) transParam); + } else if (transParam instanceof String) { + transformation = new Transformation().rawTransformation((String) transParam); + } else { + transformation = new Transformation(); + } + String format = (String) options.get("format"); + if (format != null) { + transformation.fetchFormat(format); + } + params.put("transformation", transformation.generate()); + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + return callApi("sprite", params, options, null); + } + + public Map multi(String tag, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("tag", tag); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + return callApi("multi", params, options, null); + } + + public Map explode(String public_id, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("public_id", public_id); + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + return callApi("explode", params, options, null); + } + + // options may include 'exclusive' (boolean) which causes clearing this tag + // from all other resources + public Map addTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + boolean exclusive = ObjectUtils.asBoolean(options.get("exclusive"), false); + String command = exclusive ? "set_exclusive" : "add"; + return callTagsApi(tag, command, publicIds, options); + } + + public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + return callTagsApi(tag, "remove", publicIds, options); + } + + public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + return callTagsApi(tag, "replace", publicIds, options); + } + + public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("tag", tag); + params.put("command", command); + params.put("type", (String) options.get("type")); + params.put("public_ids", Arrays.asList(publicIds)); + return callApi("tags", params, options, null); + } + + private final static String[] TEXT_PARAMS = {"public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", + "background", "opacity", "text_decoration"}; + + public Map text(String text, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("text", text); + for (String param : TEXT_PARAMS) { + params.put(param, ObjectUtils.asString(options.get(param))); + } + return callApi("text", params, options, null); + } + + public Map createArchive(Map options, String targetFormat) throws IOException { + Map params = Util.buildArchiveParams(options, targetFormat); + return callApi("generate_archive", params, options, null); + } + + public Map createZip(Map options) throws IOException { + return createArchive(options, "zip"); + } + + public Map createArchive(ArchiveParams params) throws IOException { + return createArchive(params.toMap(), params.targetFormat()); + } + + public void signRequestParams(Map params, Map options) { + if (!params.containsKey("timestamp")) + params.put("timestamp", Util.timestamp()); + cloudinary.signRequest(params, options); + } + + public String uploadTagParams(Map options) { + if (options == null) + options = new HashMap(); + if (options.get("resource_type") == null) { + options = new HashMap(options); + options.put("resource_type", "auto"); + } + + String callback = ObjectUtils.asString(options.get("callback"), this.cloudinary.config.callback); + if (callback == null) { + throw new IllegalArgumentException("Must supply callback"); + } + options.put("callback", callback); + + Map params = this.buildUploadParams(options); + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + return JSONObject.valueToString(params); + } + + public String getUploadUrl(Map options) { + if (options == null) + options = new HashMap(); + return this.cloudinary.cloudinaryApiUrl("upload", options); + } + + public String unsignedImageUploadTag(String field, String uploadPreset, Map options, Map htmlOptions) { + Map nextOptions = new HashMap(options); + nextOptions.put("upload_preset", uploadPreset); + nextOptions.put("unsigned", true); + return imageUploadTag(field, nextOptions, htmlOptions); + } + + public String imageUploadTag(String field, Map options, Map htmlOptions) { + if (htmlOptions == null) + htmlOptions = ObjectUtils.emptyMap(); + + String tagParams = StringUtils.escapeHtml(uploadTagParams(options)); + + String cloudinaryUploadUrl = getUploadUrl(options); + + StringBuilder builder = new StringBuilder(); + builder.append(""); + return builder.toString(); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 83bce2ad..c26d128f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -19,651 +19,649 @@ import com.cloudinary.utils.StringUtils; public class Url { - private final Cloudinary cloudinary; - private final Configuration config; - String publicId = null; - String type = null; - String resourceType = null; - String format = null; - String version = null; - Transformation transformation = null; - boolean signUrl; - String source = null; - private String urlSuffix; - private Boolean useRootPath; - Map sourceTransformation = null; - String[] sourceTypes = null; - String fallbackContent = null; - Transformation posterTransformation = null; - String posterSource = null; - Url posterUrl = null; - - private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"}; - private static final Pattern VIDEO_EXTENSION_RE = Pattern.compile("\\.(" + StringUtils.join(DEFAULT_VIDEO_SOURCE_TYPES, "|") + ")$"); - - public Url(Cloudinary cloudinary) { - this.cloudinary = cloudinary; - this.config = new Configuration(cloudinary.config); - } - - public Url clone() { - Url cloned = cloudinary.url(); - - cloned.fallbackContent = this.fallbackContent; - cloned.format = this.format; - cloned.posterSource = this.posterSource; - if (this.posterTransformation != null) cloned.posterTransformation = new Transformation(this.posterTransformation); - if (this.posterUrl != null) cloned.posterUrl = this.posterUrl.clone(); - cloned.publicId = this.publicId; - cloned.resourceType = this.resourceType; - cloned.signUrl = this.signUrl; - cloned.source = this.source; - if (this.transformation != null) cloned.transformation = new Transformation(this.transformation); - if (this.sourceTransformation != null) { - cloned.sourceTransformation = new HashMap(); - for (Map.Entry keyValuePair : this.sourceTransformation.entrySet()) { - cloned.sourceTransformation.put(keyValuePair.getKey(), keyValuePair.getValue()); - }; - } - cloned.sourceTypes = this.sourceTypes; - cloned.urlSuffix = this.urlSuffix; - cloned.useRootPath = this.useRootPath; - - return cloned; - } - - private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); - - /** - * Parses a cloudinary identifier of the form:
- * {@code [/][/][v/][.][#]} - */ - public Url fromIdentifier(String identifier) { - Matcher matcher = identifierPattern.matcher(identifier); - if (!matcher.matches()) { - throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); - } - - String resourceType = matcher.group(1); - if (resourceType != null) { - resourceType(resourceType); - } - - String type = matcher.group(2); - if (type != null) { - type(type); - } - - String version = matcher.group(3); - if (version != null) { - version(version); - } - - String publicId = matcher.group(4); - if (publicId != null) { - publicId(publicId); - } - - String format = matcher.group(5); - if (format != null) { - format(format); - } - - // Signature (group 6) is not used - - return this; - } - - public Url type(String type) { - this.type = type; - return this; - } - - public Url resourcType(String resourceType) { - return resourceType(resourceType); - } - - public Url resourceType(String resourceType) { - this.resourceType = resourceType; - return this; - } - - public Url publicId(Object publicId) { - this.publicId = ObjectUtils.asString(publicId); - return this; - } - - public Url format(String format) { - this.format = format; - return this; - } - - public Url cloudName(String cloudName) { - this.config.cloudName = cloudName; - return this; - } - - public Url secureDistribution(String secureDistribution) { - this.config.secureDistribution = secureDistribution; - return this; - } - - public Url secureCdnSubdomain(boolean secureCdnSubdomain) { - this.config.secureCdnSubdomain = secureCdnSubdomain; - return this; - } - - public Url suffix(String urlSuffix) { - this.urlSuffix = urlSuffix; - return this; - } - - public Url useRootPath(boolean useRootPath) { - this.useRootPath = useRootPath; - return this; - } - - public Url cname(String cname) { - this.config.cname = cname; - return this; - } - - public Url version(Object version) { - this.version = ObjectUtils.asString(version); - return this; - } - - public Url transformation(Transformation transformation) { - this.transformation = transformation; - return this; - } - - public Url secure(boolean secure) { - this.config.secure = secure; - return this; - } - - public Url privateCdn(boolean privateCdn) { - this.config.privateCdn = privateCdn; - return this; - } - - public Url cdnSubdomain(boolean cdnSubdomain) { - this.config.cdnSubdomain = cdnSubdomain; - return this; - } - - public Url shorten(boolean shorten) { - this.config.shorten = shorten; - return this; - } - - public Transformation transformation() { - if (this.transformation == null) - this.transformation = new Transformation(); - return this.transformation; - } - - public Url signed(boolean signUrl) { - this.signUrl = signUrl; - return this; - } - - public Url sourceTransformation(Map sourceTransformation) { - this.sourceTransformation = sourceTransformation; - return this; - } - - public Url sourceTransformationFor(String source, Transformation transformation) { - if (this.sourceTransformation == null) { - this.sourceTransformation = new HashMap(); - } - this.sourceTransformation.put(source, transformation); - return this; - } - - public Url sourceTypes(String[] sourceTypes) { - this.sourceTypes = sourceTypes; - return this; - } - - public Url fallbackContent(String fallbackContent) { - this.fallbackContent = fallbackContent; - return this; - } - - public Url posterTransformation(Transformation posterTransformation) { - this.posterTransformation = posterTransformation; - return this; - } - - @SuppressWarnings("rawtypes") - public Url posterTransformation(List posterTransformations) { - this.posterTransformation = new Transformation(posterTransformations); - return this; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Url posterTransformation(Map posterTransformations) { - List transformations = new ArrayList(); - Map copy = new HashMap(); - copy.putAll(posterTransformations); - transformations.add(copy); - this.posterTransformation = new Transformation(transformations); - return this; - } - - public Url posterSource(String posterSource) { - this.posterSource = posterSource; - return this; - } - - public Url posterUrl(Url posterUrl) { - this.posterUrl = posterUrl; - return this; - } - - public Url poster(Object poster) { - if (poster instanceof Transformation) { - return posterTransformation((Transformation) poster); - } else if (poster instanceof List) { - return posterTransformation((List) poster); - } else if (poster instanceof Map) { - return posterTransformation((Map) poster); - } else if (poster instanceof Url) { - return posterUrl((Url) poster); - } else if (poster instanceof String) { - return posterSource((String) poster); - } else if (poster == null || poster.equals(Boolean.FALSE)){ - return posterSource(""); - } else { - throw new IllegalArgumentException("Illegal value type supplied to poster. must be one of: , >, , , "); - } - } - - public String generate() { - return generate(null); - } - - public String generate(String source) { - - boolean useRootPath =this.config.useRootPath; - if (this.useRootPath!=null){ - useRootPath = this.useRootPath; - } - - if (StringUtils.isEmpty(this.config.cloudName)) { - throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); - } - - if (!this.config.privateCdn) { - if (StringUtils.isNotBlank(urlSuffix)) { - throw new IllegalArgumentException("URL Suffix only supported in private CDN"); - } - if (useRootPath) { - throw new IllegalArgumentException("Root path only supported in private CDN"); - } - } - - if (source == null) { - if (publicId == null) { - if (this.source == null) { - return null; - } - source = this.source; - } else { - source = publicId; - } - } - - if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { - if (StringUtils.isEmpty(type) || "asset".equals(type) ) { - return source; - } - } - - - if (type!=null && type.equals("fetch") && !StringUtils.isEmpty(format)) { - transformation().fetchFormat(format); - this.format = null; - } - String transformationStr = transformation().generate(); - String signature = ""; - - - String[] finalizedSource = finalizeSource(source,format,urlSuffix); - source = finalizedSource[0]; - String sourceToSign = finalizedSource[1]; - - if (sourceToSign.contains("/") && !sourceToSign.matches("v[0-9]+.*") && !sourceToSign.matches("https?:/.*") && StringUtils.isEmpty(version)) { - version = "1"; - } - - if (version == null) - version = ""; - else - version = "v" + version; - - - if (signUrl) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - - String toSign = StringUtils.join(new String[] { transformationStr, sourceToSign }, "/"); - toSign = toSign.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); - - - - byte[] digest = md.digest(cloudinary.getUTF8Bytes(toSign + this.config.apiSecret)); - signature = Base64Coder.encodeURLSafeString(digest); - signature = "s--" + signature.substring(0, 8) + "--" ; - } - - String resourceType = this.resourceType; - if (resourceType == null) resourceType = "image"; - String finalResourceType = finalizeResourceType(resourceType,type,urlSuffix,useRootPath,config.shorten); - String prefix = unsignedDownloadUrlPrefix(source,config.cloudName,config.privateCdn,config.cdnSubdomain,config.secureCdnSubdomain,config.cname,config.secure,config.secureDistribution); - - return StringUtils.join(new String[] { prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); - } - - private String[] finalizeSource(String source, String format, String urlSuffix) { - String[] result = new String[2]; - source = source.replaceAll("([^:])//", "\1/"); - - String sourceToSign; - if (source.toLowerCase().matches("^https?:/.*")) { - source = SmartUrlEncoder.encode(source); - sourceToSign = source; - } else { - try { - source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - sourceToSign = source; - if (StringUtils.isNotBlank(urlSuffix)) { - Pattern pattern = Pattern.compile("[\\./]"); - Matcher matcher= pattern.matcher(urlSuffix); - if (matcher.find()) { - throw new IllegalArgumentException("url_suffix should not include . or /"); - } - source = source + "/" + urlSuffix; - } - if (StringUtils.isNotBlank(format)) { - source = source + "." + format; - sourceToSign = sourceToSign + "." + format; - } - } - result[0] = source; - result[1] = sourceToSign; - return result; - } - - public String finalizeResourceType(String resourceType, String type, String urlSuffix, boolean useRootPath, boolean shorten) { - if (type == null) { - type = "upload"; - } - if (!StringUtils.isBlank(urlSuffix)) { - if (resourceType.equals("image") && type.equals("upload")) { - resourceType = "images"; - type = null; - } else if (resourceType.equals("raw") && type.equals("upload")) { - resourceType = "files"; - type = null; - } else { - throw new IllegalArgumentException("URL Suffix only supported for image/upload and raw/upload"); - } - } - if (useRootPath) { - if ((resourceType.equals("image") && type.equals("upload")) || (resourceType.equals("images") && StringUtils.isBlank(type))) { - resourceType = null; - type = null; - } else { - throw new IllegalArgumentException("Root path only supported for image/upload"); - } - } - if (shorten && resourceType.equals("image") && type.equals("upload")) { - resourceType = "iu"; - type = null; - } - String result = resourceType; - if (type!=null){ - result+="/"+type; - } - return result; - } - - public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean privateCdn, boolean cdnSubdomain, Boolean secureCdnSubdomain, String cname, boolean secure, String secureDistribution) { - if (this.config.cloudName.startsWith("/")) { - return "/res" + this.config.cloudName; - } - boolean sharedDomain = !this.config.privateCdn; - - String prefix; - - if (this.config.secure) { - if (StringUtils.isEmpty(this.config.secureDistribution) || this.config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { - secureDistribution = this.config.privateCdn ? this.config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; - } - if (!sharedDomain) { - sharedDomain = secureDistribution.equals(Cloudinary.SHARED_CDN); - } - - if (secureCdnSubdomain == null && sharedDomain) { - secureCdnSubdomain = this.config.cdnSubdomain; - } - - if (secureCdnSubdomain!=null && secureCdnSubdomain==true) { - secureDistribution = this.config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); - } - - prefix = "https://" + secureDistribution; - } else if (StringUtils.isNotBlank(this.config.cname)) { - String subdomain = this.config.cdnSubdomain ? "a" + shard(source) + "." : ""; - prefix = "http://" + subdomain + this.config.cname; - } else { - String protocol = "http://"; - cloudName = this.config.privateCdn ? this.config.cloudName + "-" : ""; - String res = "res"; - String subdomain = this.config.cdnSubdomain ? "-" + shard(source) : ""; - String domain = ".cloudinary.com"; - prefix = StringUtils.join(new String[] { protocol, cloudName, res, subdomain, domain }, ""); - } - if (sharedDomain) { - prefix += "/" + this.config.cloudName; - } - return prefix; - } - - private String shard(String input) { - CRC32 crc32 = new CRC32(); - crc32.update(cloudinary.getUTF8Bytes(input)); - return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); - } - - @SuppressWarnings("unchecked") - public String imageTag(String source) { - return imageTag(source, ObjectUtils.emptyMap()); - } - - public String imageTag(Map attributes) { - return imageTag(null, attributes); - } - - public String imageTag(String source, Map attributes) { - String url = generate(source); - attributes = new TreeMap(attributes); // Make sure they - // are ordered. - if (transformation().getHtmlHeight() != null) - attributes.put("height", transformation().getHtmlHeight()); - if (transformation().getHtmlWidth() != null) - attributes.put("width", transformation().getHtmlWidth()); - - boolean hiDPI = transformation().isHiDPI(); - boolean responsive = transformation().isResponsive(); - - if (hiDPI || responsive) { - attributes.put("data-src", url); - String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; - attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); - String responsivePlaceholder = attributes.remove("responsive_placeholder"); - if ("blank".equals(responsivePlaceholder)) { - responsivePlaceholder = CL_BLANK; - } - url = responsivePlaceholder; - } - - StringBuilder builder = new StringBuilder(); - builder.append(" attr : attributes.entrySet()) { - builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); - } - builder.append("/>"); - return builder.toString(); - } - - public String videoTag() { - return videoTag("", new HashMap()); - } - - public String videoTag(Map attributes) { - return videoTag("", attributes); - } - - private String finalizePosterUrl(String source) { - String posterUrl = null; - if (this.posterUrl != null) { - posterUrl = this.posterUrl.generate(); - } else if (this.posterTransformation != null) { - posterUrl = this.clone().format("jpg").transformation(new Transformation(this.posterTransformation)) - .generate(source); - } else if (this.posterSource != null) { - if (!StringUtils.isEmpty(this.posterSource)) - posterUrl = this.clone().format("jpg").generate(this.posterSource); - } else { - posterUrl = this.clone().format("jpg").generate(source); - } - return posterUrl; - } - - private void appendVideoSources(StringBuilder html, String source, String sourceType ) { - Url sourceUrl = this.clone(); - if (this.sourceTransformation != null) { - Transformation transformation = this.transformation; - Transformation sourceTransformation = null; - if (this.sourceTransformation.get(sourceType) != null) - sourceTransformation = new Transformation(this.sourceTransformation.get(sourceType)); - if (transformation == null) { - transformation = sourceTransformation; - } else if (sourceTransformation != null) { - transformation = transformation.chainWith(sourceTransformation); - } - sourceUrl.transformation(transformation); - } - String src = sourceUrl.format(sourceType).generate(source); - String videoType = sourceType; - if (sourceType.equals("ogv")) - videoType = "ogg"; - String mimeType = "video/" + videoType; - html.append(""); - } - - public String videoTag(String source, Map attributes) { - if (StringUtils.isEmpty(source)) - source = this.source; - if (StringUtils.isEmpty(source)) - source = publicId; - if (StringUtils.isEmpty(source)) - throw new IllegalArgumentException("must supply source or public id"); - source = VIDEO_EXTENSION_RE.matcher(source).replaceFirst(""); - - if (this.resourceType == null) this.resourceType = "video"; - attributes = new TreeMap(attributes); // Make sure they are ordered. - - String[] sourceTypes = this.sourceTypes; - - if (sourceTypes == null) { - sourceTypes = DEFAULT_VIDEO_SOURCE_TYPES; - } - - String posterUrl = this.finalizePosterUrl(source); - - if (!StringUtils.isEmpty(posterUrl)) - attributes.put("poster", posterUrl); - - StringBuilder html = new StringBuilder().append(" 1; - if (!multiSource) { - url = generate(source + "." + sourceTypes[0]); - attributes.put("src", url); - } else { - generate(source); - } - - if (this.transformation.getHtmlHeight() != null) - attributes.put("height", this.transformation.getHtmlHeight()); - if (attributes.containsKey("html_height")) - attributes.put("height", attributes.remove("html_height")); - if (this.transformation.getHtmlWidth() != null) - attributes.put("width", this.transformation.getHtmlWidth()); - if (attributes.containsKey("html_width")) - attributes.put("width", attributes.remove("html_width")); - - for (Map.Entry attr : attributes.entrySet()) { - html.append(" ").append(attr.getKey()); - if (attr.getValue() != null) { - String value = ObjectUtils.asString(attr.getValue()); - html.append("='").append(value).append("'"); - } - } - - html.append(">"); - - if (multiSource) { - for (String sourceType : sourceTypes) { - this.appendVideoSources(html, source, sourceType); - } - } - - if (this.fallbackContent != null) - html.append(this.fallbackContent); - html.append(""); - return html.toString(); - } - - public String generateSpriteCss(String source) { - this.type = "sprite"; - if (!source.endsWith(".css")) - this.format = "css"; - return generate(source); - } - - public Url source(String source) { - this.source = source; - return this; - } - - public Url source(StoredFile source) { - if (source.getResourceType() != null) - this.resourceType = source.getResourceType(); - if (source.getType() != null) - this.type = source.getType(); - if (source.getVersion() != null) - this.version = source.getVersion().toString(); - this.format = source.getFormat(); - this.source = source.getPublicId(); - return this; - } + private final Cloudinary cloudinary; + private final Configuration config; + String publicId = null; + String type = null; + String resourceType = null; + String format = null; + String version = null; + Transformation transformation = null; + boolean signUrl; + String source = null; + private String urlSuffix; + private Boolean useRootPath; + Map sourceTransformation = null; + String[] sourceTypes = null; + String fallbackContent = null; + Transformation posterTransformation = null; + String posterSource = null; + Url posterUrl = null; + + private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; + public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"}; + private static final Pattern VIDEO_EXTENSION_RE = Pattern.compile("\\.(" + StringUtils.join(DEFAULT_VIDEO_SOURCE_TYPES, "|") + ")$"); + + public Url(Cloudinary cloudinary) { + this.cloudinary = cloudinary; + this.config = new Configuration(cloudinary.config); + } + + public Url clone() { + Url cloned = cloudinary.url(); + + cloned.fallbackContent = this.fallbackContent; + cloned.format = this.format; + cloned.posterSource = this.posterSource; + if (this.posterTransformation != null) cloned.posterTransformation = new Transformation(this.posterTransformation); + if (this.posterUrl != null) cloned.posterUrl = this.posterUrl.clone(); + cloned.publicId = this.publicId; + cloned.resourceType = this.resourceType; + cloned.signUrl = this.signUrl; + cloned.source = this.source; + if (this.transformation != null) cloned.transformation = new Transformation(this.transformation); + if (this.sourceTransformation != null) { + cloned.sourceTransformation = new HashMap(); + for (Map.Entry keyValuePair : this.sourceTransformation.entrySet()) { + cloned.sourceTransformation.put(keyValuePair.getKey(), keyValuePair.getValue()); + } + } + cloned.sourceTypes = this.sourceTypes; + cloned.urlSuffix = this.urlSuffix; + cloned.useRootPath = this.useRootPath; + + return cloned; + } + + private static Pattern identifierPattern = Pattern.compile("^(?:([^/]+)/)??(?:([^/]+)/)??(?:v(\\d+)/)?" + "(?:([^#/]+?)(?:\\.([^.#/]+))?)(?:#([^/]+))?$"); + + /** + * Parses a cloudinary identifier of the form:
+ * {@code [/][/][v/][.][#]} + */ + public Url fromIdentifier(String identifier) { + Matcher matcher = identifierPattern.matcher(identifier); + if (!matcher.matches()) { + throw new RuntimeException(String.format("Couldn't parse identifier %s", identifier)); + } + + String resourceType = matcher.group(1); + if (resourceType != null) { + resourceType(resourceType); + } + + String type = matcher.group(2); + if (type != null) { + type(type); + } + + String version = matcher.group(3); + if (version != null) { + version(version); + } + + String publicId = matcher.group(4); + if (publicId != null) { + publicId(publicId); + } + + String format = matcher.group(5); + if (format != null) { + format(format); + } + + // Signature (group 6) is not used + + return this; + } + + public Url type(String type) { + this.type = type; + return this; + } + + public Url resourcType(String resourceType) { + return resourceType(resourceType); + } + + public Url resourceType(String resourceType) { + this.resourceType = resourceType; + return this; + } + + public Url publicId(Object publicId) { + this.publicId = ObjectUtils.asString(publicId); + return this; + } + + public Url format(String format) { + this.format = format; + return this; + } + + public Url cloudName(String cloudName) { + this.config.cloudName = cloudName; + return this; + } + + public Url secureDistribution(String secureDistribution) { + this.config.secureDistribution = secureDistribution; + return this; + } + + public Url secureCdnSubdomain(boolean secureCdnSubdomain) { + this.config.secureCdnSubdomain = secureCdnSubdomain; + return this; + } + + public Url suffix(String urlSuffix) { + this.urlSuffix = urlSuffix; + return this; + } + + public Url useRootPath(boolean useRootPath) { + this.useRootPath = useRootPath; + return this; + } + + public Url cname(String cname) { + this.config.cname = cname; + return this; + } + + public Url version(Object version) { + this.version = ObjectUtils.asString(version); + return this; + } + + public Url transformation(Transformation transformation) { + this.transformation = transformation; + return this; + } + + public Url secure(boolean secure) { + this.config.secure = secure; + return this; + } + + public Url privateCdn(boolean privateCdn) { + this.config.privateCdn = privateCdn; + return this; + } + + public Url cdnSubdomain(boolean cdnSubdomain) { + this.config.cdnSubdomain = cdnSubdomain; + return this; + } + + public Url shorten(boolean shorten) { + this.config.shorten = shorten; + return this; + } + + public Transformation transformation() { + if (this.transformation == null) + this.transformation = new Transformation(); + return this.transformation; + } + + public Url signed(boolean signUrl) { + this.signUrl = signUrl; + return this; + } + + public Url sourceTransformation(Map sourceTransformation) { + this.sourceTransformation = sourceTransformation; + return this; + } + + public Url sourceTransformationFor(String source, Transformation transformation) { + if (this.sourceTransformation == null) { + this.sourceTransformation = new HashMap(); + } + this.sourceTransformation.put(source, transformation); + return this; + } + + public Url sourceTypes(String[] sourceTypes) { + this.sourceTypes = sourceTypes; + return this; + } + + public Url fallbackContent(String fallbackContent) { + this.fallbackContent = fallbackContent; + return this; + } + + public Url posterTransformation(Transformation posterTransformation) { + this.posterTransformation = posterTransformation; + return this; + } + + @SuppressWarnings("rawtypes") + public Url posterTransformation(List posterTransformations) { + this.posterTransformation = new Transformation(posterTransformations); + return this; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Url posterTransformation(Map posterTransformations) { + List transformations = new ArrayList(); + Map copy = new HashMap(); + copy.putAll(posterTransformations); + transformations.add(copy); + this.posterTransformation = new Transformation(transformations); + return this; + } + + public Url posterSource(String posterSource) { + this.posterSource = posterSource; + return this; + } + + public Url posterUrl(Url posterUrl) { + this.posterUrl = posterUrl; + return this; + } + + public Url poster(Object poster) { + if (poster instanceof Transformation) { + return posterTransformation((Transformation) poster); + } else if (poster instanceof List) { + return posterTransformation((List) poster); + } else if (poster instanceof Map) { + return posterTransformation((Map) poster); + } else if (poster instanceof Url) { + return posterUrl((Url) poster); + } else if (poster instanceof String) { + return posterSource((String) poster); + } else if (poster == null || poster.equals(Boolean.FALSE)) { + return posterSource(""); + } else { + throw new IllegalArgumentException("Illegal value type supplied to poster. must be one of: , >, , , "); + } + } + + public String generate() { + return generate(null); + } + + public String generate(String source) { + + boolean useRootPath = this.config.useRootPath; + if (this.useRootPath != null) { + useRootPath = this.useRootPath; + } + + if (StringUtils.isEmpty(this.config.cloudName)) { + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + } + + if (!this.config.privateCdn) { + if (StringUtils.isNotBlank(urlSuffix)) { + throw new IllegalArgumentException("URL Suffix only supported in private CDN"); + } + if (useRootPath) { + throw new IllegalArgumentException("Root path only supported in private CDN"); + } + } + + if (source == null) { + if (publicId == null) { + if (this.source == null) { + return null; + } + source = this.source; + } else { + source = publicId; + } + } + + if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { + if (StringUtils.isEmpty(type) || "asset".equals(type)) { + return source; + } + } + + + if (type != null && type.equals("fetch") && !StringUtils.isEmpty(format)) { + transformation().fetchFormat(format); + this.format = null; + } + String transformationStr = transformation().generate(); + String signature = ""; + + + String[] finalizedSource = finalizeSource(source, format, urlSuffix); + source = finalizedSource[0]; + String sourceToSign = finalizedSource[1]; + + if (sourceToSign.contains("/") && !sourceToSign.matches("v[0-9]+.*") && !sourceToSign.matches("https?:/.*") && StringUtils.isEmpty(version)) { + version = "1"; + } + + if (version == null) + version = ""; + else + version = "v" + version; + + + if (signUrl) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + + String toSign = StringUtils.join(new String[]{transformationStr, sourceToSign}, "/"); + toSign = toSign.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); + + byte[] digest = md.digest(cloudinary.getUTF8Bytes(toSign + this.config.apiSecret)); + signature = Base64Coder.encodeURLSafeString(digest); + signature = "s--" + signature.substring(0, 8) + "--"; + } + + String resourceType = this.resourceType; + if (resourceType == null) resourceType = "image"; + String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); + String prefix = unsignedDownloadUrlPrefix(source, config.cloudName, config.privateCdn, config.cdnSubdomain, config.secureCdnSubdomain, config.cname, config.secure, config.secureDistribution); + + return StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + } + + private String[] finalizeSource(String source, String format, String urlSuffix) { + String[] result = new String[2]; + source = source.replaceAll("([^:])//", "\1/"); + + String sourceToSign; + if (source.toLowerCase().matches("^https?:/.*")) { + source = SmartUrlEncoder.encode(source); + sourceToSign = source; + } else { + try { + source = SmartUrlEncoder.encode(URLDecoder.decode(source.replace("+", "%2B"), "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + sourceToSign = source; + if (StringUtils.isNotBlank(urlSuffix)) { + Pattern pattern = Pattern.compile("[\\./]"); + Matcher matcher = pattern.matcher(urlSuffix); + if (matcher.find()) { + throw new IllegalArgumentException("url_suffix should not include . or /"); + } + source = source + "/" + urlSuffix; + } + if (StringUtils.isNotBlank(format)) { + source = source + "." + format; + sourceToSign = sourceToSign + "." + format; + } + } + result[0] = source; + result[1] = sourceToSign; + return result; + } + + public String finalizeResourceType(String resourceType, String type, String urlSuffix, boolean useRootPath, boolean shorten) { + if (type == null) { + type = "upload"; + } + if (!StringUtils.isBlank(urlSuffix)) { + if (resourceType.equals("image") && type.equals("upload")) { + resourceType = "images"; + type = null; + } else if (resourceType.equals("raw") && type.equals("upload")) { + resourceType = "files"; + type = null; + } else { + throw new IllegalArgumentException("URL Suffix only supported for image/upload and raw/upload"); + } + } + if (useRootPath) { + if ((resourceType.equals("image") && type.equals("upload")) || (resourceType.equals("images") && StringUtils.isBlank(type))) { + resourceType = null; + type = null; + } else { + throw new IllegalArgumentException("Root path only supported for image/upload"); + } + } + if (shorten && resourceType.equals("image") && type.equals("upload")) { + resourceType = "iu"; + type = null; + } + String result = resourceType; + if (type != null) { + result += "/" + type; + } + return result; + } + + public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean privateCdn, boolean cdnSubdomain, Boolean secureCdnSubdomain, String cname, boolean secure, String secureDistribution) { + if (this.config.cloudName.startsWith("/")) { + return "/res" + this.config.cloudName; + } + boolean sharedDomain = !this.config.privateCdn; + + String prefix; + + if (this.config.secure) { + if (StringUtils.isEmpty(this.config.secureDistribution) || this.config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { + secureDistribution = this.config.privateCdn ? this.config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + } + if (!sharedDomain) { + sharedDomain = secureDistribution.equals(Cloudinary.SHARED_CDN); + } + + if (secureCdnSubdomain == null && sharedDomain) { + secureCdnSubdomain = this.config.cdnSubdomain; + } + + if (secureCdnSubdomain != null && secureCdnSubdomain == true) { + secureDistribution = this.config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); + } + + prefix = "https://" + secureDistribution; + } else if (StringUtils.isNotBlank(this.config.cname)) { + String subdomain = this.config.cdnSubdomain ? "a" + shard(source) + "." : ""; + prefix = "http://" + subdomain + this.config.cname; + } else { + String protocol = "http://"; + cloudName = this.config.privateCdn ? this.config.cloudName + "-" : ""; + String res = "res"; + String subdomain = this.config.cdnSubdomain ? "-" + shard(source) : ""; + String domain = ".cloudinary.com"; + prefix = StringUtils.join(new String[]{protocol, cloudName, res, subdomain, domain}, ""); + } + if (sharedDomain) { + prefix += "/" + this.config.cloudName; + } + return prefix; + } + + private String shard(String input) { + CRC32 crc32 = new CRC32(); + crc32.update(cloudinary.getUTF8Bytes(input)); + return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); + } + + @SuppressWarnings("unchecked") + public String imageTag(String source) { + return imageTag(source, ObjectUtils.emptyMap()); + } + + public String imageTag(Map attributes) { + return imageTag(null, attributes); + } + + public String imageTag(String source, Map attributes) { + String url = generate(source); + attributes = new TreeMap(attributes); // Make sure they + // are ordered. + if (transformation().getHtmlHeight() != null) + attributes.put("height", transformation().getHtmlHeight()); + if (transformation().getHtmlWidth() != null) + attributes.put("width", transformation().getHtmlWidth()); + + boolean hiDPI = transformation().isHiDPI(); + boolean responsive = transformation().isResponsive(); + + if (hiDPI || responsive) { + attributes.put("data-src", url); + String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; + attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); + String responsivePlaceholder = attributes.remove("responsive_placeholder"); + if ("blank".equals(responsivePlaceholder)) { + responsivePlaceholder = CL_BLANK; + } + url = responsivePlaceholder; + } + + StringBuilder builder = new StringBuilder(); + builder.append(" attr : attributes.entrySet()) { + builder.append(" ").append(attr.getKey()).append("='").append(attr.getValue()).append("'"); + } + builder.append("/>"); + return builder.toString(); + } + + public String videoTag() { + return videoTag("", new HashMap()); + } + + public String videoTag(Map attributes) { + return videoTag("", attributes); + } + + private String finalizePosterUrl(String source) { + String posterUrl = null; + if (this.posterUrl != null) { + posterUrl = this.posterUrl.generate(); + } else if (this.posterTransformation != null) { + posterUrl = this.clone().format("jpg").transformation(new Transformation(this.posterTransformation)) + .generate(source); + } else if (this.posterSource != null) { + if (!StringUtils.isEmpty(this.posterSource)) + posterUrl = this.clone().format("jpg").generate(this.posterSource); + } else { + posterUrl = this.clone().format("jpg").generate(source); + } + return posterUrl; + } + + private void appendVideoSources(StringBuilder html, String source, String sourceType) { + Url sourceUrl = this.clone(); + if (this.sourceTransformation != null) { + Transformation transformation = this.transformation; + Transformation sourceTransformation = null; + if (this.sourceTransformation.get(sourceType) != null) + sourceTransformation = new Transformation(this.sourceTransformation.get(sourceType)); + if (transformation == null) { + transformation = sourceTransformation; + } else if (sourceTransformation != null) { + transformation = transformation.chainWith(sourceTransformation); + } + sourceUrl.transformation(transformation); + } + String src = sourceUrl.format(sourceType).generate(source); + String videoType = sourceType; + if (sourceType.equals("ogv")) + videoType = "ogg"; + String mimeType = "video/" + videoType; + html.append(""); + } + + public String videoTag(String source, Map attributes) { + if (StringUtils.isEmpty(source)) + source = this.source; + if (StringUtils.isEmpty(source)) + source = publicId; + if (StringUtils.isEmpty(source)) + throw new IllegalArgumentException("must supply source or public id"); + source = VIDEO_EXTENSION_RE.matcher(source).replaceFirst(""); + + if (this.resourceType == null) this.resourceType = "video"; + attributes = new TreeMap(attributes); // Make sure they are ordered. + + String[] sourceTypes = this.sourceTypes; + + if (sourceTypes == null) { + sourceTypes = DEFAULT_VIDEO_SOURCE_TYPES; + } + + String posterUrl = this.finalizePosterUrl(source); + + if (!StringUtils.isEmpty(posterUrl)) + attributes.put("poster", posterUrl); + + StringBuilder html = new StringBuilder().append(" 1; + if (!multiSource) { + url = generate(source + "." + sourceTypes[0]); + attributes.put("src", url); + } else { + generate(source); + } + + if (this.transformation.getHtmlHeight() != null) + attributes.put("height", this.transformation.getHtmlHeight()); + if (attributes.containsKey("html_height")) + attributes.put("height", attributes.remove("html_height")); + if (this.transformation.getHtmlWidth() != null) + attributes.put("width", this.transformation.getHtmlWidth()); + if (attributes.containsKey("html_width")) + attributes.put("width", attributes.remove("html_width")); + + for (Map.Entry attr : attributes.entrySet()) { + html.append(" ").append(attr.getKey()); + if (attr.getValue() != null) { + String value = ObjectUtils.asString(attr.getValue()); + html.append("='").append(value).append("'"); + } + } + + html.append(">"); + + if (multiSource) { + for (String sourceType : sourceTypes) { + this.appendVideoSources(html, source, sourceType); + } + } + + if (this.fallbackContent != null) + html.append(this.fallbackContent); + html.append(""); + return html.toString(); + } + + public String generateSpriteCss(String source) { + this.type = "sprite"; + if (!source.endsWith(".css")) + this.format = "css"; + return generate(source); + } + + public Url source(String source) { + this.source = source; + return this; + } + + public Url source(StoredFile source) { + if (source.getResourceType() != null) + this.resourceType = source.getResourceType(); + if (source.getType() != null) + this.type = source.getType(); + if (source.getVersion() != null) + this.version = source.getVersion().toString(); + this.format = source.getFormat(); + this.source = source.getPublicId(); + 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 9a259fd2..76a90b53 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,174 +11,175 @@ import org.cloudinary.json.JSONObject; 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" }; + 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"}; - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static final Map buildUploadParams(Map options) { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); + @SuppressWarnings({"rawtypes", "unchecked"}) + public static final Map buildUploadParams(Map options) { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); - params.put("public_id", (String) options.get("public_id")); - params.put("callback", (String) options.get("callback")); - params.put("format", (String) options.get("format")); - params.put("type", (String) options.get("type")); - for (String attr : BOOLEAN_UPLOAD_OPTIONS) { - Boolean value = ObjectUtils.asBoolean(options.get(attr), null); - if (value != null) - params.put(attr, value.toString()); - } + params.put("public_id", (String) options.get("public_id")); + params.put("callback", (String) options.get("callback")); + params.put("format", (String) options.get("format")); + params.put("type", (String) options.get("type")); + for (String attr : BOOLEAN_UPLOAD_OPTIONS) { + Boolean value = ObjectUtils.asBoolean(options.get(attr), null); + if (value != null) + params.put(attr, value.toString()); + } - params.put("notification_url", (String) options.get("notification_url")); - params.put("eager_notification_url", (String) options.get("eager_notification_url")); - params.put("proxy", (String) options.get("proxy")); - params.put("folder", (String) options.get("folder")); - params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); - params.put("moderation", options.get("moderation")); - Object responsive_breakpoints = options.get("responsive_breakpoints"); - if (responsive_breakpoints != null){ - params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints));} - params.put("upload_preset", options.get("upload_preset")); + params.put("notification_url", (String) options.get("notification_url")); + params.put("eager_notification_url", (String) options.get("eager_notification_url")); + params.put("proxy", (String) options.get("proxy")); + params.put("folder", (String) options.get("folder")); + params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); + params.put("moderation", options.get("moderation")); + Object responsive_breakpoints = options.get("responsive_breakpoints"); + if (responsive_breakpoints != null) { + params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints)); + } + params.put("upload_preset", options.get("upload_preset")); - if (options.get("signature") == null) { - params.put("eager", buildEager((List) options.get("eager"))); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); - } - processWriteParameters(options, params); - } else { - params.put("eager", (String) options.get("eager")); - params.put("transformation", (String) options.get("transformation")); - params.put("headers", (String) options.get("headers")); - params.put("tags", (String) options.get("tags")); - params.put("face_coordinates", (String) options.get("face_coordinates")); - params.put("context", (String) options.get("context")); - params.put("ocr", (String) options.get("ocr")); - params.put("raw_convert", (String) options.get("raw_convert")); - params.put("categorization", (String) options.get("categorization")); - params.put("detection", (String) options.get("detection")); - params.put("similarity_search", (String) options.get("similarity_search")); - params.put("auto_tagging", (String) options.get("auto_tagging")); - } - return params; - } + if (options.get("signature") == null) { + params.put("eager", buildEager((List) options.get("eager"))); + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + processWriteParameters(options, params); + } else { + params.put("eager", (String) options.get("eager")); + params.put("transformation", (String) options.get("transformation")); + params.put("headers", (String) options.get("headers")); + params.put("tags", (String) options.get("tags")); + params.put("face_coordinates", (String) options.get("face_coordinates")); + params.put("context", (String) options.get("context")); + params.put("ocr", (String) options.get("ocr")); + params.put("raw_convert", (String) options.get("raw_convert")); + params.put("categorization", (String) options.get("categorization")); + params.put("detection", (String) options.get("detection")); + params.put("similarity_search", (String) options.get("similarity_search")); + params.put("auto_tagging", (String) options.get("auto_tagging")); + } + return params; + } - protected static final String buildEager(List transformations) { - if (transformations == null) { - return null; - } - List eager = new ArrayList(); - for (Transformation transformation : transformations) { - List single_eager = new ArrayList(); - String transformationString = transformation.generate(); - if (StringUtils.isNotBlank(transformationString)) { - single_eager.add(transformationString); - } - if (transformation instanceof EagerTransformation) { - EagerTransformation eagerTransformation = (EagerTransformation) transformation; - if (StringUtils.isNotBlank(eagerTransformation.getFormat())) { - single_eager.add(eagerTransformation.getFormat()); - } - } - eager.add(StringUtils.join(single_eager, "/")); - } - return StringUtils.join(eager, "|"); - } + protected static final String buildEager(List transformations) { + if (transformations == null) { + return null; + } + List eager = new ArrayList(); + for (Transformation transformation : transformations) { + List single_eager = new ArrayList(); + String transformationString = transformation.generate(); + if (StringUtils.isNotBlank(transformationString)) { + single_eager.add(transformationString); + } + if (transformation instanceof EagerTransformation) { + EagerTransformation eagerTransformation = (EagerTransformation) transformation; + if (StringUtils.isNotBlank(eagerTransformation.getFormat())) { + single_eager.add(eagerTransformation.getFormat()); + } + } + eager.add(StringUtils.join(single_eager, "/")); + } + return StringUtils.join(eager, "|"); + } - @SuppressWarnings("unchecked") - public static final void processWriteParameters(Map options, Map params) { - if (options.get("headers") != null) - params.put("headers", buildCustomHeaders(options.get("headers"))); - if (options.get("tags") != null) - params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); - if (options.get("face_coordinates") != null) - params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); - if (options.get("custom_coordinates") != null) - params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); - if (options.get("context") != null) - params.put("context", ObjectUtils.encodeMap(options.get("context"))); - if (options.get("ocr") != null) - params.put("ocr", options.get("ocr")); - if (options.get("raw_convert") != null) - params.put("raw_convert", options.get("raw_convert")); - if (options.get("categorization") != null) - params.put("categorization", options.get("categorization")); - if (options.get("detection") != null) - params.put("detection", options.get("detection")); - if (options.get("similarity_search") != null) - params.put("similarity_search", options.get("similarity_search")); - if (options.get("background_removal") != null) - params.put("background_removal", options.get("background_removal")); - if (options.get("auto_tagging") != null) - params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); - } + @SuppressWarnings("unchecked") + public static final void processWriteParameters(Map options, Map params) { + if (options.get("headers") != null) + params.put("headers", buildCustomHeaders(options.get("headers"))); + if (options.get("tags") != null) + params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); + if (options.get("face_coordinates") != null) + params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); + if (options.get("custom_coordinates") != null) + params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); + if (options.get("context") != null) + params.put("context", ObjectUtils.encodeMap(options.get("context"))); + if (options.get("ocr") != null) + params.put("ocr", options.get("ocr")); + if (options.get("raw_convert") != null) + params.put("raw_convert", options.get("raw_convert")); + if (options.get("categorization") != null) + params.put("categorization", options.get("categorization")); + if (options.get("detection") != null) + params.put("detection", options.get("detection")); + if (options.get("similarity_search") != null) + params.put("similarity_search", options.get("similarity_search")); + if (options.get("background_removal") != null) + params.put("background_removal", options.get("background_removal")); + if (options.get("auto_tagging") != null) + params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); + } - @SuppressWarnings("unchecked") - protected static final String buildCustomHeaders(Object headers) { - if (headers == null) { - return null; - } else if (headers instanceof String) { - return (String) headers; - } else if (headers instanceof Object[]) { - return StringUtils.join((Object[]) headers, "\n") + "\n"; - } else { - Map headersMap = (Map) headers; - StringBuilder builder = new StringBuilder(); - for (Map.Entry header : headersMap.entrySet()) { - builder.append(header.getKey()).append(": ").append(header.getValue()).append("\n"); - } - return builder.toString(); - } - } + @SuppressWarnings("unchecked") + protected static final String buildCustomHeaders(Object headers) { + if (headers == null) { + return null; + } else if (headers instanceof String) { + return (String) headers; + } else if (headers instanceof Object[]) { + return StringUtils.join((Object[]) headers, "\n") + "\n"; + } else { + Map headersMap = (Map) headers; + StringBuilder builder = new StringBuilder(); + for (Map.Entry header : headersMap.entrySet()) { + builder.append(header.getKey()).append(": ").append(header.getValue()).append("\n"); + } + return builder.toString(); + } + } - @SuppressWarnings("rawtypes") - public static void clearEmpty(Map params) { - for (Iterator iterator = params.values().iterator(); iterator.hasNext();) { - Object value = iterator.next(); - if (value == null || "".equals(value)) { - iterator.remove(); - } - } - } + @SuppressWarnings("rawtypes") + public static void clearEmpty(Map params) { + for (Iterator iterator = params.values().iterator(); iterator.hasNext(); ) { + Object value = iterator.next(); + if (value == null || "".equals(value)) { + iterator.remove(); + } + } + } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static final Map buildArchiveParams(Map options, String targetFormat) { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("type", options.get("type")); - params.put("mode", options.get("mode")); - params.put("target_format", targetFormat); - params.put("target_public_id", options.get("target_public_id")); - params.put("flatten_folders", ObjectUtils.asBoolean(options.get("flatten_folders"), false)); - params.put("flatten_transformations", ObjectUtils.asBoolean(options.get("flatten_transformations"), false)); - params.put("use_original_filename", ObjectUtils.asBoolean(options.get("use_original_filename"), false)); - params.put("async", ObjectUtils.asBoolean(options.get("async"), false)); - params.put("keep_derived", ObjectUtils.asBoolean(options.get("keep_derived"), false)); - params.put("notification_url", options.get("notification_url")); - if (options.get("target_tags") != null) - params.put("target_tags", ObjectUtils.asArray(options.get("target_tags"))); - if (options.get("tags") != null) - params.put("tags", ObjectUtils.asArray(options.get("tags"))); - if (options.get("public_ids") != null) - params.put("public_ids", ObjectUtils.asArray(options.get("public_ids"))); - if (options.get("prefixes") != null) - params.put("prefixes", ObjectUtils.asArray(options.get("prefixes"))); - if (options.get("transformations") != null) - params.put("transformations", buildEager((List) options.get("transformations"))); - if (options.get("timestamp") != null) - params.put("timestamp", options.get("timestamp")); - else - params.put("timestamp", Util.timestamp()); - return params; - } - - protected static String timestamp() { - return new Long(System.currentTimeMillis() / 1000L).toString(); - } + @SuppressWarnings({"rawtypes", "unchecked"}) + public static final Map buildArchiveParams(Map options, String targetFormat) { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("type", options.get("type")); + params.put("mode", options.get("mode")); + params.put("target_format", targetFormat); + params.put("target_public_id", options.get("target_public_id")); + params.put("flatten_folders", ObjectUtils.asBoolean(options.get("flatten_folders"), false)); + params.put("flatten_transformations", ObjectUtils.asBoolean(options.get("flatten_transformations"), false)); + params.put("use_original_filename", ObjectUtils.asBoolean(options.get("use_original_filename"), false)); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false)); + params.put("keep_derived", ObjectUtils.asBoolean(options.get("keep_derived"), false)); + params.put("notification_url", options.get("notification_url")); + if (options.get("target_tags") != null) + params.put("target_tags", ObjectUtils.asArray(options.get("target_tags"))); + if (options.get("tags") != null) + params.put("tags", ObjectUtils.asArray(options.get("tags"))); + if (options.get("public_ids") != null) + params.put("public_ids", ObjectUtils.asArray(options.get("public_ids"))); + if (options.get("prefixes") != null) + params.put("prefixes", ObjectUtils.asArray(options.get("prefixes"))); + if (options.get("transformations") != null) + params.put("transformations", buildEager((List) options.get("transformations"))); + if (options.get("timestamp") != null) + params.put("timestamp", options.get("timestamp")); + else + params.put("timestamp", Util.timestamp()); + return params; + } + + protected static String timestamp() { + return new Long(System.currentTimeMillis() / 1000L).toString(); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java index 32ed9490..1de0dfa5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java @@ -5,6 +5,7 @@ @SuppressWarnings("rawtypes") public interface ApiResponse extends Map { - Map rateLimits() throws ParseException; - RateLimit apiRateLimit() throws ParseException; + Map rateLimits() throws ParseException; + + RateLimit apiRateLimit() throws ParseException; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java b/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java index 0c39e1d5..4d920a16 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java @@ -3,9 +3,9 @@ import com.cloudinary.api.exceptions.ApiException; public class AuthorizationRequired extends ApiException { - private static final long serialVersionUID = 7160740370855761014L; + private static final long serialVersionUID = 7160740370855761014L; - public AuthorizationRequired(String message) { - super(message); - } + public AuthorizationRequired(String message) { + super(message); + } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java b/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java index 10496ca9..da44da34 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java @@ -3,35 +3,35 @@ import java.util.Date; public class RateLimit { - private long limit = 0L; - private long remaining = 0L; - private Date reset = null; + private long limit = 0L; + private long remaining = 0L; + private Date reset = null; - public RateLimit() { - super(); - } + public RateLimit() { + super(); + } - public long getLimit() { - return limit; - } + public long getLimit() { + return limit; + } - public void setLimit(long limit) { - this.limit = limit; - } + public void setLimit(long limit) { + this.limit = limit; + } - public long getRemaining() { - return remaining; - } + public long getRemaining() { + return remaining; + } - public void setRemaining(long remaining) { - this.remaining = remaining; - } + public void setRemaining(long remaining) { + this.remaining = remaining; + } - public Date getReset() { - return reset; - } + public Date getReset() { + return reset; + } - public void setReset(Date reset) { - this.reset = reset; - } + public void setReset(Date reset) { + this.reset = reset; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java index a46939b8..e4ce2729 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java @@ -1,9 +1,9 @@ package com.cloudinary.api.exceptions; public class AlreadyExists extends ApiException { - private static final long serialVersionUID = 999568182896607322L; + private static final long serialVersionUID = 999568182896607322L; - public AlreadyExists(String message) { - super(message); - } + public AlreadyExists(String message) { + super(message); + } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java index 8b33bdb9..e1ca0f3c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java @@ -1,9 +1,9 @@ package com.cloudinary.api.exceptions; public class ApiException extends Exception { - private static final long serialVersionUID = 4416861825144420038L; + private static final long serialVersionUID = 4416861825144420038L; - public ApiException(String message) { - super(message); - } + public ApiException(String message) { + super(message); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java index b19aca10..a57f75a1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java @@ -2,9 +2,9 @@ public class BadRequest extends ApiException { - private static final long serialVersionUID = 1410136354253339531L; + private static final long serialVersionUID = 1410136354253339531L; - public BadRequest(String message) { - super(message); - } + public BadRequest(String message) { + super(message); + } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java index 92375009..34992a81 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java @@ -1,8 +1,9 @@ package com.cloudinary.api.exceptions; public class GeneralError extends ApiException { - private static final long serialVersionUID = 4553362706625067182L; - public GeneralError(String message) { + private static final long serialVersionUID = 4553362706625067182L; + + public GeneralError(String message) { super(message); } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java index b127d9ec..cad00f98 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java @@ -2,8 +2,9 @@ public class NotAllowed extends ApiException { - private static final long serialVersionUID = 4371365822491647653L; - public NotAllowed(String message) { + private static final long serialVersionUID = 4371365822491647653L; + + public NotAllowed(String message) { super(message); } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java index 418efaf9..1c93692d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java @@ -2,8 +2,9 @@ public class NotFound extends ApiException { - private static final long serialVersionUID = -2072640462778940357L; - public NotFound(String message) { + private static final long serialVersionUID = -2072640462778940357L; + + public NotFound(String message) { super(message); } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java index eea272e9..0afe2394 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java @@ -2,8 +2,9 @@ public class RateLimited extends ApiException { - private static final long serialVersionUID = -8298038106172355219L; - public RateLimited(String message) { + private static final long serialVersionUID = -8298038106172355219L; + + public RateLimited(String message) { super(message); } } 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 ca302717..ff358452 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -7,10 +7,12 @@ import com.cloudinary.api.ApiResponse; public abstract class AbstractApiStrategy { - protected Api api; - public void init(Api api){ - this.api = api; - } - @SuppressWarnings("rawtypes") - public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; + protected Api api; + + public void init(Api api) { + this.api = api; + } + + @SuppressWarnings("rawtypes") + public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index d413bcf6..fee97c9a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -7,15 +7,16 @@ import com.cloudinary.Uploader; public abstract class AbstractUploaderStrategy { - protected Uploader uploader; - public void init(Uploader uploader){ - this.uploader = uploader; - } - - public Cloudinary cloudinary(){ - return this.uploader.cloudinary(); - } - - @SuppressWarnings("rawtypes") - public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException ; + protected Uploader uploader; + + public void init(Uploader uploader) { + this.uploader = uploader; + } + + public Cloudinary cloudinary() { + return this.uploader.cloudinary(); + } + + @SuppressWarnings("rawtypes") + public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java index 8194e389..66d37b83 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java @@ -4,30 +4,30 @@ public class StrategyLoader { - @SuppressWarnings("unchecked") - public static T load(String className) { - T result = null; - try { - Class clazz = Class.forName(className); - result = (T) clazz.newInstance(); - } catch (Exception e) { - } - return result; - } + @SuppressWarnings("unchecked") + public static T load(String className) { + T result = null; + try { + Class clazz = Class.forName(className); + result = (T) clazz.newInstance(); + } catch (Exception e) { + } + return result; + } - public static T find(List strategies) { - for (int i = 0; i < strategies.size(); i++) { - T strategy = load(strategies.get(i)); - if (strategy != null) { - return strategy; - } - } - return null; - - } + public static T find(List strategies) { + for (int i = 0; i < strategies.size(); i++) { + T strategy = load(strategies.get(i)); + if (strategy != null) { + return strategy; + } + } + return null; + + } + + public boolean exists(List strategies) { + return find(strategies) != null; + } - public boolean exists(List strategies) { - return find(strategies) != null; - } - } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java index 76805d3e..b4ef45ef 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java @@ -5,61 +5,61 @@ import com.cloudinary.utils.StringUtils; public abstract class AbstractLayer> { - abstract T getThis(); - - protected String resourceType = null; - protected String type = null; - protected String publicId = null; - protected String format = null; - - public T resourceType(String resourceType) { - this.resourceType = resourceType; - return getThis(); - } - - public T type(String type) { - this.type = type; - return getThis(); - } - - public T publicId(String publicId) { - this.publicId = publicId.replace('/', ':'); - return getThis(); - } - - public T format(String format) { - this.format = format; - return getThis(); - } - - @Override - public String toString() { - ArrayList components = new ArrayList(); - - if (this.resourceType != null && !this.resourceType.equals("image")) { - components.add(this.resourceType); - } - - if (this.type != null && !this.type.equals("upload")) { - components.add(this.type); - } - - if (this.publicId == null) { - throw new IllegalArgumentException("Must supply publicId"); - } - - components.add(formattedPublicId()); - - return StringUtils.join(components, ":"); - } - - protected String formattedPublicId() { - String transientPublicId = this.publicId; - - if (this.format != null) { - transientPublicId = transientPublicId + "." + this.format; - } - - return transientPublicId; - } + abstract T getThis(); + + protected String resourceType = null; + protected String type = null; + protected String publicId = null; + protected String format = null; + + public T resourceType(String resourceType) { + this.resourceType = resourceType; + return getThis(); + } + + public T type(String type) { + this.type = type; + return getThis(); + } + + public T publicId(String publicId) { + this.publicId = publicId.replace('/', ':'); + return getThis(); + } + + public T format(String format) { + this.format = format; + return getThis(); + } + + @Override + public String toString() { + ArrayList components = new ArrayList(); + + if (this.resourceType != null && !this.resourceType.equals("image")) { + components.add(this.resourceType); + } + + if (this.type != null && !this.type.equals("upload")) { + components.add(this.type); + } + + if (this.publicId == null) { + throw new IllegalArgumentException("Must supply publicId"); + } + + components.add(formattedPublicId()); + + return StringUtils.join(components, ":"); + } + + protected String formattedPublicId() { + String transientPublicId = this.publicId; + + if (this.format != null) { + transientPublicId = transientPublicId + "." + this.format; + } + + return transientPublicId; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java index d60dfb7b..1cdad76e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java @@ -1,8 +1,8 @@ package com.cloudinary.transformation; public class Layer extends AbstractLayer { - @Override - Layer getThis() { - return this; - } + @Override + Layer getThis() { + return this; + } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java index 84198805..81aa962c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java @@ -3,5 +3,5 @@ /** * @deprecated */ -public class LayerBuilder extends Layer{ +public class LayerBuilder extends Layer { } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java index 6278da0b..006bc547 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java @@ -1,7 +1,7 @@ package com.cloudinary.transformation; public class SubtitlesLayer extends TextLayer { - public SubtitlesLayer() { - this.resourceType = "subtitles"; - } + public SubtitlesLayer() { + this.resourceType = "subtitles"; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index 954eac64..fb4037aa 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -6,144 +6,144 @@ import com.cloudinary.utils.StringUtils; public class TextLayer extends AbstractLayer { - protected String resourceType = "text"; - protected String fontFamily = null; - protected Integer fontSize = null; - protected String fontWeight = null; - protected String fontStyle = null; - protected String textDecoration = null; - protected String textAlign = null; - protected String stroke = null; - protected String letterSpacing = null; - protected Integer lineSpacing = null; - protected String text = null; - - @Override - TextLayer getThis() { - return this; - } - - public TextLayer resourceType(String resourceType) { - throw new UnsupportedOperationException("Cannot modify resourceType for text layers"); - } - - public TextLayer type(String type) { - throw new UnsupportedOperationException("Cannot modify type for text layers"); - } - - public TextLayer format(String format) { - throw new UnsupportedOperationException("Cannot modify format for text layers"); - } - - public TextLayer fontFamily(String fontFamily) { - this.fontFamily = fontFamily; - return getThis(); - } - - public TextLayer fontSize(int fontSize) { - this.fontSize = fontSize; - return getThis(); - } - - public TextLayer fontWeight(String fontWeight) { - this.fontWeight = fontWeight; - return getThis(); - } - - public TextLayer fontStyle(String fontStyle) { - this.fontStyle = fontStyle; - return getThis(); - } - - public TextLayer textDecoration(String textDecoration) { - this.textDecoration = textDecoration; - return getThis(); - } - - public TextLayer textAlign(String textAlign) { - this.textAlign = textAlign; - return getThis(); - } - - public TextLayer stroke(String stroke) { - this.stroke = stroke; - return getThis(); - } - - public TextLayer letterSpacing(String letterSpacing) { - this.letterSpacing = letterSpacing; - return getThis(); - } - - public TextLayer lineSpacing(Integer lineSpacing) { - this.lineSpacing = lineSpacing; - return getThis(); - } - - public TextLayer text(String text) { - this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); - return getThis(); - } - - @Override - public String toString() { - if (this.publicId == null && this.text == null) { - throw new IllegalArgumentException("Must supply either text or public_id."); - } - - ArrayList components = new ArrayList(); - components.add(this.resourceType); - - String styleIdentifier = textStyleIdentifier(); - if (styleIdentifier != null) { - components.add(styleIdentifier); - } - - if (this.publicId != null) { - components.add(this.formattedPublicId()); - } - - if (this.text != null) { - components.add(this.text); - } - - return StringUtils.join(components, ":"); - } - - protected String textStyleIdentifier() { - ArrayList components = new ArrayList(); - - if (StringUtils.isNotBlank(this.fontWeight) && !this.fontWeight.equals("normal")) - components.add(this.fontWeight); - if (StringUtils.isNotBlank(this.fontStyle) && !this.fontStyle.equals("normal")) - components.add(this.fontStyle); - if (StringUtils.isNotBlank(this.textDecoration) && !this.textDecoration.equals("none")) - components.add(this.textDecoration); - if (StringUtils.isNotBlank(this.textAlign)) - components.add(this.textAlign); - if (StringUtils.isNotBlank(this.stroke) && !this.stroke.equals("none")) - components.add(this.stroke); - if (StringUtils.isNotBlank(this.letterSpacing)) - components.add("letter_spacing_" + this.letterSpacing); - if (this.lineSpacing != null) - components.add("line_spacing_" + this.lineSpacing.toString()); - - if (this.fontFamily == null && this.fontSize == null && components.isEmpty()) { - return null; - } - - if (this.fontFamily == null) { - throw new IllegalArgumentException("Must supply fontFamily."); - } - - if (this.fontSize == null) { - throw new IllegalArgumentException("Must supply fontSize."); - } - - components.add(0, Integer.toString(this.fontSize)); - components.add(0, this.fontFamily); - - return StringUtils.join(components, "_"); - - } + protected String resourceType = "text"; + protected String fontFamily = null; + protected Integer fontSize = null; + protected String fontWeight = null; + protected String fontStyle = null; + protected String textDecoration = null; + protected String textAlign = null; + protected String stroke = null; + protected String letterSpacing = null; + protected Integer lineSpacing = null; + protected String text = null; + + @Override + TextLayer getThis() { + return this; + } + + public TextLayer resourceType(String resourceType) { + throw new UnsupportedOperationException("Cannot modify resourceType for text layers"); + } + + public TextLayer type(String type) { + throw new UnsupportedOperationException("Cannot modify type for text layers"); + } + + public TextLayer format(String format) { + throw new UnsupportedOperationException("Cannot modify format for text layers"); + } + + public TextLayer fontFamily(String fontFamily) { + this.fontFamily = fontFamily; + return getThis(); + } + + public TextLayer fontSize(int fontSize) { + this.fontSize = fontSize; + return getThis(); + } + + public TextLayer fontWeight(String fontWeight) { + this.fontWeight = fontWeight; + return getThis(); + } + + public TextLayer fontStyle(String fontStyle) { + this.fontStyle = fontStyle; + return getThis(); + } + + public TextLayer textDecoration(String textDecoration) { + this.textDecoration = textDecoration; + return getThis(); + } + + public TextLayer textAlign(String textAlign) { + this.textAlign = textAlign; + return getThis(); + } + + public TextLayer stroke(String stroke) { + this.stroke = stroke; + return getThis(); + } + + public TextLayer letterSpacing(String letterSpacing) { + this.letterSpacing = letterSpacing; + return getThis(); + } + + public TextLayer lineSpacing(Integer lineSpacing) { + this.lineSpacing = lineSpacing; + return getThis(); + } + + public TextLayer text(String text) { + this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); + return getThis(); + } + + @Override + public String toString() { + if (this.publicId == null && this.text == null) { + throw new IllegalArgumentException("Must supply either text or public_id."); + } + + ArrayList components = new ArrayList(); + components.add(this.resourceType); + + String styleIdentifier = textStyleIdentifier(); + if (styleIdentifier != null) { + components.add(styleIdentifier); + } + + if (this.publicId != null) { + components.add(this.formattedPublicId()); + } + + if (this.text != null) { + components.add(this.text); + } + + return StringUtils.join(components, ":"); + } + + protected String textStyleIdentifier() { + ArrayList components = new ArrayList(); + + if (StringUtils.isNotBlank(this.fontWeight) && !this.fontWeight.equals("normal")) + components.add(this.fontWeight); + if (StringUtils.isNotBlank(this.fontStyle) && !this.fontStyle.equals("normal")) + components.add(this.fontStyle); + if (StringUtils.isNotBlank(this.textDecoration) && !this.textDecoration.equals("none")) + components.add(this.textDecoration); + if (StringUtils.isNotBlank(this.textAlign)) + components.add(this.textAlign); + if (StringUtils.isNotBlank(this.stroke) && !this.stroke.equals("none")) + components.add(this.stroke); + if (StringUtils.isNotBlank(this.letterSpacing)) + components.add("letter_spacing_" + this.letterSpacing); + if (this.lineSpacing != null) + components.add("line_spacing_" + this.lineSpacing.toString()); + + if (this.fontFamily == null && this.fontSize == null && components.isEmpty()) { + return null; + } + + if (this.fontFamily == null) { + throw new IllegalArgumentException("Must supply fontFamily."); + } + + if (this.fontSize == null) { + throw new IllegalArgumentException("Must supply fontSize."); + } + + components.add(0, Integer.toString(this.fontSize)); + components.add(0, this.fontFamily); + + return StringUtils.join(components, "_"); + + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java index f110f425..97c21b0f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java @@ -18,300 +18,278 @@ /** * A Base64 encoder/decoder. - * + *

*

* This class is used to encode and decode data in Base64 format as described in * RFC 1521. - * + * * @author Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland, * www.source-code.biz */ public class Base64Coder { - // The line separator string of the operating system. - private static final String systemLineSeparator = System - .getProperty("line.separator"); + // The line separator string of the operating system. + private static final String systemLineSeparator = System + .getProperty("line.separator"); + + // Mapping table from 6-bit nibbles to Base64 characters. + private static final char[] map1 = new char[64]; + + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) + map1[i++] = c; + for (char c = 'a'; c <= 'z'; c++) + map1[i++] = c; + for (char c = '0'; c <= '9'; c++) + map1[i++] = c; + map1[i++] = '+'; + map1[i++] = '/'; + } - // Mapping table from 6-bit nibbles to Base64 characters. - private static final char[] map1 = new char[64]; - static { - int i = 0; - for (char c = 'A'; c <= 'Z'; c++) - map1[i++] = c; - for (char c = 'a'; c <= 'z'; c++) - map1[i++] = c; - for (char c = '0'; c <= '9'; c++) - map1[i++] = c; - map1[i++] = '+'; - map1[i++] = '/'; - } + // Mapping table from Base64 characters to 6-bit nibbles. + private static final byte[] map2 = new byte[128]; - // Mapping table from Base64 characters to 6-bit nibbles. - private static final byte[] map2 = new byte[128]; - static { - for (int i = 0; i < map2.length; i++) - map2[i] = -1; - for (int i = 0; i < 64; i++) - map2[map1[i]] = (byte) i; - } + static { + for (int i = 0; i < map2.length; i++) + map2[i] = -1; + for (int i = 0; i < 64; i++) + map2[map1[i]] = (byte) i; + } - /** - * Encodes a string into Base64 format. No blanks or line breaks are - * inserted. - * - * @param s - * A String to be encoded. - * @return A String containing the Base64 encoded data. - */ - public static String encodeString(String s) { - return new String(encode(s.getBytes())); - } + /** + * Encodes a string into Base64 format. No blanks or line breaks are + * inserted. + * + * @param s A String to be encoded. + * @return A String containing the Base64 encoded data. + */ + public static String encodeString(String s) { + return new String(encode(s.getBytes())); + } - /** - * Encodes a byte array into Base 64 format and breaks the output into lines - * of 76 characters. This method is compatible with - * {@code sun.misc.BASE64Encoder.encodeBuffer(byte[])}. - * - * @param in - * An array containing the data bytes to be encoded. - * @return A String containing the Base64 encoded data, broken into lines. - */ - public static String encodeLines(byte[] in) { - return encodeLines(in, 0, in.length, 76, systemLineSeparator); - } + /** + * Encodes a byte array into Base 64 format and breaks the output into lines + * of 76 characters. This method is compatible with + * {@code sun.misc.BASE64Encoder.encodeBuffer(byte[])}. + * + * @param in An array containing the data bytes to be encoded. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines(byte[] in) { + return encodeLines(in, 0, in.length, 76, systemLineSeparator); + } - /** - * Encodes a byte array into Base 64 format and breaks the output into - * lines. - * - * @param in - * An array containing the data bytes to be encoded. - * @param iOff - * Offset of the first byte in {@code in} to be processed. - * @param iLen - * Number of bytes to be processed in {@code in}, starting - * at {@code iOff}. - * @param lineLen - * Line length for the output data. Should be a multiple of 4. - * @param lineSeparator - * The line separator to be used to separate the output lines. - * @return A String containing the Base64 encoded data, broken into lines. - */ - public static String encodeLines(byte[] in, int iOff, int iLen, - int lineLen, String lineSeparator) { - int blockLen = (lineLen * 3) / 4; - if (blockLen <= 0) - throw new IllegalArgumentException(); - int lines = (iLen + blockLen - 1) / blockLen; - int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); - StringBuilder buf = new StringBuilder(bufLen); - int ip = 0; - while (ip < iLen) { - int l = Math.min(iLen - ip, blockLen); - buf.append(encode(in, iOff + ip, l)); - buf.append(lineSeparator); - ip += l; - } - return buf.toString(); - } + /** + * Encodes a byte array into Base 64 format and breaks the output into + * lines. + * + * @param in An array containing the data bytes to be encoded. + * @param iOff Offset of the first byte in {@code in} to be processed. + * @param iLen Number of bytes to be processed in {@code in}, starting + * at {@code iOff}. + * @param lineLen Line length for the output data. Should be a multiple of 4. + * @param lineSeparator The line separator to be used to separate the output lines. + * @return A String containing the Base64 encoded data, broken into lines. + */ + public static String encodeLines( + byte[] in, int iOff, int iLen, + int lineLen, String lineSeparator) { + int blockLen = (lineLen * 3) / 4; + if (blockLen <= 0) + throw new IllegalArgumentException(); + int lines = (iLen + blockLen - 1) / blockLen; + int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); + StringBuilder buf = new StringBuilder(bufLen); + int ip = 0; + while (ip < iLen) { + int l = Math.min(iLen - ip, blockLen); + buf.append(encode(in, iOff + ip, l)); + buf.append(lineSeparator); + ip += l; + } + return buf.toString(); + } - /** - * Encodes a byte array into Base64 format. No blanks or line breaks are - * inserted in the output. - * - * @param in - * An array containing the data bytes to be encoded. - * @return A character array containing the Base64 encoded data. - */ - public static char[] encode(byte[] in) { - return encode(in, 0, in.length); - } + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in An array containing the data bytes to be encoded. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in) { + return encode(in, 0, in.length); + } - /** - * Encodes a byte array into Base64 format. No blanks or line breaks are - * inserted in the output. - * - * @param in - * An array containing the data bytes to be encoded. - * @param iLen - * Number of bytes to process in {@code in}. - * @return A character array containing the Base64 encoded data. - */ - public static char[] encode(byte[] in, int iLen) { - return encode(in, 0, iLen); - } + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in An array containing the data bytes to be encoded. + * @param iLen Number of bytes to process in {@code in}. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iLen) { + return encode(in, 0, iLen); + } - /** - * Encodes a byte array into Base64 format. No blanks or line breaks are - * inserted in the output. - * - * @param in - * An array containing the data bytes to be encoded. - * @param iOff - * Offset of the first byte in {@code in} to be processed. - * @param iLen - * Number of bytes to process in {@code in}, starting at - * {@code iOff}. - * @return A character array containing the Base64 encoded data. - */ - public static char[] encode(byte[] in, int iOff, int iLen) { - int oDataLen = (iLen * 4 + 2) / 3; // output length without padding - int oLen = ((iLen + 2) / 3) * 4; // output length including padding - char[] out = new char[oLen]; - int ip = iOff; - int iEnd = iOff + iLen; - int op = 0; - while (ip < iEnd) { - int i0 = in[ip++] & 0xff; - int i1 = ip < iEnd ? in[ip++] & 0xff : 0; - int i2 = ip < iEnd ? in[ip++] & 0xff : 0; - int o0 = i0 >>> 2; - int o1 = ((i0 & 3) << 4) | (i1 >>> 4); - int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); - int o3 = i2 & 0x3F; - out[op++] = map1[o0]; - out[op++] = map1[o1]; - out[op] = op < oDataLen ? map1[o2] : '='; - op++; - out[op] = op < oDataLen ? map1[o3] : '='; - op++; - } - return out; - } + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted in the output. + * + * @param in An array containing the data bytes to be encoded. + * @param iOff Offset of the first byte in {@code in} to be processed. + * @param iLen Number of bytes to process in {@code in}, starting at + * {@code iOff}. + * @return A character array containing the Base64 encoded data. + */ + public static char[] encode(byte[] in, int iOff, int iLen) { + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iEnd ? in[ip++] & 0xff : 0; + int i2 = ip < iEnd ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = op < oDataLen ? map1[o2] : '='; + op++; + out[op] = op < oDataLen ? map1[o3] : '='; + op++; + } + return out; + } - /** - * Decodes a string from Base64 format. No blanks or line breaks are allowed - * within the Base64 encoded input data. - * - * @param s - * A Base64 String to be decoded. - * @return A String containing the decoded data. - * @throws IllegalArgumentException - * If the input is not valid Base64 encoded data. - */ - public static String decodeString(String s) { - return new String(decode(s)); - } + /** + * Decodes a string from Base64 format. No blanks or line breaks are allowed + * within the Base64 encoded input data. + * + * @param s A Base64 String to be decoded. + * @return A String containing the decoded data. + * @throws IllegalArgumentException If the input is not valid Base64 encoded data. + */ + public static String decodeString(String s) { + return new String(decode(s)); + } - /** - * Decodes a byte array from Base64 format and ignores line separators, tabs - * and blanks. CR, LF, Tab and Space characters are ignored in the input - * data. This method is compatible with - * {@code sun.misc.BASE64Decoder.decodeBuffer(String)}. - * - * @param s - * A Base64 String to be decoded. - * @return An array containing the decoded data bytes. - * @throws IllegalArgumentException - * If the input is not valid Base64 encoded data. - */ - public static byte[] decodeLines(String s) { - char[] buf = new char[s.length()]; - int p = 0; - for (int ip = 0; ip < s.length(); ip++) { - char c = s.charAt(ip); - if (c != ' ' && c != '\r' && c != '\n' && c != '\t') - buf[p++] = c; - } - return decode(buf, 0, p); - } + /** + * Decodes a byte array from Base64 format and ignores line separators, tabs + * and blanks. CR, LF, Tab and Space characters are ignored in the input + * data. This method is compatible with + * {@code sun.misc.BASE64Decoder.decodeBuffer(String)}. + * + * @param s A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException If the input is not valid Base64 encoded data. + */ + public static byte[] decodeLines(String s) { + char[] buf = new char[s.length()]; + int p = 0; + for (int ip = 0; ip < s.length(); ip++) { + char c = s.charAt(ip); + if (c != ' ' && c != '\r' && c != '\n' && c != '\t') + buf[p++] = c; + } + return decode(buf, 0, p); + } - /** - * Decodes a byte array from Base64 format. No blanks or line breaks are - * allowed within the Base64 encoded input data. - * - * @param s - * A Base64 String to be decoded. - * @return An array containing the decoded data bytes. - * @throws IllegalArgumentException - * If the input is not valid Base64 encoded data. - */ - public static byte[] decode(String s) { - return decode(s.toCharArray()); - } + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param s A Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException If the input is not valid Base64 encoded data. + */ + public static byte[] decode(String s) { + return decode(s.toCharArray()); + } - /** - * Decodes a byte array from Base64 format. No blanks or line breaks are - * allowed within the Base64 encoded input data. - * - * @param in - * A character array containing the Base64 encoded data. - * @return An array containing the decoded data bytes. - * @throws IllegalArgumentException - * If the input is not valid Base64 encoded data. - */ - public static byte[] decode(char[] in) { - return decode(in, 0, in.length); - } + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in A character array containing the Base64 encoded data. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in) { + return decode(in, 0, in.length); + } - /** - * Decodes a byte array from Base64 format. No blanks or line breaks are - * allowed within the Base64 encoded input data. - * - * @param in - * A character array containing the Base64 encoded data. - * @param iOff - * Offset of the first character in {@code in} to be - * processed. - * @param iLen - * Number of characters to process in {@code in}, starting - * at {@code iOff}. - * @return An array containing the decoded data bytes. - * @throws IllegalArgumentException - * If the input is not valid Base64 encoded data. - */ - public static byte[] decode(char[] in, int iOff, int iLen) { - if (iLen % 4 != 0) - throw new IllegalArgumentException( - "Length of Base64 encoded input string is not a multiple of 4."); - while (iLen > 0 && in[iOff + iLen - 1] == '=') - iLen--; - int oLen = (iLen * 3) / 4; - byte[] out = new byte[oLen]; - int ip = iOff; - int iEnd = iOff + iLen; - int op = 0; - while (ip < iEnd) { - int i0 = in[ip++]; - int i1 = in[ip++]; - int i2 = ip < iEnd ? in[ip++] : 'A'; - int i3 = ip < iEnd ? in[ip++] : 'A'; - if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) - throw new IllegalArgumentException( - "Illegal character in Base64 encoded data."); - int b0 = map2[i0]; - int b1 = map2[i1]; - int b2 = map2[i2]; - int b3 = map2[i3]; - if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) - throw new IllegalArgumentException( - "Illegal character in Base64 encoded data."); - int o0 = (b0 << 2) | (b1 >>> 4); - int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); - int o2 = ((b2 & 3) << 6) | b3; - out[op++] = (byte) o0; - if (op < oLen) - out[op++] = (byte) o1; - if (op < oLen) - out[op++] = (byte) o2; - } - return out; - } + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded input data. + * + * @param in A character array containing the Base64 encoded data. + * @param iOff Offset of the first character in {@code in} to be + * processed. + * @param iLen Number of characters to process in {@code in}, starting + * at {@code iOff}. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException If the input is not valid Base64 encoded data. + */ + public static byte[] decode(char[] in, int iOff, int iLen) { + if (iLen % 4 != 0) + throw new IllegalArgumentException( + "Length of Base64 encoded input string is not a multiple of 4."); + while (iLen > 0 && in[iOff + iLen - 1] == '=') + iLen--; + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = iOff; + int iEnd = iOff + iLen; + int op = 0; + while (ip < iEnd) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iEnd ? in[ip++] : 'A'; + int i3 = ip < iEnd ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) + out[op++] = (byte) o1; + if (op < oLen) + out[op++] = (byte) o2; + } + return out; + } - // Dummy constructor. - private Base64Coder() { - } + // Dummy constructor. + private Base64Coder() { + } - public static String encodeURLSafeString(byte[] digest) { - char[] encode = encode(digest); - for (int i = 0; i < encode.length; i++) { - if (encode[i] == '+') { - encode[i] = '-'; - } else if (encode[i] == '/') { - encode[i] = '_'; - } - } - return new String(encode); - } + public static String encodeURLSafeString(byte[] digest) { + char[] encode = encode(digest); + for (int i = 0; i < encode.length; i++) { + if (encode[i] == '+') { + encode[i] = '-'; + } else if (encode[i] == '/') { + encode[i] = '_'; + } + } + return new String(encode); + } } // end class Base64Coder \ No newline at end of file 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 e628e2fb..2be36583 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java @@ -1,183 +1,188 @@ package com.cloudinary.utils; -/** -* HtmlEscape in Java, which is compatible with utf-8 -* @author Ulrich Jensen, http://www.htmlescape.net -* Feel free to get inspired, use or steal this code and use it in your -* own projects. -* License: -* You have the right to use this code in your own project or publish it -* on your own website. -* If you are going to use this code, please include the author lines. -* Use this code at your own risk. The author does not warrent or assume any -* legal liability or responsibility for the accuracy, completeness or usefullness of -* this program code. -*/ +/** + * HtmlEscape in Java, which is compatible with utf-8 + * + * @author Ulrich Jensen, http://www.htmlescape.net + * Feel free to get inspired, use or steal this code and use it in your + * own projects. + * License: + * You have the right to use this code in your own project or publish it + * on your own website. + * If you are going to use this code, please include the author lines. + * Use this code at your own risk. The author does not warrent or assume any + * legal liability or responsibility for the accuracy, completeness or usefullness of + * this program code. + */ -public class HtmlEscape { +public class HtmlEscape { - private static char[] hex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + private static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - /** - * Method for html escaping a String, for use in a textarea - * @param original The String to escape - * @return The escaped String - */ - public static String escapeTextArea(String original) - { - return escapeTags(escapeSpecial(original)); - } - - /** - * Normal escape function, for Html escaping Strings - * @param original The original String - * @return The escape String - */ - public static String escape(String original) - { - return escapeBr(escapeTags(escapeSpecial(original))); - } - - public static String escapeTags(String original) - { - if(original==null) return ""; - StringBuffer out=new StringBuffer(""); - char[] chars=original.toCharArray(); - for(int i=0;i - case 34:out.append("""); break; //" - default:found=false;break; - } - if(!found) out.append(chars[i]); - - } - return out.toString(); - - } + /** + * Method for html escaping a String, for use in a textarea + * + * @param original The String to escape + * @return The escaped String + */ + public static String escapeTextArea(String original) { + return escapeTags(escapeSpecial(original)); + } + + /** + * Normal escape function, for Html escaping Strings + * + * @param original The original String + * @return The escape String + */ + public static String escape(String original) { + return escapeBr(escapeTags(escapeSpecial(original))); + } + + public static String escapeTags(String original) { + if (original == null) return ""; + StringBuffer out = new StringBuffer(""); + char[] chars = original.toCharArray(); + for (int i = 0; i < chars.length; i++) { + boolean found = true; + switch (chars[i]) { + case 60: + out.append("<"); + break; //< + case 62: + out.append(">"); + break; //> + case 34: + out.append("""); + break; //" + default: + found = false; + break; + } + if (!found) out.append(chars[i]); + + } + return out.toString(); + + } + + public static String escapeBr(String original) { + if (original == null) return ""; + StringBuffer out = new StringBuffer(""); + char[] chars = original.toCharArray(); + for (int i = 0; i < chars.length; i++) { + boolean found = true; + switch (chars[i]) { + case '\n': + out.append("
"); + break; //newline + case '\r': + break; + default: + found = false; + break; + } + if (!found) out.append(chars[i]); + + } + return out.toString(); + } + + public static String escapeSpecial(String original) { + if (original == null) return ""; + StringBuffer out = new StringBuffer(""); + char[] chars = original.toCharArray(); + for (int i = 0; i < chars.length; i++) { + boolean found = true; + switch(chars[i]) { + // @formatter:off + case 38:out.append("&"); break; //& + case 198:out.append("Æ"); break; //Æ + case 193:out.append("Á"); break; //Á + case 194:out.append("Â"); break; //Â + case 192:out.append("À"); break; //À + case 197:out.append("Å"); break; //Å + case 195:out.append("Ã"); break; //Ã + case 196:out.append("Ä"); break; //Ä + case 199:out.append("Ç"); break; //Ç + case 208:out.append("Ð"); break; //Ð + case 201:out.append("É"); break; //É + case 202:out.append("Ê"); break; //Ê + case 200:out.append("È"); break; //È + case 203:out.append("Ë"); break; //Ë + case 205:out.append("Í"); break; //Í + case 206:out.append("Î"); break; //Î + case 204:out.append("Ì"); break; //Ì + case 207:out.append("Ï"); break; //Ï + case 209:out.append("Ñ"); break; //Ñ + case 211:out.append("Ó"); break; //Ó + case 212:out.append("Ô"); break; //Ô + case 210:out.append("Ò"); break; //Ò + case 216:out.append("Ø"); break; //Ø + case 213:out.append("Õ"); break; //Õ + case 214:out.append("Ö"); break; //Ö + case 222:out.append("Þ"); break; //Þ + case 218:out.append("Ú"); break; //Ú + case 219:out.append("Û"); break; //Û + case 217:out.append("Ù"); break; //Ù + case 220:out.append("Ü"); break; //Ü + case 221:out.append("Ý"); break; //Ý + case 225:out.append("á"); break; //á + case 226:out.append("â"); break; //â + case 230:out.append("æ"); break; //æ + case 224:out.append("à"); break; //à + case 229:out.append("å"); break; //å + case 227:out.append("ã"); break; //ã + case 228:out.append("ä"); break; //ä + case 231:out.append("ç"); break; //ç + case 233:out.append("é"); break; //é + case 234:out.append("ê"); break; //ê + case 232:out.append("è"); break; //è + case 240:out.append("ð"); break; //ð + case 235:out.append("ë"); break; //ë + case 237:out.append("í"); break; //í + case 238:out.append("î"); break; //î + case 236:out.append("ì"); break; //ì + case 239:out.append("ï"); break; //ï + case 241:out.append("ñ"); break; //ñ + case 243:out.append("ó"); break; //ó + case 244:out.append("ô"); break; //ô + case 242:out.append("ò"); break; //ò + case 248:out.append("ø"); break; //ø + case 245:out.append("õ"); break; //õ + case 246:out.append("ö"); break; //ö + case 223:out.append("ß"); break; //ß + case 254:out.append("þ"); break; //þ + case 250:out.append("ú"); break; //ú + case 251:out.append("û"); break; //û + case 249:out.append("ù"); break; //ù + case 252:out.append("ü"); break; //ü + case 253:out.append("ý"); break; //ý + case 255:out.append("ÿ"); break; //ÿ + case 162:out.append("¢"); break; //¢ + // @formatter:on + default: + found=false; + break; + } + if (!found) { + if (chars[i] > 127) { + char c = chars[i]; + int a4 = c % 16; + c = (char) (c / 16); + int a3 = c % 16; + c = (char) (c / 16); + int a2 = c % 16; + c = (char) (c / 16); + int a1 = c % 16; + out.append("&#x" + hex[a1] + hex[a2] + hex[a3] + hex[a4] + ";"); + } else { + out.append(chars[i]); + } + } + } + return out.toString(); + } - public static String escapeBr(String original) - { - if(original==null) return ""; - StringBuffer out=new StringBuffer(""); - char[] chars=original.toCharArray(); - for(int i=0;i"); break; //newline - case '\r': break; - default:found=false;break; - } - if(!found) out.append(chars[i]); - - } - return out.toString(); - } - - public static String escapeSpecial(String original) - { - if(original==null) return ""; - StringBuffer out=new StringBuffer(""); - char[] chars=original.toCharArray(); - for(int i=0;i127) { - char c=chars[i]; - int a4=c%16; - c=(char) (c/16); - int a3=c%16; - c=(char) (c/16); - int a2=c%16; - c=(char) (c/16); - int a1=c%16; - out.append("&#x"+hex[a1]+hex[a2]+hex[a3]+hex[a4]+";"); - } - else - { - out.append(chars[i]); - } - } - } - return out.toString(); - } - } \ No newline at end of file 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 515a3620..8cf941f4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -16,146 +16,146 @@ public class ObjectUtils { - public static String asString(Object value) { - if (value == null) { - return null; - } else { - return value.toString(); - } - } - - public static String asString(Object value, String defaultValue) { - if (value == null) { - return defaultValue; - } else { - return value.toString(); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List asArray(Object value) { - if (value == null) { - return Collections.EMPTY_LIST; - } else if (value instanceof int[]) { - List array = new ArrayList(); - for (int i : (int[]) value) { - array.add(new Integer(i)); - } - return array; - } else if (value instanceof Object[]) { - return Arrays.asList((Object[]) value); - } else if (value instanceof List) { - return (List) value; - } else { - List array = new ArrayList(); - array.add(value); - return array; - } - } - - public static Boolean asBoolean(Object value, Boolean defaultValue) { - if (value == null) { - return defaultValue; - } else if (value instanceof Boolean) { - return (Boolean) value; - } else { - return "true".equals(value); - } - } - - public static Float asFloat(Object value) { - if (value == null) { - return null; - } else if (value instanceof Float) { - return (Float) value; - } else { - return Float.parseFloat(value.toString()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Map asMap(Object... values) { - if (values.length % 2 != 0) - throw new RuntimeException("Usage - (key, value, key, value, ...)"); - Map result = new HashMap(values.length / 2); - for (int i = 0; i < values.length; i += 2) { - result.put(values[i], values[i + 1]); - } - return result; - } - - @SuppressWarnings("rawtypes") - public static Map emptyMap() { - return Collections.EMPTY_MAP; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static String encodeMap(Object arg) { - if (arg != null && arg instanceof Map) { - Map mapArg = (Map) arg; - HashSet out = new HashSet(); - for (Map.Entry entry : mapArg.entrySet()) { - out.add(entry.getKey() + "=" + entry.getValue()); - } - return StringUtils.join(out.toArray(), "|"); - } else if (arg == null) { - return null; - } else { - return arg.toString(); - } - } - - public static Map only(Map hash, String... keys) { - Map result = new HashMap(); - for (String key : keys) { - if (hash.containsKey(key)) { - result.put(key, hash.get(key)); - } - } - return result; - } - - @SuppressWarnings("rawtypes") - public static Map toMap(JSONObject object) throws JSONException { - @SuppressWarnings("unchecked") - Map map = new HashMap(); - Iterator keys = object.keys(); - while (keys.hasNext()) { - String key = (String) keys.next(); - map.put(key, fromJson(object.get(key))); - } - return map; - } - - private static Object fromJson(Object json) throws JSONException { - if (json == JSONObject.NULL) { - return null; - } else if (json instanceof JSONObject) { - return toMap((JSONObject) json); - } else if (json instanceof JSONArray) { - return toList((JSONArray) json); - } else { - return json; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static List toList(JSONArray array) throws JSONException { - List list = new ArrayList(); - for (int i = 0; i < array.length(); i++) { - list.add(fromJson(array.get(i))); - } - return list; - } - - public static Integer asInteger(Object value, Integer defaultValue) { - if (value == null) { - return defaultValue; - } else if (value instanceof Integer) { - return (Integer) value; - } else { - return Integer.parseInt(value.toString()); - } - } + public static String asString(Object value) { + if (value == null) { + return null; + } else { + return value.toString(); + } + } + + public static String asString(Object value, String defaultValue) { + if (value == null) { + return defaultValue; + } else { + return value.toString(); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static List asArray(Object value) { + if (value == null) { + return Collections.EMPTY_LIST; + } else if (value instanceof int[]) { + List array = new ArrayList(); + for (int i : (int[]) value) { + array.add(new Integer(i)); + } + return array; + } else if (value instanceof Object[]) { + return Arrays.asList((Object[]) value); + } else if (value instanceof List) { + return (List) value; + } else { + List array = new ArrayList(); + array.add(value); + return array; + } + } + + public static Boolean asBoolean(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Boolean) { + return (Boolean) value; + } else { + return "true".equals(value); + } + } + + public static Float asFloat(Object value) { + if (value == null) { + return null; + } else if (value instanceof Float) { + return (Float) value; + } else { + return Float.parseFloat(value.toString()); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Map asMap(Object... values) { + if (values.length % 2 != 0) + throw new RuntimeException("Usage - (key, value, key, value, ...)"); + Map result = new HashMap(values.length / 2); + for (int i = 0; i < values.length; i += 2) { + result.put(values[i], values[i + 1]); + } + return result; + } + + @SuppressWarnings("rawtypes") + public static Map emptyMap() { + return Collections.EMPTY_MAP; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static String encodeMap(Object arg) { + if (arg != null && arg instanceof Map) { + Map mapArg = (Map) arg; + HashSet out = new HashSet(); + for (Map.Entry entry : mapArg.entrySet()) { + out.add(entry.getKey() + "=" + entry.getValue()); + } + return StringUtils.join(out.toArray(), "|"); + } else if (arg == null) { + return null; + } else { + return arg.toString(); + } + } + + public static Map only(Map hash, String... keys) { + Map result = new HashMap(); + for (String key : keys) { + if (hash.containsKey(key)) { + result.put(key, hash.get(key)); + } + } + return result; + } + + @SuppressWarnings("rawtypes") + public static Map toMap(JSONObject object) throws JSONException { + @SuppressWarnings("unchecked") + Map map = new HashMap(); + Iterator keys = object.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + map.put(key, fromJson(object.get(key))); + } + return map; + } + + private static Object fromJson(Object json) throws JSONException { + if (json == JSONObject.NULL) { + return null; + } else if (json instanceof JSONObject) { + return toMap((JSONObject) json); + } else if (json instanceof JSONArray) { + return toList((JSONArray) json); + } else { + return json; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static List toList(JSONArray array) throws JSONException { + List list = new ArrayList(); + for (int i = 0; i < array.length(); i++) { + list.add(fromJson(array.get(i))); + } + return list; + } + + public static Integer asInteger(Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Integer) { + return (Integer) value; + } else { + return Integer.parseInt(value.toString()); + } + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java index 1f88bf48..6c44ab51 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java @@ -2,16 +2,16 @@ public class Rectangle { - public int height; - public int width; - public int y; - public int x; + public int height; + public int width; + public int y; + public int x; - public Rectangle(int x, int y, int width, int height) { - this.x = x; - this.y = y; - this.width= width; - this.height= height; - } + public Rectangle(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } } 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 751820ce..3a63f8e5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -7,109 +7,110 @@ import java.util.List; public class StringUtils { - public static final String EMPTY = ""; - - public static String join(List list, String separator) { - if (list == null) { - return null; - } - - return join(list.toArray(), separator, 0, list.size()); - } - - public static String join(Object[] array, String separator) { - if (array == null) { - return null; - } - return join(array, separator, 0, array.length); - } - - public static String join(Collection collection, String separator) { - if (collection == null) { - return null; - } - - return join(collection.toArray(new String[collection.size()]), separator, 0, collection.size()); - } - - public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { - if (array == null) { - return null; - } - if (separator == null) { - separator = EMPTY; - } - - final int noOfItems = endIndex - startIndex; - if (noOfItems <= 0) { - return EMPTY; - } - - final StringBuilder buf = new StringBuilder(noOfItems * 16); - - for (int i = startIndex; i < endIndex; i++) { - if (i > startIndex) { - buf.append(separator); - } - if (array[i] != null) { - buf.append(array[i]); - } - } - return buf.toString(); - } - - final protected static char[] hexArray = "0123456789abcdef".toCharArray(); - - public static String encodeHexString(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - public static String escapeHtml(String input) { - return HtmlEscape.escapeTextArea(input); - } - - public static boolean isNotBlank(Object input) { - if (input==null) return false; - return !isBlank(input.toString()); - } - public static boolean isNotBlank(String input) { - return !isBlank(input); - } - - public static boolean isEmpty(String input){ - if (input == null || input.length()== 0) { - return true; - } - return false; - } - - public static boolean isBlank(String input) { - int strLen; - if (input == null || (strLen = input.length()) == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(input.charAt(i)) == false) { - return false; - } - } - return true; - } - - public static String read(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length = 0; - while ((length = in.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - return new String(baos.toByteArray()); - } + public static final String EMPTY = ""; + + public static String join(List list, String separator) { + if (list == null) { + return null; + } + + return join(list.toArray(), separator, 0, list.size()); + } + + public static String join(Object[] array, String separator) { + if (array == null) { + return null; + } + return join(array, separator, 0, array.length); + } + + public static String join(Collection collection, String separator) { + if (collection == null) { + return null; + } + + return join(collection.toArray(new String[collection.size()]), separator, 0, collection.size()); + } + + public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { + if (array == null) { + return null; + } + if (separator == null) { + separator = EMPTY; + } + + final int noOfItems = endIndex - startIndex; + if (noOfItems <= 0) { + return EMPTY; + } + + final StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + final protected static char[] hexArray = "0123456789abcdef".toCharArray(); + + public static String encodeHexString(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + public static String escapeHtml(String input) { + return HtmlEscape.escapeTextArea(input); + } + + public static boolean isNotBlank(Object input) { + if (input == null) return false; + return !isBlank(input.toString()); + } + + public static boolean isNotBlank(String input) { + return !isBlank(input); + } + + public static boolean isEmpty(String input) { + if (input == null || input.length() == 0) { + return true; + } + return false; + } + + public static boolean isBlank(String input) { + int strLen; + if (input == null || (strLen = input.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (Character.isWhitespace(input.charAt(i)) == false) { + return false; + } + } + return true; + } + + public static String read(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = in.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return new String(baos.toByteArray()); + } } diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java index 504e4117..d0d6090a 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java @@ -73,901 +73,822 @@ of this software and associated documentation files (the "Software"), to deal * they are not the reserved words true, false, or * null. * - * + * * @author JSON.org * @version 2014-05-03 */ public class JSONArray { - /** - * The arrayList where the JSONArray's properties are kept. - */ - private final ArrayList myArrayList; - - /** - * Construct an empty JSONArray. - */ - public JSONArray() { - this.myArrayList = new ArrayList(); - } - - /** - * Construct a JSONArray from a JSONTokener. - * - * @param x - * A JSONTokener - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(JSONTokener x) throws JSONException { - this(); - if (x.nextClean() != '[') { - throw x.syntaxError("A JSONArray text must start with '['"); - } - if (x.nextClean() != ']') { - x.back(); - for (;;) { - if (x.nextClean() == ',') { - x.back(); - this.myArrayList.add(JSONObject.NULL); - } else { - x.back(); - this.myArrayList.add(x.nextValue()); - } - switch (x.nextClean()) { - case ',': - if (x.nextClean() == ']') { - return; - } - x.back(); - break; - case ']': - return; - default: - throw x.syntaxError("Expected a ',' or ']'"); - } - } - } - } - - /** - * Construct a JSONArray from a source JSON text. - * - * @param source - * A string that begins with [ (left - * bracket) and ends with ] - *  (right bracket). - * @throws JSONException - * If there is a syntax error. - */ - public JSONArray(String source) throws JSONException { - this(new JSONTokener(source)); - } - - /** - * Construct a JSONArray from a Collection. - * - * @param collection - * A Collection. - */ - public JSONArray(Collection collection) { - this.myArrayList = new ArrayList(); - if (collection != null) { - Iterator iter = collection.iterator(); - while (iter.hasNext()) { - this.myArrayList.add(JSONObject.wrap(iter.next())); - } - } - } - - /** - * Construct a JSONArray from an array - * - * @throws JSONException - * If not an array. - */ - public JSONArray(Object array) throws JSONException { - this(); - if (array.getClass().isArray()) { - int length = Array.getLength(array); - for (int i = 0; i < length; i += 1) { - this.put(JSONObject.wrap(Array.get(array, i))); - } - } else { - throw new JSONException("JSONArray initial value should be a string or collection or array."); - } - } - - /** - * Get the object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object value. - * @throws JSONException - * If there is no value for the index. - */ - public Object get(int index) throws JSONException { - Object object = this.opt(index); - if (object == null) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - return object; - } - - /** - * Get the boolean value associated with an index. The string values "true" - * and "false" are converted to boolean. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - * @throws JSONException - * If there is no value for the index or if the value is not - * convertible to boolean. - */ - public boolean getBoolean(int index) throws JSONException { - Object object = this.get(index); - if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) { - return false; - } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) { - return true; - } - throw new JSONException("JSONArray[" + index + "] is not a boolean."); - } - - /** - * Get the double value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public double getDouble(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).doubleValue() : Double.parseDouble((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the int value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value is not a number. - */ - public int getInt(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).intValue() : Integer.parseInt((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the JSONArray associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONArray value. - * @throws JSONException - * If there is no value for the index. or if the value is not a - * JSONArray - */ - public JSONArray getJSONArray(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONArray) { - return (JSONArray) object; - } - throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); - } - - /** - * Get the JSONObject associated with an index. - * - * @param index - * subscript - * @return A JSONObject value. - * @throws JSONException - * If there is no value for the index or if the value is not a - * JSONObject - */ - public JSONObject getJSONObject(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof JSONObject) { - return (JSONObject) object; - } - throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); - } - - /** - * Get the long value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. - */ - public long getLong(int index) throws JSONException { - Object object = this.get(index); - try { - return object instanceof Number ? ((Number) object).longValue() : Long.parseLong((String) object); - } catch (Exception e) { - throw new JSONException("JSONArray[" + index + "] is not a number."); - } - } - - /** - * Get the string associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A string value. - * @throws JSONException - * If there is no string value for the index. - */ - public String getString(int index) throws JSONException { - Object object = this.get(index); - if (object instanceof String) { - return (String) object; - } - throw new JSONException("JSONArray[" + index + "] not a string."); - } - - /** - * Determine if the value is null. - * - * @param index - * The index must be between 0 and length() - 1. - * @return true if the value at the index is null, or if there is no value. - */ - public boolean isNull(int index) { - return JSONObject.NULL.equals(this.opt(index)); - } - - /** - * Make a string from the contents of this JSONArray. The - * separator string is inserted between each element. Warning: - * This method assumes that the data structure is acyclical. - * - * @param separator - * A string that will be inserted between the elements. - * @return a string. - * @throws JSONException - * If the array contains an invalid number. - */ - public String join(String separator) throws JSONException { - int len = this.length(); - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < len; i += 1) { - if (i > 0) { - sb.append(separator); - } - sb.append(JSONObject.valueToString(this.myArrayList.get(i))); - } - return sb.toString(); - } - - /** - * Get the number of elements in the JSONArray, included nulls. - * - * @return The length (or size). - */ - public int length() { - return this.myArrayList.size(); - } - - /** - * Get the optional object value associated with an index. - * - * @param index - * The index must be between 0 and length() - 1. - * @return An object value, or null if there is no object at that index. - */ - public Object opt(int index) { - return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index); - } - - /** - * Get the optional boolean value associated with an index. It returns false - * if there is no value at that index, or if the value is not Boolean.TRUE - * or the String "true". - * - * @param index - * The index must be between 0 and length() - 1. - * @return The truth. - */ - public boolean optBoolean(int index) { - return this.optBoolean(index, false); - } - - /** - * Get the optional boolean value associated with an index. It returns the - * defaultValue if there is no value at that index or if it is not a Boolean - * or the String "true" or "false" (case insensitive). - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * A boolean default. - * @return The truth. - */ - public boolean optBoolean(int index, boolean defaultValue) { - try { - return this.getBoolean(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional double value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public double optDouble(int index) { - return this.optDouble(index, Double.NaN); - } - - /** - * Get the optional double value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * subscript - * @param defaultValue - * The default value. - * @return The value. - */ - public double optDouble(int index, double defaultValue) { - try { - return this.getDouble(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional int value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public int optInt(int index) { - return this.optInt(index, 0); - } - - /** - * Get the optional int value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public int optInt(int index, int defaultValue) { - try { - return this.getInt(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional JSONArray associated with an index. - * - * @param index - * subscript - * @return A JSONArray value, or null if the index has no value, or if the - * value is not a JSONArray. - */ - public JSONArray optJSONArray(int index) { - Object o = this.opt(index); - return o instanceof JSONArray ? (JSONArray) o : null; - } - - /** - * Get the optional JSONObject associated with an index. Null is returned if - * the key is not found, or null if the index has no value, or if the value - * is not a JSONObject. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A JSONObject value. - */ - public JSONObject optJSONObject(int index) { - Object o = this.opt(index); - return o instanceof JSONObject ? (JSONObject) o : null; - } - - /** - * Get the optional long value associated with an index. Zero is returned if - * there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @return The value. - */ - public long optLong(int index) { - return this.optLong(index, 0); - } - - /** - * Get the optional long value associated with an index. The defaultValue is - * returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return The value. - */ - public long optLong(int index, long defaultValue) { - try { - return this.getLong(index); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get the optional string value associated with an index. It returns an - * empty string if there is no value at that index. If the value is not a - * string and is not null, then it is coverted to a string. - * - * @param index - * The index must be between 0 and length() - 1. - * @return A String value. - */ - public String optString(int index) { - return this.optString(index, ""); - } - - /** - * Get the optional string associated with an index. The defaultValue is - * returned if the key is not found. - * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. - * @return A String value. - */ - public String optString(int index, String defaultValue) { - Object object = this.opt(index); - return JSONObject.NULL.equals(object) ? defaultValue : object.toString(); - } - - /** - * Append a boolean value. This increases the array's length by one. - * - * @param value - * A boolean value. - * @return this. - */ - public JSONArray put(boolean value) { - this.put(value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param value - * A Collection value. - * @return this. - */ - public JSONArray put(Collection value) { - this.put(new JSONArray(value)); - return this; - } - - /** - * Append a double value. This increases the array's length by one. - * - * @param value - * A double value. - * @throws JSONException - * if the value is not finite. - * @return this. - */ - public JSONArray put(double value) throws JSONException { - Double d = new Double(value); - JSONObject.testValidity(d); - this.put(d); - return this; - } - - /** - * Append an int value. This increases the array's length by one. - * - * @param value - * An int value. - * @return this. - */ - public JSONArray put(int value) { - this.put(new Integer(value)); - return this; - } - - /** - * Append an long value. This increases the array's length by one. - * - * @param value - * A long value. - * @return this. - */ - public JSONArray put(long value) { - this.put(new Long(value)); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject which - * is produced from a Map. - * - * @param value - * A Map value. - * @return this. - */ - public JSONArray put(Map value) { - this.put(new JSONObject(value)); - return this; - } - - /** - * Append an object value. This increases the array's length by one. - * - * @param value - * An object value. The value should be a Boolean, Double, - * Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. - * @return this. - */ - public JSONArray put(Object value) { - this.myArrayList.add(value); - return this; - } - - /** - * Put or replace a boolean value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * A boolean value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, boolean value) throws JSONException { - this.put(index, value ? Boolean.TRUE : Boolean.FALSE); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONArray which - * is produced from a Collection. - * - * @param index - * The subscript. - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is not finite. - */ - public JSONArray put(int index, Collection value) throws JSONException { - this.put(index, new JSONArray(value)); - return this; - } - - /** - * Put or replace a double value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A double value. - * @return this. - * @throws JSONException - * If the index is negative or if the value is not finite. - */ - public JSONArray put(int index, double value) throws JSONException { - this.put(index, new Double(value)); - return this; - } - - /** - * Put or replace an int value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * An int value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, int value) throws JSONException { - this.put(index, new Integer(value)); - return this; - } - - /** - * Put or replace a long value. If the index is greater than the length of - * the JSONArray, then null elements will be added as necessary to pad it - * out. - * - * @param index - * The subscript. - * @param value - * A long value. - * @return this. - * @throws JSONException - * If the index is negative. - */ - public JSONArray put(int index, long value) throws JSONException { - this.put(index, new Long(value)); - return this; - } - - /** - * Put a value in the JSONArray, where the value will be a JSONObject that - * is produced from a Map. - * - * @param index - * The subscript. - * @param value - * The Map value. - * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. - */ - public JSONArray put(int index, Map value) throws JSONException { - this.put(index, new JSONObject(value)); - return this; - } - - /** - * Put or replace an object value in the JSONArray. If the index is greater - * than the length of the JSONArray, then null elements will be added as - * necessary to pad it out. - * - * @param index - * The subscript. - * @param value - * The value to put into the array. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. - */ - public JSONArray put(int index, Object value) throws JSONException { - JSONObject.testValidity(value); - if (index < 0) { - throw new JSONException("JSONArray[" + index + "] not found."); - } - if (index < this.length()) { - this.myArrayList.set(index, value); - } else { - while (index != this.length()) { - this.put(JSONObject.NULL); - } - this.put(value); - } - return this; - } - - /** - * Remove an index and close the hole. - * - * @param index - * The index of the element to be removed. - * @return The value that was associated with the index, or null if there - * was no value. - */ - public Object remove(int index) { - return index >= 0 && index < this.length() ? this.myArrayList.remove(index) : null; - } - - /** - * Determine if two JSONArrays are similar. They must contain similar - * sequences. - * - * @param other - * The other JSONArray - * @return true if they are equal - */ - public boolean similar(Object other) { - if (!(other instanceof JSONArray)) { - return false; - } - int len = this.length(); - if (len != ((JSONArray) other).length()) { - return false; - } - for (int i = 0; i < len; i += 1) { - Object valueThis = this.get(i); - Object valueOther = ((JSONArray) other).get(i); - if (valueThis instanceof JSONObject) { - if (!((JSONObject) valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof JSONArray) { - if (!((JSONArray) valueThis).similar(valueOther)) { - return false; - } - } else if (!valueThis.equals(valueOther)) { - return false; - } - } - return true; - } - - /** - * Produce a JSONObject by combining a JSONArray of names with the values of - * this JSONArray. - * - * @param names - * A JSONArray containing a list of key strings. These will be - * paired with the values. - * @return A JSONObject, or null if there are no names or if this JSONArray - * has no values. - * @throws JSONException - * If any of the names are null. - */ - public JSONObject toJSONObject(JSONArray names) throws JSONException { - if (names == null || names.length() == 0 || this.length() == 0) { - return null; - } - JSONObject jo = new JSONObject(); - for (int i = 0; i < names.length(); i += 1) { - jo.put(names.getString(i), this.opt(i)); - } - return jo; - } - - /** - * Make a JSON text of this JSONArray. For compactness, no unnecessary - * whitespace is added. If it is not possible to produce a syntactically - * correct JSON text then null will be returned instead. This could occur if - * the array contains an invalid number. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return a printable, displayable, transmittable representation of the - * array. - */ - public String toString() { - try { - return this.toString(0); - } catch (Exception e) { - return null; - } - } - - /** - * Make a prettyprinted JSON text of this JSONArray. Warning: This method - * assumes that the data structure is acyclical. - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @return a printable, displayable, transmittable representation of the - * object, beginning with [ (left - * bracket) and ending with ] - *  (right bracket). - * @throws JSONException - */ - public String toString(int indentFactor) throws JSONException { - StringWriter sw = new StringWriter(); - synchronized (sw.getBuffer()) { - return this.write(sw, indentFactor, 0).toString(); - } - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @return The writer. - * @throws JSONException - */ - public Writer write(Writer writer) throws JSONException { - return this.write(writer, 0, 0); - } - - /** - * Write the contents of the JSONArray as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @param indent - * The indention of the top level. - * @return The writer. - * @throws JSONException - */ - Writer write(Writer writer, int indentFactor, int indent) throws JSONException { - try { - boolean commanate = false; - int length = this.length(); - writer.write('['); - - if (length == 1) { - JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent); - } else if (length != 0) { - final int newindent = indent + indentFactor; - - for (int i = 0; i < length; i += 1) { - if (commanate) { - writer.write(','); - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, newindent); - JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent); - commanate = true; - } - if (indentFactor > 0) { - writer.write('\n'); - } - JSONObject.indent(writer, indent); - } - writer.write(']'); - return writer; - } catch (IOException e) { - throw new JSONException(e); - } - } - - @SuppressWarnings("unchecked") - public ArrayList toList(Class type) { - ArrayList listdata = new ArrayList(); - for (int i = 0; i < this.length(); i++) { - listdata.add((T)this.get(i)); - } - return listdata; - } + /** + * The arrayList where the JSONArray's properties are kept. + */ + private final ArrayList myArrayList; + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * + * @param x A JSONTokener + * @throws JSONException If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() != ']') { + x.back(); + for (; ; ) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + } + + /** + * Construct a JSONArray from a source JSON text. + * + * @param source A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + /** + * Construct a JSONArray from a Collection. + * + * @param collection A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = new ArrayList(); + if (collection != null) { + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + this.myArrayList.add(JSONObject.wrap(iter.next())); + } + } + } + + /** + * Construct a JSONArray from an array + * + * @throws JSONException If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + } + + /** + * Get the object value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + /** + * Get the boolean value associated with an index. The string values "true" + * and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException If there is no value for the index or if the value is not + * convertible to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object).equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a boolean."); + } + + /** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a number. + */ + public double getDouble(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).doubleValue() : Double.parseDouble((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value is not a number. + */ + public int getInt(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).intValue() : Integer.parseInt((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the JSONArray associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException If there is no value for the index. or if the value is not a + * JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); + } + + /** + * Get the JSONObject associated with an index. + * + * @param index subscript + * @return A JSONObject value. + * @throws JSONException If there is no value for the index or if the value is not a + * JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); + } + + /** + * Get the long value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a number. + */ + public long getLong(int index) throws JSONException { + Object object = this.get(index); + try { + return object instanceof Number ? ((Number) object).longValue() : Long.parseLong((String) object); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + "] is not a number."); + } + } + + /** + * Get the string associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException If there is no string value for the index. + */ + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String) object; + } + throw new JSONException("JSONArray[" + index + "] not a string."); + } + + /** + * Determine if the value is null. + * + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. Warning: + * This method assumes that the data structure is acyclical. + * + * @param separator A string that will be inserted between the elements. + * @return a string. + * @throws JSONException If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = this.length(); + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + /** + * Get the optional object value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index); + } + + /** + * Get the optional boolean value associated with an index. It returns false + * if there is no value at that index, or if the value is not Boolean.TRUE + * or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + /** + * Get the optional boolean value associated with an index. It returns the + * defaultValue if there is no value at that index or if it is not a Boolean + * or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return this.getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional int value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return this.optInt(index, 0); + } + + /** + * Get the optional int value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return this.getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional JSONArray associated with an index. + * + * @param index subscript + * @return A JSONArray value, or null if the index has no value, or if the + * value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = this.opt(index); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get the optional JSONObject associated with an index. Null is returned if + * the key is not found, or null if the index has no value, or if the value + * is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = this.opt(index); + return o instanceof JSONObject ? (JSONObject) o : null; + } + + /** + * Get the optional long value associated with an index. Zero is returned if + * there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return this.optLong(index, 0); + } + + /** + * Get the optional long value associated with an index. The defaultValue is + * returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return this.getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value is not a + * string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return this.optString(index, ""); + } + + /** + * Get the optional string associated with an index. The defaultValue is + * returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object.toString(); + } + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + this.put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param value A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + this.put(new JSONArray(value)); + return this; + } + + /** + * Append a double value. This increases the array's length by one. + * + * @param value A double value. + * @return this. + * @throws JSONException if the value is not finite. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + this.put(d); + return this; + } + + /** + * Append an int value. This increases the array's length by one. + * + * @param value An int value. + * @return this. + */ + public JSONArray put(int value) { + this.put(new Integer(value)); + return this; + } + + /** + * Append an long value. This increases the array's length by one. + * + * @param value A long value. + * @return this. + */ + public JSONArray put(long value) { + this.put(new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject which + * is produced from a Map. + * + * @param value A Map value. + * @return this. + */ + public JSONArray put(Map value) { + this.put(new JSONObject(value)); + return this; + } + + /** + * Append an object value. This increases the array's length by one. + * + * @param value An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index The subscript. + * @param value A boolean value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONArray which + * is produced from a Collection. + * + * @param index The subscript. + * @param value A Collection value. + * @return this. + * @throws JSONException If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + this.put(index, new JSONArray(value)); + return this; + } + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index The subscript. + * @param value A double value. + * @return this. + * @throws JSONException If the index is negative or if the value is not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + this.put(index, new Double(value)); + return this; + } + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index The subscript. + * @param value An int value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + this.put(index, new Integer(value)); + return this; + } + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad it + * out. + * + * @param index The subscript. + * @param value A long value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + this.put(index, new Long(value)); + return this; + } + + /** + * Put a value in the JSONArray, where the value will be a JSONObject that + * is produced from a Map. + * + * @param index The subscript. + * @param value The Map value. + * @return this. + * @throws JSONException If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value)); + return this; + } + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * + * @param index The subscript. + * @param value The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the index is negative or if the the value is an invalid + * number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + this.myArrayList.set(index, value); + } else { + while (index != this.length()) { + this.put(JSONObject.NULL); + } + this.put(value); + } + return this; + } + + /** + * Remove an index and close the hole. + * + * @param index The index of the element to be removed. + * @return The value that was associated with the index, or null if there + * was no value. + */ + public Object remove(int index) { + return index >= 0 && index < this.length() ? this.myArrayList.remove(index) : null; + } + + /** + * Determine if two JSONArrays are similar. They must contain similar + * sequences. + * + * @param other The other JSONArray + * @return true if they are equal + */ + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray) other).length()) { + return false; + } + for (int i = 0; i < len; i += 1) { + Object valueThis = this.get(i); + Object valueOther = ((JSONArray) other).get(i); + if (valueThis instanceof JSONObject) { + if (!((JSONObject) valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray) valueThis).similar(valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } + + /** + * Produce a JSONObject by combining a JSONArray of names with the values of + * this JSONArray. + * + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || this.length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + /** + * Make a JSON text of this JSONArray. For compactness, no unnecessary + * whitespace is added. If it is not possible to produce a syntactically + * correct JSON text then null will be returned instead. This could occur if + * the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable representation of the + * array. + */ + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a prettyprinted JSON text of this JSONArray. Warning: This method + * assumes that the data structure is acyclical. + * + * @param indentFactor The number of spaces to add to each level of indentation. + * @return a printable, displayable, transmittable representation of the + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + StringWriter sw = new StringWriter(); + synchronized (sw.getBuffer()) { + return this.write(sw, indentFactor, 0).toString(); + } + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONArray as JSON text to a writer. For + * compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param indentFactor The number of spaces to add to each level of indentation. + * @param indent The indention of the top level. + * @return The writer. + * @throws JSONException + */ + Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + try { + boolean commanate = false; + int length = this.length(); + writer.write('['); + + if (length == 1) { + JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent); + } else if (length != 0) { + final int newindent = indent + indentFactor; + + for (int i = 0; i < length; i += 1) { + if (commanate) { + writer.write(','); + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, newindent); + JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent); + commanate = true; + } + if (indentFactor > 0) { + writer.write('\n'); + } + JSONObject.indent(writer, indent); + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } + + @SuppressWarnings("unchecked") + public ArrayList toList(Class type) { + ArrayList listdata = new ArrayList(); + for (int i = 0; i < this.length(); i++) { + listdata.add((T) this.get(i)); + } + return listdata; + } } diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java index 6eb1b6b4..5fe33487 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java @@ -13,8 +13,7 @@ public class JSONException extends RuntimeException { /** * Constructs a JSONException with an explanatory message. * - * @param message - * Detail about the reason for the exception. + * @param message Detail about the reason for the exception. */ public JSONException(String message) { super(message); @@ -22,6 +21,7 @@ public JSONException(String message) { /** * Constructs a new JSONException with the specified cause. + * * @param cause The cause. */ public JSONException(Throwable cause) { @@ -34,7 +34,7 @@ public JSONException(Throwable cause) { * or unknown. * * @return the cause of this exception or null if the cause is nonexistent - * or unknown. + * or unknown. */ @Override public Throwable getCause() { diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java index 3ad4154d..9ca1012b 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java @@ -66,12 +66,12 @@ of this software and associated documentation files (the "Software"), to deal *

* The put methods add or replace values in an object. For * example, - * + *

*

  * myString = new JSONObject()
  *         .put("JSON", "Hello, World!").toString();
  * 
- * + *

* produces the string {"JSON": "Hello, World"}. *

* The texts produced by the toString methods strictly conform to @@ -115,10 +115,9 @@ protected final Object clone() { /** * A Null object is equal to the null value and to itself. * - * @param object - * An object to test for nullness. + * @param object An object to test for nullness. * @return true if the object parameter is the JSONObject.NULL object or - * null. + * null. */ @Override public boolean equals(Object object) { @@ -160,14 +159,11 @@ public JSONObject() { * strings is used to identify the keys that should be copied. Missing keys * are ignored. * - * @param jo - * A JSONObject. - * @param names - * An array of strings. + * @param jo A JSONObject. + * @param names An array of strings. * @throws JSONException - * @exception JSONException - * If a value is a non-finite number or if a name is - * duplicated. + * @throws JSONException If a value is a non-finite number or if a name is + * duplicated. */ public JSONObject(JSONObject jo, String[] names) { this(); @@ -182,11 +178,9 @@ public JSONObject(JSONObject jo, String[] names) { /** * Construct a JSONObject from a JSONTokener. * - * @param x - * A JSONTokener object containing the source string. - * @throws JSONException - * If there is a syntax error in the source string or a - * duplicated key. + * @param x A JSONTokener object containing the source string. + * @throws JSONException If there is a syntax error in the source string or a + * duplicated key. */ public JSONObject(JSONTokener x) throws JSONException { this(); @@ -196,16 +190,16 @@ public JSONObject(JSONTokener x) throws JSONException { if (x.nextClean() != '{') { throw x.syntaxError("A JSONObject text must begin with '{'"); } - for (;;) { + for (; ; ) { c = x.nextClean(); switch (c) { - case 0: - throw x.syntaxError("A JSONObject text must end with '}'"); - case '}': - return; - default: - x.back(); - key = x.nextValue().toString(); + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); } // The key is followed by ':'. @@ -219,17 +213,17 @@ public JSONObject(JSONTokener x) throws JSONException { // Pairs are separated by ','. switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == '}') { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': return; - } - x.back(); - break; - case '}': - return; - default: - throw x.syntaxError("Expected a ',' or '}'"); + default: + throw x.syntaxError("Expected a ',' or '}'"); } } } @@ -237,8 +231,7 @@ public JSONObject(JSONTokener x) throws JSONException { /** * Construct a JSONObject from a Map. * - * @param map - * A map object that can be used to initialize the contents of + * @param map A map object that can be used to initialize the contents of * the JSONObject. * @throws JSONException */ @@ -263,19 +256,18 @@ public JSONObject(Map map) { * "is" followed by an uppercase letter, the method is invoked, * and a key and the value returned from the getter method are put into the * new JSONObject. - * + *

* The key is formed by removing the "get" or "is" * prefix. If the second remaining character is not upper case, then the * first character is converted to lower case. - * + *

* For example, if an object has a method named "getName", and * if the result of calling object.getName() is * "Larry Fine", then the JSONObject will contain * "name": "Larry Fine". * - * @param bean - * An object that has getter methods that should be used to make - * a JSONObject. + * @param bean An object that has getter methods that should be used to make + * a JSONObject. */ public JSONObject(Object bean) { this(); @@ -289,15 +281,13 @@ public JSONObject(Object bean) { * those keys in the object. If a key is not found or not visible, then it * will not be copied into the new JSONObject. * - * @param object - * An object that has fields that should be used to make a - * JSONObject. - * @param names - * An array of strings, the names of the fields to be obtained - * from the object. + * @param object An object that has fields that should be used to make a + * JSONObject. + * @param names An array of strings, the names of the fields to be obtained + * from the object. */ @SuppressWarnings("rawtypes") - public JSONObject(Object object, String names[]) { + public JSONObject(Object object, String names[]) { this(); Class c = object.getClass(); for (int i = 0; i < names.length; i += 1) { @@ -313,13 +303,11 @@ public JSONObject(Object object, String names[]) { * Construct a JSONObject from a source JSON text string. This is the most * commonly used JSONObject constructor. * - * @param source - * A string beginning with { (left - * brace) and ending with } - *  (right brace). - * @exception JSONException - * If there is a syntax error in the source string or a - * duplicated key. + * @param source A string beginning with { (left + * brace) and ending with } + *  (right brace). + * @throws JSONException If there is a syntax error in the source string or a + * duplicated key. */ public JSONObject(String source) throws JSONException { this(new JSONTokener(source)); @@ -328,12 +316,9 @@ public JSONObject(String source) throws JSONException { /** * Construct a JSONObject from a ResourceBundle. * - * @param baseName - * The ResourceBundle base name. - * @param locale - * The Locale to load the ResourceBundle for. - * @throws JSONException - * If any JSONExceptions are detected. + * @param baseName The ResourceBundle base name. + * @param locale The Locale to load the ResourceBundle for. + * @throws JSONException If any JSONExceptions are detected. */ public JSONObject(String baseName, Locale locale) throws JSONException { this(); @@ -374,18 +359,15 @@ public JSONObject(String baseName, Locale locale) throws JSONException { * is stored under the key to hold all of the accumulated values. If there * is already a JSONArray, then the new value is appended to it. In * contrast, the put method replaces the previous value. - * + *

* If only one value is accumulated that is not a JSONArray, then the result * will be the same as using put. But if multiple values are accumulated, * then the result will be like append. * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. + * @param key A key string. + * @param value An object to be accumulated under the key. * @return this. - * @throws JSONException - * If the value is an invalid number or if the key is null. + * @throws JSONException If the value is an invalid number or if the key is null. */ public JSONObject accumulate(String key, Object value) throws JSONException { testValidity(value); @@ -408,14 +390,11 @@ public JSONObject accumulate(String key, Object value) throws JSONException { * JSONArray containing the value parameter. If the key was already * associated with a JSONArray, then the value parameter is appended to it. * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. + * @param key A key string. + * @param value An object to be accumulated under the key. * @return this. - * @throws JSONException - * If the key is null or if the current value associated with - * the key is not a JSONArray. + * @throws JSONException If the key is null or if the current value associated with + * the key is not a JSONArray. */ public JSONObject append(String key, Object value) throws JSONException { testValidity(value); @@ -435,8 +414,7 @@ public JSONObject append(String key, Object value) throws JSONException { * Produce a string from a double. The string "null" will be returned if the * number is not finite. * - * @param d - * A double. + * @param d A double. * @return A String. */ public static String doubleToString(double d) { @@ -462,11 +440,9 @@ public static String doubleToString(double d) { /** * Get the value object associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return The object associated with the key. - * @throws JSONException - * if the key is not found. + * @throws JSONException if the key is not found. */ public Object get(String key) throws JSONException { if (key == null) { @@ -482,22 +458,20 @@ public Object get(String key) throws JSONException { /** * Get the boolean value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return The truth. - * @throws JSONException - * if the value is not a Boolean or the String "true" or - * "false". + * @throws JSONException if the value is not a Boolean or the String "true" or + * "false". */ public boolean getBoolean(String key) throws JSONException { Object object = this.get(key); if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { + .equalsIgnoreCase("false"))) { return false; } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { + .equalsIgnoreCase("true"))) { return true; } throw new JSONException("JSONObject[" + quote(key) @@ -507,12 +481,10 @@ public boolean getBoolean(String key) throws JSONException { /** * Get the double value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. */ public double getDouble(String key) throws JSONException { Object object = this.get(key); @@ -528,12 +500,10 @@ public double getDouble(String key) throws JSONException { /** * Get the int value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return The integer value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an integer. + * @throws JSONException if the key is not found or if the value cannot be converted + * to an integer. */ public int getInt(String key) throws JSONException { Object object = this.get(key); @@ -549,11 +519,9 @@ public int getInt(String key) throws JSONException { /** * Get the JSONArray value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return A JSONArray which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONArray. + * @throws JSONException if the key is not found or if the value is not a JSONArray. */ public JSONArray getJSONArray(String key) throws JSONException { Object object = this.get(key); @@ -567,11 +535,9 @@ public JSONArray getJSONArray(String key) throws JSONException { /** * Get the JSONObject value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return A JSONObject which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONObject. + * @throws JSONException if the key is not found or if the value is not a JSONObject. */ public JSONObject getJSONObject(String key) throws JSONException { Object object = this.get(key); @@ -585,12 +551,10 @@ public JSONObject getJSONObject(String key) throws JSONException { /** * Get the long value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return The long value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to a long. + * @throws JSONException if the key is not found or if the value cannot be converted + * to a long. */ public long getLong(String key) throws JSONException { Object object = this.get(key); @@ -629,7 +593,7 @@ public static String[] getNames(JSONObject jo) { * @return An array of field names, or null if there are no names. */ @SuppressWarnings("rawtypes") - public static String[] getNames(Object object) { + public static String[] getNames(Object object) { if (object == null) { return null; } @@ -649,11 +613,9 @@ public static String[] getNames(Object object) { /** * Get the string associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return A string which is the value. - * @throws JSONException - * if there is no string value for the key. + * @throws JSONException if there is no string value for the key. */ public String getString(String key) throws JSONException { Object object = this.get(key); @@ -666,8 +628,7 @@ public String getString(String key) throws JSONException { /** * Determine if the JSONObject contains a specific key. * - * @param key - * A key string. + * @param key A key string. * @return true if the key exists in the JSONObject. */ public boolean has(String key) { @@ -679,12 +640,10 @@ public boolean has(String key) { * create one with a value of 1. If there is such a property, and if it is * an Integer, Long, Double, or Float, then add one to it. * - * @param key - * A key string. + * @param key A key string. * @return this. - * @throws JSONException - * If there is already a property with this name that is not an - * Integer, Long, Double, or Float. + * @throws JSONException If there is already a property with this name that is not an + * Integer, Long, Double, or Float. */ public JSONObject increment(String key) throws JSONException { Object value = this.opt(key); @@ -708,10 +667,9 @@ public JSONObject increment(String key) throws JSONException { * Determine if the value associated with the key is null or if there is no * value. * - * @param key - * A key string. + * @param key A key string. * @return true if there is no value associated with the key or if the value - * is the JSONObject.NULL object. + * is the JSONObject.NULL object. */ public boolean isNull(String key) { return JSONObject.NULL.equals(this.opt(key)); @@ -749,7 +707,7 @@ public int length() { * JSONObject. * * @return A JSONArray containing the key strings, or null if the JSONObject - * is empty. + * is empty. */ public JSONArray names() { JSONArray ja = new JSONArray(); @@ -763,11 +721,9 @@ public JSONArray names() { /** * Produce a string from a Number. * - * @param number - * A Number + * @param number A Number * @return A String. - * @throws JSONException - * If n is a non-finite number. + * @throws JSONException If n is a non-finite number. */ public static String numberToString(Number number) throws JSONException { if (number == null) { @@ -793,8 +749,7 @@ public static String numberToString(Number number) throws JSONException { /** * Get an optional value associated with a key. * - * @param key - * A key string. + * @param key A key string. * @return An object which is the value, or null if there is no value. */ public Object opt(String key) { @@ -805,8 +760,7 @@ public Object opt(String key) { * Get an optional boolean associated with a key. It returns false if there * is no such key, or if the value is not Boolean.TRUE or the String "true". * - * @param key - * A key string. + * @param key A key string. * @return The truth. */ public boolean optBoolean(String key) { @@ -818,10 +772,8 @@ public boolean optBoolean(String key) { * defaultValue if there is no such key, or if it is not a Boolean or the * String "true" or "false" (case insensitive). * - * @param key - * A key string. - * @param defaultValue - * The default. + * @param key A key string. + * @param defaultValue The default. * @return The truth. */ public boolean optBoolean(String key, boolean defaultValue) { @@ -837,8 +789,7 @@ public boolean optBoolean(String key, boolean defaultValue) { * key or if its value is not a number. If the value is a string, an attempt * will be made to evaluate it as a number. * - * @param key - * A string which is the key. + * @param key A string which is the key. * @return An object which is the value. */ public double optDouble(String key) { @@ -850,10 +801,8 @@ public double optDouble(String key) { * there is no such key or if its value is not a number. If the value is a * string, an attempt will be made to evaluate it as a number. * - * @param key - * A key string. - * @param defaultValue - * The default. + * @param key A key string. + * @param defaultValue The default. * @return An object which is the value. */ public double optDouble(String key, double defaultValue) { @@ -869,8 +818,7 @@ public double optDouble(String key, double defaultValue) { * such key or if the value is not a number. If the value is a string, an * attempt will be made to evaluate it as a number. * - * @param key - * A key string. + * @param key A key string. * @return An object which is the value. */ public int optInt(String key) { @@ -882,10 +830,8 @@ public int optInt(String key) { * is no such key or if the value is not a number. If the value is a string, * an attempt will be made to evaluate it as a number. * - * @param key - * A key string. - * @param defaultValue - * The default. + * @param key A key string. + * @param defaultValue The default. * @return An object which is the value. */ public int optInt(String key, int defaultValue) { @@ -900,8 +846,7 @@ public int optInt(String key, int defaultValue) { * Get an optional JSONArray associated with a key. It returns null if there * is no such key, or if its value is not a JSONArray. * - * @param key - * A key string. + * @param key A key string. * @return A JSONArray which is the value. */ public JSONArray optJSONArray(String key) { @@ -913,8 +858,7 @@ public JSONArray optJSONArray(String key) { * Get an optional JSONObject associated with a key. It returns null if * there is no such key, or if its value is not a JSONObject. * - * @param key - * A key string. + * @param key A key string. * @return A JSONObject which is the value. */ public JSONObject optJSONObject(String key) { @@ -927,8 +871,7 @@ public JSONObject optJSONObject(String key) { * such key or if the value is not a number. If the value is a string, an * attempt will be made to evaluate it as a number. * - * @param key - * A key string. + * @param key A key string. * @return An object which is the value. */ public long optLong(String key) { @@ -940,10 +883,8 @@ public long optLong(String key) { * is no such key or if the value is not a number. If the value is a string, * an attempt will be made to evaluate it as a number. * - * @param key - * A key string. - * @param defaultValue - * The default. + * @param key A key string. + * @param defaultValue The default. * @return An object which is the value. */ public long optLong(String key, long defaultValue) { @@ -959,8 +900,7 @@ public long optLong(String key, long defaultValue) { * if there is no such key. If the value is not a string and is not null, * then it is converted to a string. * - * @param key - * A key string. + * @param key A key string. * @return A string which is the value. */ public String optString(String key) { @@ -971,10 +911,8 @@ public String optString(String key) { * Get an optional string associated with a key. It returns the defaultValue * if there is no such key. * - * @param key - * A key string. - * @param defaultValue - * The default. + * @param key A key string. + * @param defaultValue The default. * @return A string which is the value. */ public String optString(String key, String defaultValue) { @@ -983,7 +921,7 @@ public String optString(String key, String defaultValue) { } @SuppressWarnings("rawtypes") - private void populateMap(Object bean) { + private void populateMap(Object bean) { Class klass = bean.getClass(); // If klass is a System class then set includeSuperClass to false. @@ -1032,13 +970,10 @@ private void populateMap(Object bean) { /** * Put a key/boolean pair in the JSONObject. * - * @param key - * A key string. - * @param value - * A boolean which is the value. + * @param key A key string. + * @param value A boolean which is the value. * @return this. - * @throws JSONException - * If the key is null. + * @throws JSONException If the key is null. */ public JSONObject put(String key, boolean value) throws JSONException { this.put(key, value ? Boolean.TRUE : Boolean.FALSE); @@ -1049,10 +984,8 @@ public JSONObject put(String key, boolean value) throws JSONException { * Put a key/value pair in the JSONObject, where the value will be a * JSONArray which is produced from a Collection. * - * @param key - * A key string. - * @param value - * A Collection value. + * @param key A key string. + * @param value A Collection value. * @return this. * @throws JSONException */ @@ -1064,13 +997,10 @@ public JSONObject put(String key, Collection value) throws JSONException /** * Put a key/double pair in the JSONObject. * - * @param key - * A key string. - * @param value - * A double which is the value. + * @param key A key string. + * @param value A double which is the value. * @return this. - * @throws JSONException - * If the key is null or if the number is invalid. + * @throws JSONException If the key is null or if the number is invalid. */ public JSONObject put(String key, double value) throws JSONException { this.put(key, new Double(value)); @@ -1080,13 +1010,10 @@ public JSONObject put(String key, double value) throws JSONException { /** * Put a key/int pair in the JSONObject. * - * @param key - * A key string. - * @param value - * An int which is the value. + * @param key A key string. + * @param value An int which is the value. * @return this. - * @throws JSONException - * If the key is null. + * @throws JSONException If the key is null. */ public JSONObject put(String key, int value) throws JSONException { this.put(key, new Integer(value)); @@ -1096,13 +1023,10 @@ public JSONObject put(String key, int value) throws JSONException { /** * Put a key/long pair in the JSONObject. * - * @param key - * A key string. - * @param value - * A long which is the value. + * @param key A key string. + * @param value A long which is the value. * @return this. - * @throws JSONException - * If the key is null. + * @throws JSONException If the key is null. */ public JSONObject put(String key, long value) throws JSONException { this.put(key, new Long(value)); @@ -1113,10 +1037,8 @@ public JSONObject put(String key, long value) throws JSONException { * Put a key/value pair in the JSONObject, where the value will be a * JSONObject which is produced from a Map. * - * @param key - * A key string. - * @param value - * A Map value. + * @param key A key string. + * @param value A Map value. * @return this. * @throws JSONException */ @@ -1129,15 +1051,12 @@ public JSONObject put(String key, Map value) throws JSONExceptio * Put a key/value pair in the JSONObject. If the value is null, then the * key will be removed from the JSONObject if it is present. * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. * @return this. - * @throws JSONException - * If the value is non-finite number or if the key is null. + * @throws JSONException If the value is non-finite number or if the key is null. */ public JSONObject put(String key, Object value) throws JSONException { if (key == null) { @@ -1157,11 +1076,10 @@ public JSONObject put(String key, Object value) throws JSONException { * are both non-null, and only if there is not already a member with that * name. * - * @param key string + * @param key string * @param value object * @return this. - * @throws JSONException - * if the key is a duplicate + * @throws JSONException if the key is a duplicate */ public JSONObject putOnce(String key, Object value) throws JSONException { if (key != null && value != null) { @@ -1177,15 +1095,12 @@ public JSONObject putOnce(String key, Object value) throws JSONException { * Put a key/value pair in the JSONObject, but only if the key and the value * are both non-null. * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. * @return this. - * @throws JSONException - * If the value is a non-finite number. + * @throws JSONException If the value is a non-finite number. */ public JSONObject putOpt(String key, Object value) throws JSONException { if (key != null && value != null) { @@ -1200,8 +1115,7 @@ public JSONObject putOpt(String key, Object value) throws JSONException { * allowing JSON text to be delivered in HTML. In JSON text, a string cannot * contain a control character or an unescaped quote or backslash. * - * @param string - * A String + * @param string A String * @return A String correctly formatted for insertion in a JSON text. */ public static String quote(String string) { @@ -1233,42 +1147,42 @@ public static Writer quote(String string, Writer w) throws IOException { b = c; c = string.charAt(i); switch (c) { - case '\\': - case '"': - w.write('\\'); - w.write(c); - break; - case '/': - if (b == '<') { + case '\\': + case '"': w.write('\\'); - } - w.write(c); - break; - case '\b': - w.write("\\b"); - break; - case '\t': - w.write("\\t"); - break; - case '\n': - w.write("\\n"); - break; - case '\f': - w.write("\\f"); - break; - case '\r': - w.write("\\r"); - break; - default: - if (c < ' ' || (c >= '\u0080' && c < '\u00a0') - || (c >= '\u2000' && c < '\u2100')) { - w.write("\\u"); - hhhh = Integer.toHexString(c); - w.write("0000", 0, 4 - hhhh.length()); - w.write(hhhh); - } else { w.write(c); - } + break; + case '/': + if (b == '<') { + w.write('\\'); + } + w.write(c); + break; + case '\b': + w.write("\\b"); + break; + case '\t': + w.write("\\t"); + break; + case '\n': + w.write("\\n"); + break; + case '\f': + w.write("\\f"); + break; + case '\r': + w.write("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } } } w.write('"'); @@ -1278,10 +1192,9 @@ public static Writer quote(String string, Writer w) throws IOException { /** * Remove a name and its value, if present. * - * @param key - * The name to be removed. + * @param key The name to be removed. * @return The value that was associated with the name, or null if there was - * no value. + * no value. */ public Object remove(String key) { return this.map.remove(key); @@ -1301,20 +1214,20 @@ public boolean similar(Object other) { return false; } Set set = this.keySet(); - if (!set.equals(((JSONObject)other).keySet())) { + if (!set.equals(((JSONObject) other).keySet())) { return false; } Iterator iterator = set.iterator(); while (iterator.hasNext()) { String name = iterator.next(); Object valueThis = this.get(name); - Object valueOther = ((JSONObject)other).get(name); + Object valueOther = ((JSONObject) other).get(name); if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { + if (!((JSONObject) valueThis).similar(valueOther)) { return false; } } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { + if (!((JSONArray) valueThis).similar(valueOther)) { return false; } } else if (!valueThis.equals(valueOther)) { @@ -1331,8 +1244,7 @@ public boolean similar(Object other) { * Try to convert a string into a number, boolean, or null. If the string * can't be converted, return the string. * - * @param string - * A String. + * @param string A String. * @return A simple JSON value. */ public static Object stringToValue(String string) { @@ -1383,10 +1295,8 @@ public static Object stringToValue(String string) { /** * Throw an exception if the object is a NaN or infinite number. * - * @param o - * The object to test. - * @throws JSONException - * If o is a non-finite number. + * @param o The object to test. + * @throws JSONException If o is a non-finite number. */ public static void testValidity(Object o) throws JSONException { if (o != null) { @@ -1408,12 +1318,10 @@ public static void testValidity(Object o) throws JSONException { * Produce a JSONArray containing the values of the members of this * JSONObject. * - * @param names - * A JSONArray containing a list of key strings. This determines - * the sequence of the values in the result. + * @param names A JSONArray containing a list of key strings. This determines + * the sequence of the values in the result. * @return A JSONArray of values. - * @throws JSONException - * If any of the values are non-finite numbers. + * @throws JSONException If any of the values are non-finite numbers. */ public JSONArray toJSONArray(JSONArray names) throws JSONException { if (names == null || names.length() == 0) { @@ -1434,9 +1342,9 @@ public JSONArray toJSONArray(JSONArray names) throws JSONException { * Warning: This method assumes that the data structure is acyclical. * * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). */ public String toString() { try { @@ -1451,14 +1359,12 @@ public String toString() { *

* Warning: This method assumes that the data structure is acyclical. * - * @param indentFactor - * The number of spaces to add to each level of indentation. + * @param indentFactor The number of spaces to add to each level of indentation. * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the object contains an invalid number. + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException If the object contains an invalid number. */ public String toString(int indentFactor) throws JSONException { StringWriter w = new StringWriter(); @@ -1478,21 +1384,19 @@ public String toString(int indentFactor) throws JSONException { * JSONObject will be made from it and its toJSONString method will be * called. Otherwise, the value's toString method will be called, and the * result will be quoted. - * + *

*

* Warning: This method assumes that the data structure is acyclical. * - * @param value - * The value to be serialized. + * @param value The value to be serialized. * @return a printable, displayable, transmittable representation of the - * object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the value is or contains an invalid number. + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException If the value is or contains an invalid number. */ @SuppressWarnings("unchecked") - public static String valueToString(Object value) throws JSONException { + public static String valueToString(Object value) throws JSONException { if (value == null || value.equals(null)) { return "null"; } @@ -1516,7 +1420,7 @@ public static String valueToString(Object value) throws JSONException { return value.toString(); } if (value instanceof Map) { - return new JSONObject((Map)value).toString(); + return new JSONObject((Map) value).toString(); } if (value instanceof Collection) { return new JSONArray((Collection) value).toString(); @@ -1535,12 +1439,11 @@ public static String valueToString(Object value) throws JSONException { * one of the java packages, turn it into a string. And if it doesn't, try * to wrap it in a JSONObject. If the wrapping fails, then null is returned. * - * @param object - * The object to wrap + * @param object The object to wrap * @return The wrapped value */ @SuppressWarnings("unchecked") - public static Object wrap(Object object) { + public static Object wrap(Object object) { try { if (object == null) { return NULL; @@ -1592,8 +1495,8 @@ public Writer write(Writer writer) throws JSONException { } @SuppressWarnings("unchecked") - static final Writer writeValue(Writer writer, Object value, - int indentFactor, int indent) throws JSONException, IOException { + static final Writer writeValue(Writer writer, Object value, + int indentFactor, int indent) throws JSONException, IOException { if (value == null || value.equals(null)) { writer.write("null"); } else if (value instanceof JSONObject) { diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java index dfa3ff15..fc266601 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java @@ -1,4 +1,5 @@ package org.cloudinary.json; + /** * The JSONString interface allows a toJSONString() * method so that a class can change the behavior of diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java index fe53fa9a..ca1dcaaf 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONTokener.java @@ -35,29 +35,30 @@ of this software and associated documentation files (the "Software"), to deal * A JSONTokener takes a source string and extracts characters and tokens from * it. It is used by the JSONObject and JSONArray constructors to parse * JSON source strings. + * * @author JSON.org * @version 2014-05-03 */ public class JSONTokener { - private long character; + private long character; private boolean eof; - private long index; - private long line; - private char previous; - private Reader reader; + private long index; + private long line; + private char previous; + private Reader reader; private boolean usePrevious; /** * Construct a JSONTokener from a Reader. * - * @param reader A reader. + * @param reader A reader. */ public JSONTokener(Reader reader) { this.reader = reader.markSupported() - ? reader - : new BufferedReader(reader); + ? reader + : new BufferedReader(reader); this.eof = false; this.usePrevious = false; this.previous = 0; @@ -69,6 +70,7 @@ public JSONTokener(Reader reader) { /** * Construct a JSONTokener from an InputStream. + * * @param inputStream The source. */ public JSONTokener(InputStream inputStream) throws JSONException { @@ -79,7 +81,7 @@ public JSONTokener(InputStream inputStream) throws JSONException { /** * Construct a JSONTokener from a string. * - * @param s A source string. + * @param s A source string. */ public JSONTokener(String s) { this(new StringReader(s)); @@ -104,9 +106,10 @@ public void back() throws JSONException { /** * Get the hex value of a character (base16). + * * @param c A character between '0' and '9' or between 'A' and 'F' or - * between 'a' and 'f'. - * @return An int between 0 and 15, or -1 if c was not a hex digit. + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. */ public static int dehexchar(char c) { if (c >= '0' && c <= '9') { @@ -129,6 +132,7 @@ public boolean end() { /** * Determine if the source string still contains characters that next() * can consume. + * * @return true if not yet at the end of the source. */ public boolean more() throws JSONException { @@ -181,6 +185,7 @@ public char next() throws JSONException { /** * Consume the next character, and check that it matches a specified * character. + * * @param c The character to match. * @return The character. * @throws JSONException if the character does not match. @@ -198,38 +203,38 @@ public char next(char c) throws JSONException { /** * Get the next n characters. * - * @param n The number of characters to take. - * @return A string of n characters. - * @throws JSONException - * Substring bounds error if there are not - * n characters remaining in the source string. + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException Substring bounds error if there are not + * n characters remaining in the source string. */ - public String next(int n) throws JSONException { - if (n == 0) { - return ""; - } + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } - char[] chars = new char[n]; - int pos = 0; + char[] chars = new char[n]; + int pos = 0; - while (pos < n) { - chars[pos] = this.next(); - if (this.end()) { - throw this.syntaxError("Substring bounds error"); - } - pos += 1; - } - return new String(chars); - } + while (pos < n) { + chars[pos] = this.next(); + if (this.end()) { + throw this.syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(chars); + } /** * Get the next char in the string, skipping whitespace. + * + * @return A character, or 0 if there are no more characters. * @throws JSONException - * @return A character, or 0 if there are no more characters. */ public char nextClean() throws JSONException { - for (;;) { + for (; ; ) { char c = this.next(); if (c == 0 || c > ' ') { return c; @@ -243,58 +248,59 @@ public char nextClean() throws JSONException { * Backslash processing is done. The formal JSON format does not * allow strings in single quotes, but an implementation is allowed to * accept them. + * * @param quote The quoting character, either - * " (double quote) or - * ' (single quote). - * @return A String. + * " (double quote) or + * ' (single quote). + * @return A String. * @throws JSONException Unterminated string. */ public String nextString(char quote) throws JSONException { char c; StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { c = this.next(); switch (c) { - case 0: - case '\n': - case '\r': - throw this.syntaxError("Unterminated string"); - case '\\': - c = this.next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - sb.append((char)Integer.parseInt(this.next(4), 16)); - break; - case '"': - case '\'': + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); case '\\': - case '/': - sb.append(c); + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char) Integer.parseInt(this.next(4), 16)); + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); + } break; default: - throw this.syntaxError("Illegal escape."); - } - break; - default: - if (c == quote) { - return sb.toString(); - } - sb.append(c); + if (c == quote) { + return sb.toString(); + } + sb.append(c); } } } @@ -303,12 +309,13 @@ public String nextString(char quote) throws JSONException { /** * Get the text up but not including the specified character or the * end of line, whichever comes first. - * @param delimiter A delimiter character. - * @return A string. + * + * @param delimiter A delimiter character. + * @return A string. */ public String nextTo(char delimiter) throws JSONException { StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { char c = this.next(); if (c == delimiter || c == 0 || c == '\n' || c == '\r') { if (c != 0) { @@ -324,13 +331,14 @@ public String nextTo(char delimiter) throws JSONException { /** * Get the text up but not including one of the specified delimiter * characters or the end of line, whichever comes first. + * * @param delimiters A set of delimiter characters. * @return A string, trimmed. */ public String nextTo(String delimiters) throws JSONException { char c; StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { c = this.next(); if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') { @@ -347,9 +355,9 @@ public String nextTo(String delimiters) throws JSONException { /** * Get the next value. The value can be a Boolean, Double, Integer, * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. - * @throws JSONException If syntax error. * * @return An object. + * @throws JSONException If syntax error. */ public Object nextValue() throws JSONException { char c = this.nextClean(); @@ -394,6 +402,7 @@ public Object nextValue() throws JSONException { /** * Skip characters until the next character is the requested character. * If the requested character is not found, no characters are skipped. + * * @param to A character to skip to. * @return The requested character, or zero if the requested character * is not found. @@ -427,7 +436,7 @@ public char skipTo(char to) throws JSONException { * Make a JSONException to signal a syntax error. * * @param message The error message. - * @return A JSONException object, suitable for throwing + * @return A JSONException object, suitable for throwing */ public JSONException syntaxError(String message) { return new JSONException(message + this.toString()); @@ -441,6 +450,6 @@ public JSONException syntaxError(String message) { */ public String toString() { return " at " + this.index + " [character " + this.character + " line " + - this.line + "]"; + this.line + "]"; } } 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 db8ebece..874a46ba 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -23,985 +23,984 @@ import static org.junit.Assert.*; public class CloudinaryTest { - private static final String DEFAULT_ROOT_PATH = "http://res.cloudinary.com/test123/"; - private static final String DEFAULT_UPLOAD_PATH = DEFAULT_ROOT_PATH + "image/upload/"; + private static final String DEFAULT_ROOT_PATH = "http://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; - - @Rule - public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); - } - - @Test - public void testCloudName() { - // should use cloud_name from config - String result = cloudinary.url().generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "test", result); - } - - @Test - 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); - } - - @Test - public void testSecureDistribution() { - // should use default secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistributionOverwrite() { - // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); - assertEquals("https://something.else.com/test123/image/upload/test", result); - } - - @Test - public void testSecureDistibution() { - // should take secure distribution from config if secure=TRUE - cloudinary.config.secureDistribution = "config.secure.distribution.com"; - String result = cloudinary.url().secure(true).generate("test"); - assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - 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"); - assertEquals("https://something.cloudfront.net/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testFormat() { - // should use format from options - String result = cloudinary.url().format("jpg").generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "test.jpg", result); - } - - @Test - public void testCrop() { - Transformation transformation = new Transformation().width(100).height(101); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "h_101,w_100/test", result); - assertEquals("101", transformation.getHtmlHeight().toString()); - assertEquals("100", transformation.getHtmlWidth().toString()); - transformation = new Transformation().width(100).height(101).crop("crop"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_101,w_100/test", result); - } - - @Test - public void testVariousOptions() { - // should use x, y, radius, prefix, gravity and quality from options - Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); - } - - @Test - public void testTransformationSimple() { - // should support named transformation - Transformation transformation = new Transformation().named("blip"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "t_blip/test", result); - } - - @Test - public void testTransformationArray() { - // should support array of named transformations - Transformation transformation = new Transformation().named("blip", "blop"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "t_blip.blop/test", result); - } - - @Test - public void testBaseTransformations() { - // should support base transformation - Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/c_crop,w_100/test", result); - } - - @Test - public void testBaseTransformationArray() { - // should support array of base transformations - Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); - assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); - } - - @Test - public void testNoEmptyTransformation() { - // should not include empty transformations - Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/test", result); - } - - @Test - 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); - } - - @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); - } - - @Test - public void testIgnoreHttp() { - // should ignore http links only if type is not given or is asset - String result = cloudinary.url().generate("http://test"); - assertEquals("http://test", result); - 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); - } - - @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); - } - - @Test - public void testCname() { - // should support external cname - String result = cloudinary.url().cname("hello.com").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"); - assertEquals("http://a2.hello.com/test123/image/upload/test", result); - } - - @Test - 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); - } - - @Test - public void testBackground() { - // should support background - Transformation transformation = new Transformation().background("red"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "b_red/test", result); - transformation = new Transformation().background("#112233"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "b_rgb:112233/test", result); - } - - @Test - public void testDefaultImage() { - // should support default_image - Transformation transformation = new Transformation().defaultImage("default"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "d_default/test", result); - } - - @Test - public void testAngle() { - // should support angle - Transformation transformation = new Transformation().angle(12); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "a_12/test", result); - transformation = new Transformation().angle("exif", "12"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "a_exif.12/test", result); - } - - @Test - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); - - transformation = new Transformation().overlay(new TextLayer().text("goodbye")); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); - } - - @Test - 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); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); - } - - @Test - 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); - } - - @Test - public void testEffect() { - // should support effect - Transformation transformation = new Transformation().effect("sepia"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia/test", result); - } - - @Test - public void testEffectWithParam() { - // should support effect with param - Transformation transformation = new Transformation().effect("sepia", 10); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia:10/test", result); - } - - @Test - public void testDensity() { - // should support density - Transformation transformation = new Transformation().density(150); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "dn_150/test", result); - } - - @Test - public void testPage() { - // should support page - Transformation transformation = new Transformation().page(5); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "pg_5/test", result); - } - - @Test - public void testBorder() { - // should support border - Transformation transformation = new Transformation().border(5, "black"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_black/test", result); - transformation = new Transformation().border(5, "#ffaabbdd"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_rgb:ffaabbdd/test", result); - transformation = new Transformation().border("1px_solid_blue"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "bo_1px_solid_blue/test", result); - } - - @Test - public void testFlags() { - // should support flags - Transformation transformation = new Transformation().flags("abc"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc/test", result); - transformation = new Transformation().flags("abc", "def"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc.def/test", result); - } - - @Test - public void testOpacity() { - // should support opacity - Transformation transformation = new Transformation().opacity(50); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "o_50/test", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testImageTag() { - Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals("my image", result); - transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); - assertEquals( - "my image", - result); - transformation = new Transformation().width("auto").crop("crop"); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); - assertEquals( - "my image", - result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); - assertEquals( - "my image", - result); - } - - @Test - public void testFolders() { - // should add version if public_id contains / - String result = cloudinary.url().generate("folder/test"); - assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); - result = cloudinary.url().version(123).generate("folder/test"); - assertEquals(DEFAULT_UPLOAD_PATH + "v123/folder/test", result); - } - - @Test - public void testFoldersWithVersion() { - // should not add version if public_id contains version already - String result = cloudinary.url().generate("v1234/test"); - assertEquals(DEFAULT_UPLOAD_PATH + "v1234/test", result); - } - - @Test - public void testShorten() { - // should allow to shorted image/upload urls - String result = cloudinary.url().shorten(true).generate("test"); - assertEquals("http://res.cloudinary.com/test123/iu/test", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testPrivateDownload() throws Exception { - String url = cloudinary.privateDownload("imgÿ=&é", "jpg", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("imgÿ=&é", parameters.get("public_id")); - assertEquals("jpg", parameters.get("format")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download", uri.getPath()); - } - - @SuppressWarnings("unchecked") - @Test - public void testZipDownload() throws Exception { - String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); - URI uri = new URI(url); - Map parameters = getUrlParameters(uri); - assertEquals("ttag", parameters.get("tag")); - assertEquals("a", parameters.get("api_key")); - assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); - } - - @Test - public void testSpriteCss() { - String result = cloudinary.url().generateSpriteCss("test"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - result = cloudinary.url().generateSpriteCss("test.css"); - assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); - } - - @SuppressWarnings("unchecked") - @Test - public void testEscapePublicId() { - // should escape public_ids - Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); - for (Map.Entry entry : tests.entrySet()) { - String result = cloudinary.url().generate(entry.getKey()); - assertEquals(DEFAULT_UPLOAD_PATH + "" + entry.getValue(), result); - } - } - - @Test - public void testSignedUrl() { - // should correctly sign a url - String expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; - String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) - .generate("image.jpg"); - assertEquals(expected, actual); - - expected = DEFAULT_UPLOAD_PATH + "s----SjmNDA--/v1234/image.jpg"; - actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - - expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; - actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); - assertEquals(expected, actual); - } - - @Test - public void testResponsiveWidth() { - // should support responsive width - Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - String result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); - trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(trans).generate("test"); - assertTrue(trans.isResponsive()); - assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_pad,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(null); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInSharedDistribution() { - cloudinary.url().suffix("hello").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInNonUploadTypes() { - cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithSlash() { - cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithDot() { - cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); - } - - @Test - public void testSupportUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); - assertEquals("http://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); - - } - - @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); - } - - @Test - public void testNotSignTheUrlSuffix() { - - Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); - String url = cloudinary.url().format("jpg").signed(true).generate("test"); - Matcher matcher = pattern.matcher(url); - matcher.find(); - 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); - - url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); - matcher = pattern.matcher(url); - matcher.find(); - expectedSignature = url.substring(matcher.start(), matcher.end()); - - 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); - } - - @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); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathInSharedDistribution() { - cloudinary.url().useRootPath(true).generate("test"); - } - - @Test - public void testSupportUseRootPathForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); - assertEquals("http://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); - } - - @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); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForFacebook() { - cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForRaw() { - cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); - } - - @Test - public void testVideoCodec() { - // should support a string value - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoCodec("auto")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vc_auto/video_id", actual); - // should support a hash value - actual = cloudinary.url().resourceType("video") - .transformation( - new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264", "profile", "basic", "level","3.1")) - ).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); - } - - @Test - public void testAudioCodec(){ - // should support a string value - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioCodec("acc")).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "ac_acc/video_id", actual); - } - - @Test - public void testBitRate() { - // should support a numeric value - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate(2048)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "br_2048/video_id", actual); - // should support a string value - actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("44k")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "br_44k/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("1m")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "br_1m/video_id", actual); - - } - - @Test - public void testAudioFrequency() { - // should support an integer value - String actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().audioFrequency(44100)).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); - // should support a string value - actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioFrequency("44100")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); - } - - @Test - public void testVideoSampling() { - String actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().videoSamplingFrames(20)).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vs_20/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vs_20s/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20.0)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vs_20.0s/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSampling("2.3s")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "vs_2.3s/video_id", actual); - } - - @Test - public void testStartOffset() { - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset(2.63)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "so_2.63/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63p")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63%")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffsetPercent(2.63)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); - } - - @Test - public void testDuration() { - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration(2.63)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "du_2.63/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63p")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63%")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().durationPercent(2.63)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); - } - - @Test - public void testOffset() { - - String actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset("2.66..3.21")).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_3.21,so_2.66/video_id", actual); - actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset(new float[] { 2.67f, 3.22f })).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); - actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset(new double[] { 2.67, 3.22 })).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); - actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset(new String[] { "35%", "70%" })).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_70p,so_35p/video_id", actual); - actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset(new String[] { "36p", "71p" })).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_71p,so_36p/video_id", actual); - actual = cloudinary.url().resourceType("video") - .transformation(new Transformation().offset(new String[] { "35.5p", "70.5p" })).generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "eo_70.5p,so_35.5p/video_id", actual); - - } - - @Test - public void testZoom() { - String actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom("1.5")) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); - actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom(1.5)) - .generate("video_id"); - assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); - } - - @Test - public void testUtils() { - assertEquals(ObjectUtils.asBoolean(true, null), true); - assertEquals(ObjectUtils.asBoolean(false, null), false); - } - - @Test - public void testVideoTag() { - String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - assertEquals(expectedTag, cloudinary.url().videoTag("movie", ObjectUtils.emptyMap())); - assertEquals(expectedTag, cloudinary.url().publicId("movie").videoTag()); - } - - @Test - public void testVideoTagWithAttributes() { - Map attributes = ObjectUtils.asMap( - "autoplay", true, - "controls", null, - "loop", null, - "muted", "true", - "preload", null, - "style", "border: 1px"); - String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - assertEquals(expectedTag, cloudinary.url().videoTag("movie", attributes)); - } - - @Test - public void testVideoTagWithTransformation() { - Transformation transformation = new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264")) - .audioCodec("acc").startOffset(3); - String expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264/movie"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl); - String actualTag = cloudinary.url().transformation(transformation).sourceTypes(new String[] { "mp4" }) - .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); - assertEquals(expectedTag, actualTag); - - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); - assertEquals(expectedTag, actualTag); - - transformation.width(250); - expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264,w_250/movie"; - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap()); - assertEquals(expectedTag, actualTag); - - transformation.crop("fit"); - expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,c_fit,so_3.0,vc_h264,w_250/movie"; - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap()); - assertEquals(expectedTag, actualTag); - } - - @Test - public void testVideoTagWithFallback() { - String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; - String fallback = "Cannot display video"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, fallback); - String actualTag = cloudinary.url().fallbackContent(fallback).sourceTypes(new String[] { "mp4" }) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl, fallback); - actualTag = cloudinary.url().fallbackContent(fallback).videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - } - - @Test - public void testVideoTagWithSourceTypes() { - String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl); - String actualTag = cloudinary.url().sourceTypes(new String[] { "ogv", "mp4" }) - .videoTag("movie.mp4", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - } - - @Test - public void testVideoTagWithSourceTransformation() { - String expectedUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/movie"; - String expectedOgvUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/q_70/movie"; - String expectedMp4Url = VIDEO_UPLOAD_PATH + "q_50/w_100/q_30/movie"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url, expectedOgvUrl); - String actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) - .sourceTransformationFor("mp4", new Transformation().quality(30)) - .sourceTransformationFor("ogv", new Transformation().quality(70)) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url); - actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) - .sourceTransformationFor("mp4", new Transformation().quality(30)) - .sourceTransformationFor("ogv", new Transformation().quality(70)) - .sourceTypes(new String[] { "webm", "mp4" }).videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - } - - @Test - public void testVideoTagWithPoster() { - String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; - String posterUrl = "http://image/somewhere.jpg"; - String expectedTag = ""; - expectedTag = String.format(expectedTag, posterUrl, expectedUrl); - String actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}).poster(posterUrl) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - posterUrl = VIDEO_UPLOAD_PATH + "g_north/movie.jpg"; - expectedTag = ""; - expectedTag = String.format(expectedTag, posterUrl, expectedUrl); - actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) - .poster(new Transformation().gravity("north")) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - posterUrl = DEFAULT_UPLOAD_PATH + "g_north/my_poster.jpg"; - expectedTag = ""; - expectedTag = String.format(expectedTag, posterUrl, expectedUrl); - actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) - .poster(cloudinary.url() - .publicId("my_poster") - .format("jpg") - .transformation(new Transformation().gravity("north"))) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - expectedTag = ""; - expectedTag = String.format(expectedTag, expectedUrl); - actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) - .poster(null) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) - .poster(false) - .videoTag("movie", ObjectUtils.emptyMap()); - assertEquals(expectedTag, actualTag); - - } - - @Test - public void testAspectRatio() { - String actual = cloudinary.url().transformation(new Transformation().aspectRatio("1.5")) - .generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); - actual = cloudinary.url().transformation(new Transformation().aspectRatio(1.5)) - .generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); - actual = cloudinary.url().transformation(new Transformation().aspectRatio(3,2)) - .generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "ar_3:2/test", actual); - } - - @Test - public void testOverlayOptions() { - Object tests[] = { - new Layer().publicId("logo"), - "logo", - new Layer().publicId("folder/logo"), - "folder:logo", - new Layer().publicId("logo").type("private"), - "private:logo", - new Layer().publicId("logo").format("png"), - "logo.png", - new Layer().resourceType("video").publicId("cat"), - "video:cat", - new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) - .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), - "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", - new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), - "subtitles:Arial_40:sample_sub_he.srt" }; - - for (int i = 0; i < tests.length; i += 2) { - Object layer = tests[i]; - String expected = (String) tests[i + 1]; - assertEquals(expected, layer.toString()); - } - } - - - @Test - public void testBackwardCampatibleOverlayOptions() { - Object tests[] = { - new LayerBuilder().publicId("logo"), - "logo", - new LayerBuilder().publicId("folder/logo"), - "folder:logo", - new LayerBuilder().publicId("logo").type("private"), - "private:logo", - new LayerBuilder().publicId("logo").format("png"), - "logo.png", - new LayerBuilder().resourceType("video").publicId("cat"), - "video:cat", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) - .fontWeight("bold").fontStyle("italic").letterSpacing("4"), - "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", - new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", - new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), - "subtitles:Arial_40:sample_sub_he.srt" }; - - for (int i = 0; i < tests.length; i += 2) { - Object layer = tests[i]; - String expected = (String) tests[i + 1]; - assertEquals(expected, layer.toString()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testOverlayError1() { - // Must supply font_family for text in overlay - cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testOverlayError2() { - // Must supply public_id for for non-text underlay - cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); - } - - @Test - public void testResponsiveBreakpointsToJson() { - assertEquals("an empty ResponsiveBreakpoint should have create_derived=true", - "{\"create_derived\":true}", - new ResponsiveBreakpoint().toString() - ); - String[] expectedArr = "{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}".split("[{}]")[1].split(",(?=\")"); - Arrays.sort(expectedArr); - JSONObject actual = new ResponsiveBreakpoint().createDerived(false) - .transformation(new Transformation().angle(45)) - .maxWidth(500) - .minWidth(100) - .maxImages(5) - ; - String[] actualArr = actual.toString().split("[{}]")[1].split(",(?=\")"); - Arrays.sort(actualArr); - assertArrayEquals(expectedArr, actualArr); - } - - public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { - Map params = new HashMap(); - for (String param : uri.getRawQuery().split("&")) { - String pair[] = param.split("="); - String key = URLDecoder.decode(pair[0], "UTF-8"); - String value = ""; - if (pair.length > 1) { - value = URLDecoder.decode(pair[1], "UTF-8"); - } - params.put(new String(key), new String(value)); - } - return params; - } + private Cloudinary cloudinary; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + } + + @Test + public void testCloudName() { + // should use cloud_name from config + String result = cloudinary.url().generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "test", result); + } + + @Test + 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); + } + + @Test + public void testSecureDistribution() { + // should use default secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistributionOverwrite() { + // should allow overwriting secure distribution if secure=TRUE + String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); + assertEquals("https://something.else.com/test123/image/upload/test", result); + } + + @Test + public void testSecureDistibution() { + // should take secure distribution from config if secure=TRUE + cloudinary.config.secureDistribution = "config.secure.distribution.com"; + String result = cloudinary.url().secure(true).generate("test"); + assertEquals("https://config.secure.distribution.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + 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"); + assertEquals("https://something.cloudfront.net/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testFormat() { + // should use format from options + String result = cloudinary.url().format("jpg").generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "test.jpg", result); + } + + @Test + public void testCrop() { + Transformation transformation = new Transformation().width(100).height(101); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "h_101,w_100/test", result); + assertEquals("101", transformation.getHtmlHeight().toString()); + assertEquals("100", transformation.getHtmlWidth().toString()); + transformation = new Transformation().width(100).height(101).crop("crop"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_101,w_100/test", result); + } + + @Test + public void testVariousOptions() { + // should use x, y, radius, prefix, gravity and quality from options + Transformation transformation = new Transformation().x(1).y(2).radius(3).gravity("center").quality(0.4).prefix("a"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); + } + + @Test + public void testTransformationSimple() { + // should support named transformation + Transformation transformation = new Transformation().named("blip"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "t_blip/test", result); + } + + @Test + public void testTransformationArray() { + // should support array of named transformations + Transformation transformation = new Transformation().named("blip", "blop"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "t_blip.blop/test", result); + } + + @Test + public void testBaseTransformations() { + // should support base transformation + Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/c_crop,w_100/test", result); + } + + @Test + public void testBaseTransformationArray() { + // should support array of base transformations + Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); + } + + @Test + public void testNoEmptyTransformation() { + // should not include empty transformations + Transformation transformation = new Transformation().chain().x(100).y(100).crop("fill").chain(); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/test", result); + } + + @Test + 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); + } + + @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); + } + + @Test + public void testIgnoreHttp() { + // should ignore http links only if type is not given or is asset + String result = cloudinary.url().generate("http://test"); + assertEquals("http://test", result); + 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); + } + + @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); + } + + @Test + public void testCname() { + // should support external cname + String result = cloudinary.url().cname("hello.com").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"); + assertEquals("http://a2.hello.com/test123/image/upload/test", result); + } + + @Test + 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); + } + + @Test + public void testBackground() { + // should support background + Transformation transformation = new Transformation().background("red"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "b_red/test", result); + transformation = new Transformation().background("#112233"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "b_rgb:112233/test", result); + } + + @Test + public void testDefaultImage() { + // should support default_image + Transformation transformation = new Transformation().defaultImage("default"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "d_default/test", result); + } + + @Test + public void testAngle() { + // should support angle + Transformation transformation = new Transformation().angle(12); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "a_12/test", result); + transformation = new Transformation().angle("exif", "12"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "a_exif.12/test", result); + } + + @Test + public void testOverlay() { + // should support overlay + Transformation transformation = new Transformation().overlay("text:hello"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); + // should not pass width/height to html if overlay + transformation = new Transformation().overlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); + + transformation = new Transformation().overlay(new TextLayer().text("goodbye")); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); + } + + @Test + 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); + // should not pass width/height to html if underlay + transformation = new Transformation().underlay("text:hello").width(100).height(100); + result = cloudinary.url().transformation(transformation).generate("test"); + assertNull(transformation.getHtmlHeight()); + assertNull(transformation.getHtmlWidth()); + assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); + } + + @Test + 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); + } + + @Test + public void testEffect() { + // should support effect + Transformation transformation = new Transformation().effect("sepia"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia/test", result); + } + + @Test + public void testEffectWithParam() { + // should support effect with param + Transformation transformation = new Transformation().effect("sepia", 10); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia:10/test", result); + } + + @Test + public void testDensity() { + // should support density + Transformation transformation = new Transformation().density(150); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "dn_150/test", result); + } + + @Test + public void testPage() { + // should support page + Transformation transformation = new Transformation().page(5); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "pg_5/test", result); + } + + @Test + public void testBorder() { + // should support border + Transformation transformation = new Transformation().border(5, "black"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_black/test", result); + transformation = new Transformation().border(5, "#ffaabbdd"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_5px_solid_rgb:ffaabbdd/test", result); + transformation = new Transformation().border("1px_solid_blue"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "bo_1px_solid_blue/test", result); + } + + @Test + public void testFlags() { + // should support flags + Transformation transformation = new Transformation().flags("abc"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc/test", result); + transformation = new Transformation().flags("abc", "def"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "fl_abc.def/test", result); + } + + @Test + public void testOpacity() { + // should support opacity + Transformation transformation = new Transformation().opacity(50); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "o_50/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testImageTag() { + Transformation transformation = new Transformation().width(100).height(101).crop("crop"); + String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals("my image", result); + transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); + assertEquals( + "my image", + result); + transformation = new Transformation().width("auto").crop("crop"); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); + assertEquals( + "my image", + result); + result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); + assertEquals( + "my image", + result); + } + + @Test + public void testFolders() { + // should add version if public_id contains / + String result = cloudinary.url().generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); + result = cloudinary.url().version(123).generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v123/folder/test", result); + } + + @Test + public void testFoldersWithVersion() { + // should not add version if public_id contains version already + String result = cloudinary.url().generate("v1234/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1234/test", result); + } + + @Test + public void testShorten() { + // should allow to shorted image/upload urls + String result = cloudinary.url().shorten(true).generate("test"); + assertEquals("http://res.cloudinary.com/test123/iu/test", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testPrivateDownload() throws Exception { + String url = cloudinary.privateDownload("imgÿ=&é", "jpg", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("imgÿ=&é", parameters.get("public_id")); + assertEquals("jpg", parameters.get("format")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download", uri.getPath()); + } + + @SuppressWarnings("unchecked") + @Test + public void testZipDownload() throws Exception { + String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); + URI uri = new URI(url); + Map parameters = getUrlParameters(uri); + assertEquals("ttag", parameters.get("tag")); + assertEquals("a", parameters.get("api_key")); + assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); + } + + @Test + public void testSpriteCss() { + String result = cloudinary.url().generateSpriteCss("test"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + result = cloudinary.url().generateSpriteCss("test.css"); + assertEquals("http://res.cloudinary.com/test123/image/sprite/test.css", result); + } + + @SuppressWarnings("unchecked") + @Test + public void testEscapePublicId() { + // should escape public_ids + Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + for (Map.Entry entry : tests.entrySet()) { + String result = cloudinary.url().generate(entry.getKey()); + assertEquals(DEFAULT_UPLOAD_PATH + "" + entry.getValue(), result); + } + } + + @Test + public void testSignedUrl() { + // should correctly sign a url + String expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg"; + String actual = cloudinary.url().version(1234).transformation(new Transformation().crop("crop").width(10).height(20)).signed(true) + .generate("image.jpg"); + assertEquals(expected, actual); + + expected = DEFAULT_UPLOAD_PATH + "s----SjmNDA--/v1234/image.jpg"; + actual = cloudinary.url().version(1234).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + + expected = DEFAULT_UPLOAD_PATH + "s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg"; + actual = cloudinary.url().transformation(new Transformation().crop("crop").width(10).height(20)).signed(true).generate("image.jpg"); + assertEquals(expected, actual); + } + + @Test + public void testResponsiveWidth() { + // should support responsive width + Transformation trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + String result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_limit,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); + trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); + result = cloudinary.url().transformation(trans).generate("test"); + assertTrue(trans.isResponsive()); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_pad,w_auto/test", result); + Transformation.setResponsiveWidthTransformation(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInSharedDistribution() { + cloudinary.url().suffix("hello").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInNonUploadTypes() { + cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithSlash() { + cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithDot() { + cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); + } + + @Test + public void testSupportUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); + assertEquals("http://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); + + } + + @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); + } + + @Test + public void testNotSignTheUrlSuffix() { + + Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); + String url = cloudinary.url().format("jpg").signed(true).generate("test"); + Matcher matcher = pattern.matcher(url); + matcher.find(); + 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); + + url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); + matcher = pattern.matcher(url); + matcher.find(); + expectedSignature = url.substring(matcher.start(), matcher.end()); + + 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); + } + + @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); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathInSharedDistribution() { + cloudinary.url().useRootPath(true).generate("test"); + } + + @Test + public void testSupportUseRootPathForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); + assertEquals("http://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); + } + + @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); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForFacebook() { + cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForRaw() { + cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); + } + + @Test + public void testVideoCodec() { + // should support a string value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoCodec("auto")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_auto/video_id", actual); + // should support a hash value + actual = cloudinary.url().resourceType("video") + .transformation( + new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264", "profile", "basic", "level", "3.1")) + ).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); + } + + @Test + public void testAudioCodec() { + // should support a string value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioCodec("acc")).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "ac_acc/video_id", actual); + } + + @Test + public void testBitRate() { + // should support a numeric value + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate(2048)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_2048/video_id", actual); + // should support a string value + actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("44k")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_44k/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().bitRate("1m")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "br_1m/video_id", actual); + + } + + @Test + public void testAudioFrequency() { + // should support an integer value + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().audioFrequency(44100)).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); + // should support a string value + actual = cloudinary.url().resourceType("video").transformation(new Transformation().audioFrequency("44100")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "af_44100/video_id", actual); + } + + @Test + public void testVideoSampling() { + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().videoSamplingFrames(20)).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20s/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSamplingSeconds(20.0)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_20.0s/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().videoSampling("2.3s")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vs_2.3s/video_id", actual); + } + + @Test + public void testStartOffset() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63p")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("2.63%")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffsetPercent(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + } + + @Test + public void testDuration() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63p")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().duration("2.63%")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().durationPercent(2.63)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "du_2.63p/video_id", actual); + } + + @Test + public void testOffset() { + + String actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset("2.66..3.21")).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.21,so_2.66/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new float[]{2.67f, 3.22f})).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new double[]{2.67, 3.22})).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_3.22,so_2.67/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[]{"35%", "70%"})).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_70p,so_35p/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[]{"36p", "71p"})).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_71p,so_36p/video_id", actual); + actual = cloudinary.url().resourceType("video") + .transformation(new Transformation().offset(new String[]{"35.5p", "70.5p"})).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "eo_70.5p,so_35.5p/video_id", actual); + + } + + @Test + public void testZoom() { + String actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom("1.5")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().zoom(1.5)) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "z_1.5/video_id", actual); + } + + @Test + public void testUtils() { + assertEquals(ObjectUtils.asBoolean(true, null), true); + assertEquals(ObjectUtils.asBoolean(false, null), false); + } + + @Test + public void testVideoTag() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + assertEquals(expectedTag, cloudinary.url().videoTag("movie", ObjectUtils.emptyMap())); + assertEquals(expectedTag, cloudinary.url().publicId("movie").videoTag()); + } + + @Test + public void testVideoTagWithAttributes() { + Map attributes = ObjectUtils.asMap( + "autoplay", true, + "controls", null, + "loop", null, + "muted", "true", + "preload", null, + "style", "border: 1px"); + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + assertEquals(expectedTag, cloudinary.url().videoTag("movie", attributes)); + } + + @Test + public void testVideoTagWithTransformation() { + Transformation transformation = new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264")) + .audioCodec("acc").startOffset(3); + String expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264/movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl); + String actualTag = cloudinary.url().transformation(transformation).sourceTypes(new String[]{"mp4"}) + .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + assertEquals(expectedTag, actualTag); + + transformation.width(250); + expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264,w_250/movie"; + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap()); + assertEquals(expectedTag, actualTag); + + transformation.crop("fit"); + expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,c_fit,so_3.0,vc_h264,w_250/movie"; + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); + actualTag = cloudinary.url().transformation(transformation) + .videoTag("movie", ObjectUtils.asMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithFallback() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String fallback = "Cannot display video"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, fallback); + String actualTag = cloudinary.url().fallbackContent(fallback).sourceTypes(new String[]{"mp4"}) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl, fallback); + actualTag = cloudinary.url().fallbackContent(fallback).videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithSourceTypes() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl); + String actualTag = cloudinary.url().sourceTypes(new String[]{"ogv", "mp4"}) + .videoTag("movie.mp4", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithSourceTransformation() { + String expectedUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/movie"; + String expectedOgvUrl = VIDEO_UPLOAD_PATH + "q_50/w_100/q_70/movie"; + String expectedMp4Url = VIDEO_UPLOAD_PATH + "q_50/w_100/q_30/movie"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url, expectedOgvUrl); + String actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) + .sourceTransformationFor("mp4", new Transformation().quality(30)) + .sourceTransformationFor("ogv", new Transformation().quality(70)) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedMp4Url); + actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) + .sourceTransformationFor("mp4", new Transformation().quality(30)) + .sourceTransformationFor("ogv", new Transformation().quality(70)) + .sourceTypes(new String[]{"webm", "mp4"}).videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + } + + @Test + public void testVideoTagWithPoster() { + String expectedUrl = VIDEO_UPLOAD_PATH + "movie"; + String posterUrl = "http://image/somewhere.jpg"; + String expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + String actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}).poster(posterUrl) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + posterUrl = VIDEO_UPLOAD_PATH + "g_north/movie.jpg"; + expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(new Transformation().gravity("north")) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + posterUrl = DEFAULT_UPLOAD_PATH + "g_north/my_poster.jpg"; + expectedTag = ""; + expectedTag = String.format(expectedTag, posterUrl, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(cloudinary.url() + .publicId("my_poster") + .format("jpg") + .transformation(new Transformation().gravity("north"))) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + expectedTag = ""; + expectedTag = String.format(expectedTag, expectedUrl); + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(null) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + actualTag = cloudinary.url().sourceTypes(new String[]{"mp4"}) + .poster(false) + .videoTag("movie", ObjectUtils.emptyMap()); + assertEquals(expectedTag, actualTag); + + } + + @Test + public void testAspectRatio() { + String actual = cloudinary.url().transformation(new Transformation().aspectRatio("1.5")) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); + actual = cloudinary.url().transformation(new Transformation().aspectRatio(1.5)) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_1.5/test", actual); + actual = cloudinary.url().transformation(new Transformation().aspectRatio(3, 2)) + .generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "ar_3:2/test", actual); + } + + @Test + public void testOverlayOptions() { + Object tests[] = { + new Layer().publicId("logo"), + "logo", + new Layer().publicId("folder/logo"), + "folder:logo", + new Layer().publicId("logo").type("private"), + "private:logo", + new Layer().publicId("logo").format("png"), + "logo.png", + new Layer().resourceType("video").publicId("cat"), + "video:cat", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), + "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + "subtitles:Arial_40:sample_sub_he.srt"}; + + for (int i = 0; i < tests.length; i += 2) { + Object layer = tests[i]; + String expected = (String) tests[i + 1]; + assertEquals(expected, layer.toString()); + } + } + + + @Test + public void testBackwardCampatibleOverlayOptions() { + Object tests[] = { + new LayerBuilder().publicId("logo"), + "logo", + new LayerBuilder().publicId("folder/logo"), + "folder:logo", + new LayerBuilder().publicId("logo").type("private"), + "private:logo", + new LayerBuilder().publicId("logo").format("png"), + "logo.png", + new LayerBuilder().resourceType("video").publicId("cat"), + "video:cat", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing("4"), + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", + new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), + "subtitles:Arial_40:sample_sub_he.srt"}; + + for (int i = 0; i < tests.length; i += 2) { + Object layer = tests[i]; + String expected = (String) tests[i + 1]; + assertEquals(expected, layer.toString()); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError1() { + // Must supply font_family for text in overlay + cloudinary.url().transformation(new Transformation().overlay(new TextLayer().fontStyle("italic"))).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testOverlayError2() { + // Must supply public_id for for non-text underlay + cloudinary.url().transformation(new Transformation().underlay(new Layer().resourceType("video"))).generate("test"); + } + + @Test + public void testResponsiveBreakpointsToJson() { + assertEquals("an empty ResponsiveBreakpoint should have create_derived=true", + "{\"create_derived\":true}", + new ResponsiveBreakpoint().toString() + ); + String[] expectedArr = "{\"create_derived\":false,\"max_width\":500,\"min_width\":100,\"max_images\":5,\"transformation\":\"a_45\"}".split("[{}]")[1].split(",(?=\")"); + Arrays.sort(expectedArr); + JSONObject actual = new ResponsiveBreakpoint().createDerived(false) + .transformation(new Transformation().angle(45)) + .maxWidth(500) + .minWidth(100) + .maxImages(5); + String[] actualArr = actual.toString().split("[{}]")[1].split(",(?=\")"); + Arrays.sort(actualArr); + assertArrayEquals(expectedArr, actualArr); + } + + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { + Map params = new HashMap(); + for (String param : uri.getRawQuery().split("&")) { + String pair[] = param.split("="); + String key = URLDecoder.decode(pair[0], "UTF-8"); + String value = ""; + if (pair.length > 1) { + value = URLDecoder.decode(pair[1], "UTF-8"); + } + params.put(new String(key), new String(value)); + } + return params; + } } 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 34d861be..76660c26 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -79,7 +79,7 @@ public void testLayerOptions() { "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), - "subtitles:Arial_40:sample_sub_he.srt" }; + "subtitles:Arial_40:sample_sub_he.srt"}; for (int i = 0; i < tests.length; i += 2) { Object layer = tests[i]; From 3dceeb0e7055722d004dde665ce54901cf83a5e5 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 2 Mar 2016 15:10:12 +0200 Subject: [PATCH 122/592] Whitespace --- .../com/cloudinary/http42/ApiStrategy.java | 153 ++++++----- .../cloudinary/http42/UploaderStrategy.java | 200 +++++++-------- .../com/cloudinary/http42/api/Response.java | 90 +++---- .../java/com/cloudinary/test/ApiTest.java | 2 +- .../com/cloudinary/http43/ApiStrategy.java | 222 ++++++++-------- .../cloudinary/http43/UploaderStrategy.java | 241 +++++++++--------- .../com/cloudinary/http43/api/Response.java | 90 +++---- .../com/cloudinary/http44/ApiStrategy.java | 223 ++++++++-------- .../cloudinary/http44/UploaderStrategy.java | 241 +++++++++--------- .../com/cloudinary/http44/api/Response.java | 90 +++---- 10 files changed, 771 insertions(+), 781 deletions(-) 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 3d509040..07bd18ac 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -31,95 +31,90 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -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 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); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); +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 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); + if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); + if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); int timeout = ObjectUtils.asInteger(options.get("timeout"), this.api.cloudinary.config.timeout); String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - 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"); + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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 ); + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); - request.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); - - 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); - } - } + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + request.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); + + 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 index d9e1e769..95998b17 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -30,105 +30,105 @@ public class UploaderStrategy extends AbstractUploaderStrategy { - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(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", Cloudinary.USER_AGENT + " 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); - // 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) 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("Uprecognized 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); - - if (code != 200 && code != 400 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } - return result; - } + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public Map callApi(String action, Map params, Map options, Object file) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(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", Cloudinary.USER_AGENT + " 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); + // 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) 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("Uprecognized 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); + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("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 (result.containsKey("error")) { + Map error = (Map) result.get("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException((String) error.get("message")); + } + } + return result; + } } diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java index 386f915e..07c0b29c 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java @@ -17,54 +17,54 @@ @SuppressWarnings("rawtypes") public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; + private static final long serialVersionUID = -5458609797599845837L; + private HttpResponse response = null; - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } + @SuppressWarnings("unchecked") + public Response(HttpResponse response, Map result) { + super(result); + this.response = response; + } - public HttpResponse getRawHttpResponse() { - return this.response; - } + public HttpResponse getRawHttpResponse() { + return this.response; + } - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final Pattern RATE_LIMIT_REGEX = Pattern + .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); - public Map rateLimits() throws 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 Map rateLimits() throws 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 ParseException { - return rateLimits().get("Api"); - } + public RateLimit apiRateLimit() throws ParseException { + return rateLimits().get("Api"); + } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index a0863cb6..4d4f7f4c 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -9,7 +9,7 @@ import org.apache.http.conn.ConnectTimeoutException; public class ApiTest extends AbstractApiTest { - @Test(expected = ConnectTimeoutException.class) + @Test(expected = ConnectTimeoutException.class) public void testTimeoutException() throws Exception { // should allow listing resources Map options = new HashMap(); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index 6b91e540..b4d38375 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -34,121 +34,117 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -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(Cloudinary.USER_AGENT + " 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 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); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); +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(Cloudinary.USER_AGENT + " 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 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); + if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); + if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - 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())); - } - } - - 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); - - 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); - } - } + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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())); + } + } + + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + + 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); + } + } } diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index b4168e0d..d1765c96 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -28,125 +28,126 @@ 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(Cloudinary.USER_AGENT + " 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) throws IOException { - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); - - HttpPost postMethod = new HttpPost(apiUrl); - - 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); - 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) 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(); - } - - if (code != 200 && code != 400 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } - return result; - } + + private CloseableHttpClient client = null; + + @Override + public void init(Uploader uploader) { + super.init(uploader); + + HttpClientBuilder clientBuilder = HttpClients.custom(); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " 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) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + + HttpPost postMethod = new HttpPost(apiUrl); + + 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); + 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) 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(); + } + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("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 (result.containsKey("error")) { + Map error = (Map) result.get("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException((String) error.get("message")); + } + } + return result; + } } 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 index 67029121..14582f50 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java @@ -16,54 +16,54 @@ @SuppressWarnings("rawtypes") public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; + private static final long serialVersionUID = -5458609797599845837L; + private HttpResponse response = null; - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } + @SuppressWarnings("unchecked") + public Response(HttpResponse response, Map result) { + super(result); + this.response = response; + } - public HttpResponse getRawHttpResponse() { - return this.response; - } + public HttpResponse getRawHttpResponse() { + return this.response; + } - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final Pattern RATE_LIMIT_REGEX = Pattern + .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); - 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 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"); - } + public RateLimit apiRateLimit() throws java.text.ParseException { + return rateLimits().get("Api"); + } } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index 315521a5..77c9c308 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -34,121 +34,118 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -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(Cloudinary.USER_AGENT + " 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 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); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); +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(Cloudinary.USER_AGENT + " 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 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); + if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); + if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - 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())); - } - } - - 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); - - 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); - } - } + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + 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())); + } + } + + 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", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + + 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); + } + } } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index ade0e157..0e0b5dfc 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -28,125 +28,126 @@ 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(Cloudinary.USER_AGENT + " 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) throws IOException { - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); - - HttpPost postMethod = new HttpPost(apiUrl); - - 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); - 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) 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(); - } - - if (code != 200 && code != 400 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } - return result; - } + + private CloseableHttpClient client = null; + + @Override + public void init(Uploader uploader) { + super.init(uploader); + + HttpClientBuilder clientBuilder = HttpClients.custom(); + clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " 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) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + + HttpPost postMethod = new HttpPost(apiUrl); + + 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); + 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 && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) 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(); + } + + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("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 (result.containsKey("error")) { + Map error = (Map) result.get("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException((String) error.get("message")); + } + } + 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 index d7b77569..51805353 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java @@ -16,54 +16,54 @@ @SuppressWarnings("rawtypes") public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; + private static final long serialVersionUID = -5458609797599845837L; + private HttpResponse response = null; - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } + @SuppressWarnings("unchecked") + public Response(HttpResponse response, Map result) { + super(result); + this.response = response; + } - public HttpResponse getRawHttpResponse() { - return this.response; - } + public HttpResponse getRawHttpResponse() { + return this.response; + } - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final Pattern RATE_LIMIT_REGEX = Pattern + .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + private static final DateFormat RFC1123 = new SimpleDateFormat( + RFC1123_PATTERN); - 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 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"); - } + public RateLimit apiRateLimit() throws java.text.ParseException { + return rateLimits().get("Api"); + } } From fddc38e9c334f75a3b4ace6576c71ed73548dfbc Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 2 Mar 2016 15:12:16 +0200 Subject: [PATCH 123/592] Whitespace --- .../com/cloudinary/test/UploaderTest.java | 644 +++++++++--------- .../src/main/res/layout/main.xml | 16 +- .../com/cloudinary/android/ApiStrategy.java | 10 +- .../cloudinary/android/MultipartUtility.java | 230 +++---- .../cloudinary/android/UploaderStrategy.java | 194 +++--- .../java/com/cloudinary/android/Utils.java | 26 +- 6 files changed, 558 insertions(+), 562 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 1a9d3745..8a75f2ba 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -27,345 +27,345 @@ public class UploaderTest extends InstrumentationTestCase { public static final String TEST_IMAGE = "images/old_logo.png"; - public static final String TEST_PRESET = "cloudinary_java_test"; - private Cloudinary cloudinary; - private static boolean first = true; - - public void setUp() throws Exception { - String url = Utils.cloudinaryUrlFromContext(getInstrumentation().getContext()); - this.cloudinary = new Cloudinary(url); - if (first) { - first = false; - if (cloudinary.config.apiSecret == null) { - Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); - } - } - } - - protected InputStream getAssetStream(String filename) throws IOException { - return getInstrumentation().getContext().getAssets().open(filename); - } - - public void testUpload() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true))); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - assertNotNull(result.get("colors")); - assertNotNull(result.get("predominant")); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - public void testUnsignedUpload() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), TEST_PRESET, - ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - Log.d("TestRunner",cloudinary.config.apiSecret); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - public void testUploadUrl() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/old_logo.png", ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - public void testUploadDataUri() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject( - cloudinary - .uploader() - .upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", - ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 16L); - assertEquals(result.getLong("height"), 16L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - public void testUploadExternalSignature() throws Exception { - String apiSecret = cloudinary.config.apiSecret; - if (apiSecret == null) - return; - Map config = new HashMap(); - config.put("api_key", cloudinary.config.apiKey); - config.put("cloud_name", cloudinary.config.cloudName); - - Map params = new HashMap(); - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); - Cloudinary emptyCloudinary = new Cloudinary(config); - JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream(TEST_IMAGE), params)); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - public void testRename() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.emptyMap())); - - cloudinary.uploader().rename(result.getString("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); - - JSONObject result2 = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/favicon.ico"), ObjectUtils.emptyMap())); - boolean error_found = false; - try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); - } catch (Exception e) { - error_found = true; - } - assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); - } - - public void testExplicit() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().explicit("cloudinary", - ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name"))); - String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png") - .version(result.get("version")).generate("cloudinary"); - assertEquals(result.getJSONArray("eager").getJSONObject(0).get("url"), url); - } - - public void testEager() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), - ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - } - - public void testHeaders() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", new String[] { "Link: 1" })); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); - } - - public void testText() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().text("hello world", ObjectUtils.emptyMap())); - assertTrue(result.getInt("width") > 1); - assertTrue(result.getInt("height") > 1); - } - - public void testSprite() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - JSONObject result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap())); - assertEquals(2, result.getJSONObject("image_infos").length()); - result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100"))); - assertTrue((result.getString("css_url")).contains("w_100")); - result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", - ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg"))); - assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); - } - - public void testMulti() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - JSONObject result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap())); - assertTrue((result.getString("url")).endsWith(".gif")); - result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100"))); - assertTrue((result.getString("url")).contains("w_100")); - result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf"))); - assertTrue((result.getString("url")).contains("w_111")); - assertTrue((result.getString("url")).endsWith(".pdf")); - } - - public void testUniqueFilename() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - - File f = new File(getInstrumentation().getContext().getCacheDir() + "/old_logo.png"); - - InputStream is = getAssetStream(TEST_IMAGE); - int size = is.available(); - byte[] buffer = new byte[size]; - is.read(buffer); - is.close(); - - FileOutputStream fos = new FileOutputStream(f); - fos.write(buffer); - fos.close(); - - JSONObject result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true))); - assertTrue(result.getString("public_id").matches("old_logo_[a-z0-9]{6}")); - result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true, "unique_filename", false))); - assertEquals(result.getString("public_id"), "old_logo"); - } - - public void testFaceCoordinates() throws Exception { - // should allow sending face coordinates - if (cloudinary.config.apiSecret == null) - return; - Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121, 31, 231, 182); - Rectangle rect2 = new Rectangle(120, 30, 229, 270); - coordinates.addRect(rect1); - coordinates.addRect(rect2); - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); - JSONArray resultFaces = result.getJSONArray("faces"); - assertEquals(2, resultFaces.length()); - - JSONArray resultCoordinates = resultFaces.getJSONArray(0); - - assertEquals(rect1.x, resultCoordinates.getInt(0)); - assertEquals(rect1.y, resultCoordinates.getInt(1)); - assertEquals(rect1.width, resultCoordinates.getInt(2)); - assertEquals(rect1.height, resultCoordinates.getInt(3)); - - resultCoordinates = resultFaces.getJSONArray(1); - - assertEquals(rect2.x, resultCoordinates.getInt(0)); - assertEquals(rect2.y, resultCoordinates.getInt(1)); - assertEquals(rect2.width, resultCoordinates.getInt(2)); - assertEquals(rect2.height, resultCoordinates.getInt(3)); - - } - - public void testContext() throws Exception { - // should allow sending context - if (cloudinary.config.apiSecret == null) - return; - @SuppressWarnings("rawtypes") - Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("context", context)); - } - - public void testModerationRequest() throws Exception { - // should support requesting manual moderation - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("moderation", "manual"))); - assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); - assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); - } - - public void testRawConvertRequest() { - // should support requesting raw conversion - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream("docx.docx"), ObjectUtils.asMap("raw_convert", "illegal", "resource_type", "raw")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); - } - } - - public void testCategorizationRequest() { - // should support requesting categorization - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("categorization", "illegal")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); - } - } - - public void testDetectionRequest() { - // should support requesting detection - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("detection", "illegal")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); - } - } - - public void testAutoTaggingRequest() { - // should support requesting auto tagging - if (cloudinary.config.apiSecret == null) - return; - - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("auto_tagging", 0.5f)); - } catch (Exception e) { - for (int i = 0; i < e.getStackTrace().length; i++) { - StackTraceElement x = e.getStackTrace()[i]; - } - assertTrue(e.getMessage().matches("^Must use(.*)")); - } - } - - @Test - public void testFilenameOption() throws Exception { - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", "emanelif"))); - assertEquals("emanelif", result.getString("original_filename")); - } - - @Test - public void testComplexFilenameOption() throws Exception { - String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); - complexFilename = complexFilename.replace(".png", ""); - - assertEquals(complexFilename, result.getString("original_filename")); - } - - @SuppressWarnings("unchecked") - public void testUploadLarge() throws Exception { - // support uploading large files - if (cloudinary.config.apiSecret == null) - return; - + public static final String TEST_PRESET = "cloudinary_java_test"; + private Cloudinary cloudinary; + private static boolean first = true; + + public void setUp() throws Exception { + String url = Utils.cloudinaryUrlFromContext(getInstrumentation().getContext()); + this.cloudinary = new Cloudinary(url); + if (first) { + first = false; + if (cloudinary.config.apiSecret == null) { + Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); + } + } + } + + protected InputStream getAssetStream(String filename) throws IOException { + return getInstrumentation().getContext().getAssets().open(filename); + } + + public void testUpload() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true))); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + assertNotNull(result.get("colors")); + assertNotNull(result.get("predominant")); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUnsignedUpload() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), TEST_PRESET, + ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + Log.d("TestRunner", cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadUrl() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/old_logo.png", ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadDataUri() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject( + cloudinary + .uploader() + .upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", + ObjectUtils.emptyMap())); + assertEquals(result.getLong("width"), 16L); + assertEquals(result.getLong("height"), 16L); + Map to_sign = new HashMap(); + to_sign.put("public_id", (String) result.get("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testUploadExternalSignature() throws Exception { + String apiSecret = cloudinary.config.apiSecret; + if (apiSecret == null) + return; + Map config = new HashMap(); + config.put("api_key", cloudinary.config.apiKey); + config.put("cloud_name", cloudinary.config.cloudName); + + Map params = new HashMap(); + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); + Cloudinary emptyCloudinary = new Cloudinary(config); + JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream(TEST_IMAGE), params)); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + + public void testRename() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.emptyMap())); + + cloudinary.uploader().rename(result.getString("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); + + JSONObject result2 = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/favicon.ico"), ObjectUtils.emptyMap())); + boolean error_found = false; + try { + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); + } catch (Exception e) { + error_found = true; + } + assertTrue(error_found); + cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); + } + + public void testExplicit() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().explicit("cloudinary", + ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name"))); + String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png") + .version(result.get("version")).generate("cloudinary"); + assertEquals(result.getJSONArray("eager").getJSONObject(0).get("url"), url); + } + + public void testEager() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), + ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + } + + public void testHeaders() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", new String[]{"Link: 1"})); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + } + + public void testText() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().text("hello world", ObjectUtils.emptyMap())); + assertTrue(result.getInt("width") > 1); + assertTrue(result.getInt("height") > 1); + } + + public void testSprite() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); + JSONObject result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap())); + assertEquals(2, result.getJSONObject("image_infos").length()); + result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100"))); + assertTrue((result.getString("css_url")).contains("w_100")); + result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", + ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg"))); + assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); + } + + public void testMulti() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); + JSONObject result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap())); + assertTrue((result.getString("url")).endsWith(".gif")); + result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100"))); + assertTrue((result.getString("url")).contains("w_100")); + result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf"))); + assertTrue((result.getString("url")).contains("w_111")); + assertTrue((result.getString("url")).endsWith(".pdf")); + } + + public void testUniqueFilename() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + + File f = new File(getInstrumentation().getContext().getCacheDir() + "/old_logo.png"); + + InputStream is = getAssetStream(TEST_IMAGE); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + + FileOutputStream fos = new FileOutputStream(f); + fos.write(buffer); + fos.close(); + + JSONObject result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true))); + assertTrue(result.getString("public_id").matches("old_logo_[a-z0-9]{6}")); + result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true, "unique_filename", false))); + assertEquals(result.getString("public_id"), "old_logo"); + } + + public void testFaceCoordinates() throws Exception { + // should allow sending face coordinates + if (cloudinary.config.apiSecret == null) + return; + Coordinates coordinates = new Coordinates(); + Rectangle rect1 = new Rectangle(121, 31, 231, 182); + Rectangle rect2 = new Rectangle(120, 30, 229, 270); + coordinates.addRect(rect1); + coordinates.addRect(rect2); + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); + JSONArray resultFaces = result.getJSONArray("faces"); + assertEquals(2, resultFaces.length()); + + JSONArray resultCoordinates = resultFaces.getJSONArray(0); + + assertEquals(rect1.x, resultCoordinates.getInt(0)); + assertEquals(rect1.y, resultCoordinates.getInt(1)); + assertEquals(rect1.width, resultCoordinates.getInt(2)); + assertEquals(rect1.height, resultCoordinates.getInt(3)); + + resultCoordinates = resultFaces.getJSONArray(1); + + assertEquals(rect2.x, resultCoordinates.getInt(0)); + assertEquals(rect2.y, resultCoordinates.getInt(1)); + assertEquals(rect2.width, resultCoordinates.getInt(2)); + assertEquals(rect2.height, resultCoordinates.getInt(3)); + + } + + public void testContext() throws Exception { + // should allow sending context + if (cloudinary.config.apiSecret == null) + return; + @SuppressWarnings("rawtypes") + Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("context", context)); + } + + public void testModerationRequest() throws Exception { + // should support requesting manual moderation + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("moderation", "manual"))); + assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); + assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); + } + + public void testRawConvertRequest() { + // should support requesting raw conversion + if (cloudinary.config.apiSecret == null) + return; + try { + cloudinary.uploader().upload(getAssetStream("docx.docx"), ObjectUtils.asMap("raw_convert", "illegal", "resource_type", "raw")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testCategorizationRequest() { + // should support requesting categorization + if (cloudinary.config.apiSecret == null) + return; + try { + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testDetectionRequest() { + // should support requesting detection + if (cloudinary.config.apiSecret == null) + return; + try { + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + } + } + + public void testAutoTaggingRequest() { + // should support requesting auto tagging + if (cloudinary.config.apiSecret == null) + return; + + try { + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("auto_tagging", 0.5f)); + } catch (Exception e) { + for (int i = 0; i < e.getStackTrace().length; i++) { + StackTraceElement x = e.getStackTrace()[i]; + } + assertTrue(e.getMessage().matches("^Must use(.*)")); + } + } + + @Test + public void testFilenameOption() throws Exception { + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", "emanelif"))); + assertEquals("emanelif", result.getString("original_filename")); + } + + @Test + public void testComplexFilenameOption() throws Exception { + String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); + complexFilename = complexFilename.replace(".png", ""); + + assertEquals(complexFilename, result.getString("original_filename")); + } + + @SuppressWarnings("unchecked") + public void testUploadLarge() throws Exception { + // support uploading large files + if (cloudinary.config.apiSecret == null) + return; + File temp = File.createTempFile("cldupload.test.", ""); FileOutputStream out = new FileOutputStream(temp); - int[] header = new int[]{0x42,0x4D,0x4A,0xB9,0x59,0x00,0x00,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x05,0x00,0x00,0x78,0x05,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0xC0,0xB8,0x59,0x00,0x61,0x0F,0x00,0x00,0x61,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xB8,0x1E,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC4,0xF5,0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + int[] header = new int[]{0x42, 0x4D, 0x4A, 0xB9, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB8, 0x59, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xB8, 0x1E, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF5, 0x28, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; byte[] byteHeader = new byte[138]; for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; byte[] piece = new byte[10]; Arrays.fill(piece, (byte) 0xff); out.write(byteHeader); for (int i = 1; i <= 588000; i++) { - out.write(piece); + out.write(piece); } out.close(); assertEquals(5880138, temp.length()); - + JSONObject resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000))); assertEquals("raw", resource.getString("resource_type")); - + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5243000))); assertEquals("image", resource.getString("resource_type")); assertEquals(1400L, resource.getLong("width")); assertEquals(1400L, resource.getLong("height")); - + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138))); assertEquals("image", resource.getString("resource_type")); assertEquals(1400L, resource.getLong("width")); diff --git a/cloudinary-android-test/src/main/res/layout/main.xml b/cloudinary-android-test/src/main/res/layout/main.xml index 3a5f117d..a70bd1d5 100644 --- a/cloudinary-android-test/src/main/res/layout/main.xml +++ b/cloudinary-android-test/src/main/res/layout/main.xml @@ -1,12 +1,12 @@ - + diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java index 8cb0cf09..a3c3420f 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java @@ -8,10 +8,10 @@ public class ApiStrategy extends AbstractApiStrategy { - @SuppressWarnings("rawtypes") - @Override - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - throw new Exception("Administration API is not supported for mobile applications."); - } + @SuppressWarnings("rawtypes") + @Override + public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + throw new Exception("Administration API is not supported for mobile applications."); + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 8d344ba3..a652084d 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -17,126 +17,122 @@ /** * This utility class provides an abstraction layer for sending multipart HTTP * POST requests to a web server. - * + * * @author www.codejava.net * @author Cloudinary */ public class MultipartUtility { - private final String boundary; - private static final String LINE_FEED = "\r\n"; - private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; - private HttpURLConnection httpConn; - private String charset; - private OutputStream outputStream; - private PrintWriter writer; - - public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; - - - /** - * This constructor initializes a new HTTP POST request with content type is - * set to multipart/form-data - * - * @param requestURL - * @param charset - * @throws IOException - */ - public MultipartUtility(String requestURL, String charset, String boundary, Map headers) throws IOException { - this.charset = charset; - this.boundary = boundary; - - URL url = new URL(requestURL); - httpConn = (HttpURLConnection) url.openConnection(); - httpConn.setDoOutput(true); // indicates POST method - httpConn.setDoInput(true); - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - httpConn.setRequestProperty(header.getKey(), header.getValue()); - } - } - httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); - httpConn.setRequestProperty("User-Agent", USER_AGENT); - outputStream = httpConn.getOutputStream(); - writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); - } - - public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { - this(requestURL, charset, boundary, null); - } - - /** - * Adds a form field to the request - * - * @param name - * field name - * @param value - * field value - */ - public void addFormField(String name, String value) { - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); - writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); - writer.append(LINE_FEED); - writer.append(value).append(LINE_FEED); - writer.flush(); - } - - /** - * Adds a upload file section to the request - * - * @param fieldName - * name attribute in {@code } - * @param uploadFile - * a File to be uploaded - * @throws IOException - */ - public void addFilePart(String fieldName, File uploadFile, String fileName) throws IOException { - if (fileName == null) fileName = uploadFile.getName(); - FileInputStream inputStream = new FileInputStream(uploadFile); - addFilePart(fieldName, inputStream, fileName); - } - - public void addFilePart(String fieldName, File uploadFile) throws IOException { - addFilePart(fieldName, uploadFile, "file"); - } - - public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { - if (fileName == null) fileName = "file"; - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); - writer.append("Content-Type: ").append(APPLICATION_OCTET_STREAM).append(LINE_FEED); - writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); - writer.append(LINE_FEED); - writer.flush(); - - byte[] buffer = new byte[4096]; - int bytesRead = -1; - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - outputStream.flush(); - inputStream.close(); - - writer.append(LINE_FEED); - writer.flush(); - } - - public void addFilePart(String fieldName, InputStream inputStream) throws IOException { - addFilePart(fieldName, inputStream, "file"); - } - - /** - * Completes the request and receives response from the server. - * - * @return a list of Strings as response in case the server returned status - * OK, otherwise an exception is thrown. - * @throws IOException - */ - public HttpURLConnection execute() throws IOException { - writer.append("--" + boundary + "--").append(LINE_FEED); - writer.close(); - - return httpConn; - } + private final String boundary; + private static final String LINE_FEED = "\r\n"; + private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; + private HttpURLConnection httpConn; + private String charset; + private OutputStream outputStream; + private PrintWriter writer; + + public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; + + + /** + * This constructor initializes a new HTTP POST request with content type is + * set to multipart/form-data + * + * @param requestURL + * @param charset + * @throws IOException + */ + public MultipartUtility(String requestURL, String charset, String boundary, Map headers) throws IOException { + this.charset = charset; + this.boundary = boundary; + + URL url = new URL(requestURL); + httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setDoOutput(true); // indicates POST method + httpConn.setDoInput(true); + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + httpConn.setRequestProperty(header.getKey(), header.getValue()); + } + } + httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + httpConn.setRequestProperty("User-Agent", USER_AGENT); + outputStream = httpConn.getOutputStream(); + writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); + } + + public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { + this(requestURL, charset, boundary, null); + } + + /** + * Adds a form field to the request + * + * @param name field name + * @param value field value + */ + public void addFormField(String name, String value) { + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); + writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); + writer.append(LINE_FEED); + writer.append(value).append(LINE_FEED); + writer.flush(); + } + + /** + * Adds a upload file section to the request + * + * @param fieldName name attribute in {@code } + * @param uploadFile a File to be uploaded + * @throws IOException + */ + public void addFilePart(String fieldName, File uploadFile, String fileName) throws IOException { + if (fileName == null) fileName = uploadFile.getName(); + FileInputStream inputStream = new FileInputStream(uploadFile); + addFilePart(fieldName, inputStream, fileName); + } + + public void addFilePart(String fieldName, File uploadFile) throws IOException { + addFilePart(fieldName, uploadFile, "file"); + } + + public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { + if (fileName == null) fileName = "file"; + writer.append("--" + boundary).append(LINE_FEED); + writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); + writer.append("Content-Type: ").append(APPLICATION_OCTET_STREAM).append(LINE_FEED); + writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); + writer.append(LINE_FEED); + writer.flush(); + + byte[] buffer = new byte[4096]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + inputStream.close(); + + writer.append(LINE_FEED); + writer.flush(); + } + + public void addFilePart(String fieldName, InputStream inputStream) throws IOException { + addFilePart(fieldName, inputStream, "file"); + } + + /** + * Completes the request and receives response from the server. + * + * @return a list of Strings as response in case the server returned status + * OK, otherwise an exception is thrown. + * @throws IOException + */ + public HttpURLConnection execute() throws IOException { + writer.append("--" + boundary + "--").append(LINE_FEED); + writer.close(); + + return httpConn; + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 0ddc14fc..6a50c0de 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -18,107 +18,107 @@ public class UploaderStrategy extends AbstractUploaderStrategy { - @SuppressWarnings("rawtypes") - @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + @SuppressWarnings("rawtypes") + @Override + public Map callApi(String action, Map params, Map options, Object file) throws IOException { + // initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - if (Boolean.TRUE.equals(options.get("unsigned"))) { - // Nothing to do - } else { - String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - if (options.containsKey("signature") && options.containsKey("timestamp")) { - params.put("timestamp", options.get("timestamp")); - params.put("signature", options.get("signature")); - params.put("api_key", apiKey); - } else { - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); - } - } - String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); + if (Boolean.TRUE.equals(options.get("unsigned"))) { + // Nothing to do + } else { + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); + if (apiKey == null) + throw new IllegalArgumentException("Must supply api_key"); + if (options.containsKey("signature") && options.containsKey("timestamp")) { + params.put("timestamp", options.get("timestamp")); + params.put("signature", options.get("signature")); + params.put("api_key", apiKey); + } else { + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); + if (apiSecret == null) + throw new IllegalArgumentException("Must supply api_secret"); + params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); + params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); + params.put("api_key", apiKey); + } + } + String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); - } - } else { - if (StringUtils.isNotBlank(param.getValue())) { - multipart.addFormField(param.getKey(), param.getValue().toString()); - } - } - } + // Remove blank parameters + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof Collection) { + for (Object value : (Collection) param.getValue()) { + multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); + } + } else { + if (StringUtils.isNotBlank(param.getValue())) { + multipart.addFormField(param.getKey(), param.getValue().toString()); + } + } + } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - multipart.addFilePart("file", (File) file, filename); - } else if (file instanceof String) { - multipart.addFormField("file", (String) file); - } else if (file instanceof InputStream) { - multipart.addFilePart("file", (InputStream) file, filename); - } else if (file instanceof byte[]) { - multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); - } - HttpURLConnection connection = multipart.execute(); - int code; - try { - code = connection.getResponseCode(); - } catch (IOException e) { - if (e.getMessage().equals("No authentication challenges found")) { - // Android trying to be clever... - code = 401; - } else { - throw e; - } - } - InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); - String responseData = readFully(responseStream); - connection.disconnect(); + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) file); + } + String filename = (String) options.get("filename"); + if (file instanceof File) { + multipart.addFilePart("file", (File) file, filename); + } else if (file instanceof String) { + multipart.addFormField("file", (String) file); + } else if (file instanceof InputStream) { + multipart.addFilePart("file", (InputStream) file, filename); + } else if (file instanceof byte[]) { + multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); + } + HttpURLConnection connection = multipart.execute(); + int code; + try { + code = connection.getResponseCode(); + } catch (IOException e) { + if (e.getMessage().equals("No authentication challenges found")) { + // Android trying to be clever... + code = 401; + } else { + throw e; + } + } + InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); + String responseData = readFully(responseStream); + connection.disconnect(); - if (code != 200 && code != 400 && code != 500) { - throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); - } + if (code != 200 && code != 400 && code != 500) { + throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); + } - JSONObject result; - try { - result = new JSONObject(responseData); - if (result.has("error")) { - JSONObject error = result.getJSONObject("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException(error.getString("message")); - } - } - return ObjectUtils.toMap(result); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } - } + JSONObject result; + try { + result = new JSONObject(responseData); + if (result.has("error")) { + JSONObject error = result.getJSONObject("error"); + if (returnError) { + error.put("http_code", code); + } else { + throw new RuntimeException(error.getString("message")); + } + } + return ObjectUtils.toMap(result); + } catch (JSONException e) { + throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); + } + } - protected static String readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length = 0; - while ((length = in.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - return new String(baos.toByteArray()); - } + protected static String readFully(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = in.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return new String(baos.toByteArray()); + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java b/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java index 28e9b956..45ec07a8 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java @@ -6,17 +6,17 @@ import android.content.pm.PackageManager.NameNotFoundException; public class Utils { - public static String cloudinaryUrlFromContext(Context context){ - String url=""; - try { - PackageManager packageManager = context.getPackageManager(); - ApplicationInfo info = packageManager.getApplicationInfo( context.getPackageName(), PackageManager.GET_META_DATA); - if (info != null && info.metaData != null) { - url = (String) info.metaData.get("CLOUDINARY_URL"); - } - } catch (NameNotFoundException e) { - // No metadata found - } - return url; - } + public static String cloudinaryUrlFromContext(Context context) { + String url = ""; + try { + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo info = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); + if (info != null && info.metaData != null) { + url = (String) info.metaData.get("CLOUDINARY_URL"); + } + } catch (NameNotFoundException e) { + // No metadata found + } + return url; + } } From c320d5842d2af4240ff62c7f94308e2a5d4dec92 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 3 Mar 2016 12:54:56 +0200 Subject: [PATCH 124/592] Whitespace --- .../main/java/com/cloudinary/Singleton.java | 14 +- .../java/com/cloudinary/SingletonManager.java | 6 +- .../main/resources/META-INF/cloudinary.tld | 28 +- .../com/cloudinary/test/AbstractApiTest.java | 1290 ++++++++--------- .../cloudinary/test/AbstractUploaderTest.java | 437 +++--- 5 files changed, 890 insertions(+), 885 deletions(-) diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java b/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java index 43453b9b..a3767492 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java @@ -1,23 +1,23 @@ package com.cloudinary; -/** This class contains a singleton in a generic way. This class is used by the tags to +/** + * This class contains a singleton in a generic way. This class is used by the tags to * retrieve the Cloudinary configuration. - * + *

* the containing framework is responsible for registering the cloudinary configuration with the * Singleton, and then removing it on shutdown. This allows the user to use Spring or any other * framework without imposing additional dependencies on the cloudinary project. - * - * @author jpollak * + * @author jpollak */ public class Singleton { private static Cloudinary cloudinary; - + public static void registerCloudinary(Cloudinary cloudinary) { Singleton.cloudinary = cloudinary; } - + public static void deregisterCloudinary() { cloudinary = null; } @@ -25,7 +25,7 @@ public static void deregisterCloudinary() { private static class DefaultCloudinaryHolder { public static final Cloudinary INSTANCE = new Cloudinary(); } - + public static Cloudinary getCloudinary() { if (cloudinary == null) { return DefaultCloudinaryHolder.INSTANCE; diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/SingletonManager.java b/cloudinary-taglib/src/main/java/com/cloudinary/SingletonManager.java index 0b1c1248..5983bb84 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/SingletonManager.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/SingletonManager.java @@ -3,15 +3,15 @@ public class SingletonManager { private Cloudinary cloudinary; - + public void setCloudinary(Cloudinary cloudinary) { this.cloudinary = cloudinary; } - + public void init() { Singleton.registerCloudinary(cloudinary); } - + public void destroy() { Singleton.deregisterCloudinary(); } diff --git a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld index 8a8f260a..0370c3bf 100644 --- a/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld +++ b/cloudinary-taglib/src/main/resources/META-INF/cloudinary.tld @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" + version="2.0"> 1.1 2.0 Cloudinary Taglib @@ -341,7 +341,7 @@ false true - + signed false true @@ -412,7 +412,7 @@ false true - + signed false true @@ -514,19 +514,19 @@ true - urlSuffix - false - true + urlSuffix + false + true - secureCdnSubdomain - false - true + secureCdnSubdomain + false + true - useRootPath - false - true + useRootPath + false + true true 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 4c9bb2f2..c4dba3c7 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 @@ -34,88 +34,89 @@ import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.utils.ObjectUtils; -@SuppressWarnings({ "rawtypes", "unchecked" }) +@SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractApiTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; private Cloudinary cloudinary; - protected Api api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); - - @BeforeClass - public static void setUpClass() throws IOException { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - return; - } - Api api = cloudinary.api(); - try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - try { - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - } - Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[] { "api_test_tag", uniqueTag }, "context", "key=value", "eager", - Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options.put("public_id", "api_test1"); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - } - - @Rule public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.config.apiSecret); - this.api = cloudinary.api(); - - - } - - public Map findByAttr(List elements, String attr, Object value) { - for (Map element : elements) { - if (value.equals(element.get(attr))) { - return element; - } - } - return null; - } - - @Test - public void test01ResourceTypes() throws Exception { - // should allow listing resource_types - Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); - } + protected Api api; + private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + return; + } + Api api = cloudinary.api(); + try { + api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + try { + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + } + Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[]{"api_test_tag", uniqueTag}, "context", "key=value", "eager", + Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options.put("public_id", "api_test1"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + + + } + + public Map findByAttr(List elements, String attr, Object value) { + for (Map element : elements) { + if (value.equals(element.get(attr))) { + return element; + } + } + return null; + } + + @Test + public void test01ResourceTypes() throws Exception { + // should allow listing resource_types + Map result = api.resourceTypes(ObjectUtils.emptyMap()); + assertContains("image", (Collection) result.get("resource_types")); + } @Test public void test02Resources() throws Exception { @@ -138,571 +139,570 @@ public void testTimeoutParameter() throws Exception { } @Test - public void test03ResourcesCursor() throws Exception { - // should allow listing resources with cursor - Map options = new HashMap(); - options.put("max_results", 1); - Map result = api.resources(options); - List resources = (List) result.get("resources"); - assertNotNull(resources); - assertEquals(1, resources.size()); - assertNotNull(result.get("next_cursor")); - - options.put("next_cursor", result.get("next_cursor")); - Map result2 = api.resources(options); - List resources2 = (List) result2.get("resources"); - assertNotNull(resources2); - assertEquals(resources2.size(), 1); - assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void test04ResourcesByType() throws Exception { - // should allow listing resources by type - Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - } - - @Test - public void test05ResourcesByPrefix() throws Exception { - // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void testResourcesListingDirection() throws Exception { - // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); - List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); - List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); - } - - @Ignore - public void testResourcesListingStartAt() throws Exception { - // should allow listing resources by start date - make sure your clock - // is set correctly!!! - Thread.sleep(2000L); - java.util.Date startAt = new java.util.Date(); - Thread.sleep(2000L); - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); - List resources = (List) listResources.get("resources"); - assertEquals(response.get("public_id"), resources.get(0).get("public_id")); - } - - @Test - public void testResourcesByPublicIds() throws Exception { - // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test06ResourcesTag() throws Exception { - // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - assertNotNull(resource); - resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); - assertNotNull(resource); - List resources = (List) result.get("resources"); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); - } - assertTrue(found); - } - - @Test - public void test07ResourceMetadata() throws Exception { - // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); - assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); - assertEquals(resource.get("bytes"), 3381); - assertEquals(((List) resource.get("derived")).size(), 1); - } - - @Test - public void test08DeleteDerived() throws Exception { - // should allow deleting derived resource - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - List derived = (List) resource.get("derived"); - assertEquals(derived.size(), 1); - String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - derived = (List) resource.get("derived"); - assertEquals(derived.size(), 0); - } - - @Test(expected = NotFound.class) - public void test09DeleteResources() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByPrefix() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test09aDeleteResourcesByTags() throws Exception { - // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); - assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); - } - - @Test - public void test10Tags() throws Exception { - // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - } - - @Test - public void test11TagsPrefix() throws Exception { - // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); - List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); - result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); - tags = (List) result.get("tags"); - assertEquals(0, tags.size()); - } - - @Test - public void test12Transformations() throws Exception { - // should allow listing transformations - Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); - - assertNotNull(transformation); - assertTrue((Boolean) transformation.get("used")); - } - - @Test - public void test13TransformationMetadata() throws Exception { - // should allow getting transformation metadata - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); - } - - @Test - public void test14TransformationUpdate() throws Exception { - // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), false); - } - - @Test - public void test15TransformationCreate() throws Exception { - // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(transformation.get("allowed_for_strict"), true); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test15aTransformationUnsafeUpdate() throws Exception { - // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), - ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); - assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); - assertEquals(transformation.get("used"), false); - } - - @Test - public void test16aTransformationDelete() throws Exception { - // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - } - - @Test - public void test17aTransformationDeleteImplicit() throws Exception { - // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ - @Test(expected = NotFound.class) - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - } - - @Test - public void test18Usage() throws Exception { - // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); - assertNotNull(result.get("last_updated")); - } - - @Test - public void test19Ping() throws Exception { - // should support ping API call - Map result = api.ping(ObjectUtils.emptyMap()); - assertEquals(result.get("status"), "ok"); - } - - // This test must be last because it deletes (potentially) all dependent - // transformations which some tests rely on. - // Add @Test if you really want to test it - This test deletes derived - // resources! - public void testDeleteAllResources() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); - assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); - api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); - // assertEquals(0, ((org.cloudinary.json.JSONArray) - // result.get("derived")).size()); - } - - @Test - public void testManualModeration() throws Exception { - // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); - } - - @Test - public void testOcrUpdate() { - // should support requesting ocr info - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testRawConvertUpdate() { - // should support requesting raw conversion - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testCategorizationUpdate() { - // should support requesting categorization - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testDetectionUpdate() { - // should support requesting detection - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testSimilaritySearchUpdate() { - // should support requesting similarity search - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - - @Test - public void testUpdateCustomCoordinates() throws IOException, Exception { - // should update custom coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - int[] expected = new int[] { 121, 31, 110, 151}; - ArrayList actual = (ArrayList) ((ArrayList)((Map) result.get("coordinates")).get("custom")).get(0); - for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i], actual.get(i)); - } - } - - @Test - public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response - // header - ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); - ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); - assertNotNull(result1.apiRateLimit()); - assertNotNull(result2.apiRateLimit()); - assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); - assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); - assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); - assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); - assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); - } - - @Test - public void testListUploadPresets() throws Exception { - // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); - - ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); - assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } - - @Test - public void testGetUploadPreset() throws Exception { - // should allow getting a single upload_preset - String[] tags = { "a", "b", "c" }; - Map context = ObjectUtils.asMap("a", "b", "c", "d"); - Transformation transformation = new Transformation(); - transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", - context)); - 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"); - Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); - assertEquals(outTransformation.get("width"), 100); - assertEquals(outTransformation.get("crop"), "scale"); - Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); - assertArrayEquals(tags, outTags); - Map outContext = (Map) settings.get("context"); - assertEquals(context, outContext); - } - - @Test - public void testDeleteUploadPreset() throws Exception { - // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - boolean error = false; - try { - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { - error = true; - } - assertTrue(error); - } - - @Test - public void testUpdateUploadPreset() throws Exception { - // should allow updating upload_presets - 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)); - 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, preset.get("settings")); - api.deleteUploadPreset(name, ObjectUtils.emptyMap()); - } - - @Test - public void testListByModerationUpdate() throws Exception { - // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); - api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); - Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); - Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); - Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); - } - - // For this test to work, "Auto-create folders" should be enabled in the - // Upload Settings. - // Uncomment @Test if you really want to test it. - // @Test - public void testFolderApi() throws Exception { - // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); - Map result = api.rootFolders(null); - assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); - assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); - result = api.subFolders("test_folder1", null); - assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); - assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); - try { - api.subFolders("test_folder", null); - } catch (Exception e) { - assertTrue(e instanceof NotFound); - } - api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); - } - - @Test - public void testRestore() throws Exception { - // should support restoring resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test_restore", "backup", true)); - Map resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); - assertEquals(resource.get("bytes"), 3381); - api.deleteResources(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); - resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); - assertEquals(resource.get("bytes"), 0); - assertTrue((Boolean) resource.get("placeholder")); - Map response = api.restore(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); - Map info = (Map) response.get("api_test_restore"); - assertNotNull(info); - assertEquals(info.get("bytes"), 3381); - resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); - assertEquals(resource.get("bytes"), 3381); - } - - @Test - public void testUploadMapping() throws Exception { - try { - api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); - } catch (Exception e) { - - } - api.createUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://cloudinary.com")); - Map result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); - assertEquals(result.get("template"), "http://cloudinary.com"); - api.updateUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://res.cloudinary.com")); - result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); - assertEquals(result.get("template"), "http://res.cloudinary.com"); - result = api.uploadMappings(ObjectUtils.emptyMap()); - ListIterator mappings = ((ArrayList) result.get("mappings")).listIterator(); - boolean found = false; - while (mappings.hasNext()) { - Map mapping = (Map) mappings.next(); - if (mapping.get("folder").equals("api_test_upload_mapping") - && mapping.get("template").equals("http://res.cloudinary.com")) { - found = true; - break; - } - } - assertTrue(found); - api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); - result = api.uploadMappings(ObjectUtils.emptyMap()); - found = false; - while (mappings.hasNext()) { - Map mapping = (Map) mappings.next(); - if (mapping.get("folder").equals("api_test_upload_mapping") - && mapping.get("template").equals("http://res.cloudinary.com")) { - found = true; - break; - } - } - assertTrue(!found); - } - - - - private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } + public void test03ResourcesCursor() throws Exception { + // should allow listing resources with cursor + Map options = new HashMap(); + options.put("max_results", 1); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + assertEquals(1, resources.size()); + assertNotNull(result.get("next_cursor")); + + options.put("next_cursor", result.get("next_cursor")); + Map result2 = api.resources(options); + List resources2 = (List) result2.get("resources"); + assertNotNull(resources2); + assertEquals(resources2.size(), 1); + assertNotSame(resources2.get(0).get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void test04ResourcesByType() throws Exception { + // should allow listing resources by type + Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + } + + @Test + public void test05ResourcesByPrefix() throws Exception { + // should allow listing resources by prefix + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void testResourcesListingDirection() throws Exception { + // should allow listing resources in both directions + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + List resources = (List) result.get("resources"); + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + List resourcesDesc = (List) result.get("resources"); + Collections.reverse(resources); + assertEquals(resources, resourcesDesc); + } + + @Ignore + public void testResourcesListingStartAt() throws Exception { + // should allow listing resources by start date - make sure your clock + // is set correctly!!! + Thread.sleep(2000L); + java.util.Date startAt = new java.util.Date(); + Thread.sleep(2000L); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); + List resources = (List) listResources.get("resources"); + assertEquals(response.get("public_id"), resources.get(0).get("public_id")); + } + + @Test + public void testResourcesByPublicIds() throws Exception { + // should allow listing resources by public ids + Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", "api_test")); + assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test06ResourcesTag() throws Exception { + // should allow listing resources by tag + Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + assertNotNull(resource); + resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); + assertNotNull(resource); + List resources = (List) result.get("resources"); + boolean found = false; + for (Map r : resources) { + ArrayList tags = (ArrayList) r.get("tags"); + found = found || tags.contains("api_test_tag"); + } + assertTrue(found); + } + + @Test + public void test07ResourceMetadata() throws Exception { + // should allow get resource metadata + Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + assertNotNull(resource); + assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("bytes"), 3381); + assertEquals(((List) resource.get("derived")).size(), 1); + } + + @Test + public void test08DeleteDerived() throws Exception { + // should allow deleting derived resource + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = (List) resource.get("derived"); + assertEquals(derived.size(), 1); + String derived_resource_id = (String) derived.get(0).get("id"); + api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = (List) resource.get("derived"); + assertEquals(derived.size(), 0); + } + + @Test(expected = NotFound.class) + public void test09DeleteResources() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); + Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); + api.resource("api_test3", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByPrefix() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); + Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); + api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test09aDeleteResourcesByTags() throws Exception { + // should allow deleting resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); + Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + assertNotNull(resource); + api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.resource("api_test4", ObjectUtils.emptyMap()); + } + + @Test + public void test10Tags() throws Exception { + // should allow listing tags + Map result = api.tags(ObjectUtils.emptyMap()); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + } + + @Test + public void test11TagsPrefix() throws Exception { + // should allow listing tag by prefix + Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + List tags = (List) result.get("tags"); + assertContains("api_test_tag", tags); + result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); + tags = (List) result.get("tags"); + assertEquals(0, tags.size()); + } + + @Test + public void test12Transformations() throws Exception { + // should allow listing transformations + Map result = api.transformations(ObjectUtils.emptyMap()); + Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + + assertNotNull(transformation); + assertTrue((Boolean) transformation.get("used")); + } + + @Test + public void test13TransformationMetadata() throws Exception { + // should allow getting transformation metadata + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(100).generate()); + } + + @Test + public void test14TransformationUpdate() throws Exception { + // should allow updating transformation allowed_for_strict + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), false); + } + + @Test + public void test15TransformationCreate() throws Exception { + // should allow creating named transformation + api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(transformation.get("allowed_for_strict"), true); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test15aTransformationUnsafeUpdate() throws Exception { + // should allow unsafe update of named transformation + api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), + ObjectUtils.emptyMap()); + Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + assertNotNull(transformation); + assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); + assertEquals(transformation.get("used"), false); + } + + @Test + public void test16aTransformationDelete() throws Exception { + // should allow deleting named transformation + api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test(expected = NotFound.class) + public void test16bTransformationDelete() throws Exception { + api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + } + + @Test + public void test17aTransformationDeleteImplicit() throws Exception { + // should allow deleting implicit transformation + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + /** + * @throws Exception + * @expectedException \Cloudinary\Api\NotFound + */ + @Test(expected = NotFound.class) + public void test17bTransformationDeleteImplicit() throws Exception { + api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + } + + @Test + public void test18Usage() throws Exception { + // should support usage API call + Map result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + @Test + public void test19Ping() throws Exception { + // should support ping API call + Map result = api.ping(ObjectUtils.emptyMap()); + assertEquals(result.get("status"), "ok"); + } + + // This test must be last because it deletes (potentially) all dependent + // transformations which some tests rely on. + // Add @Test if you really want to test it - This test deletes derived + // resources! + public void testDeleteAllResources() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); + api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); + result = api.resource("api_test5", ObjectUtils.emptyMap()); + // assertEquals(0, ((org.cloudinary.json.JSONArray) + // result.get("derived")).size()); + } + + @Test + public void testManualModeration() throws Exception { + // should support setting manual moderation status + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); + } + + @Test + public void testOcrUpdate() { + // should support requesting ocr info + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testRawConvertUpdate() { + // should support requesting raw conversion + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testCategorizationUpdate() { + // should support requesting categorization + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testDetectionUpdate() { + // should support requesting detection + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testSimilaritySearchUpdate() { + // should support requesting similarity search + try { + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); + } catch (Exception e) { + assertTrue(e instanceof BadRequest); + assertTrue(e.getMessage().matches("^Illegal value(.*)")); + } + } + + @Test + public void testUpdateCustomCoordinates() throws IOException, Exception { + // should update custom coordinates + Coordinates coordinates = new Coordinates("121,31,110,151"); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + int[] expected = new int[]{121, 31, 110, 151}; + ArrayList actual = (ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual.get(i)); + } + } + + @Test + public void testApiLimits() throws Exception { + // should support reporting the current API limits found in the response + // header + ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); + ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); + assertNotNull(result1.apiRateLimit()); + assertNotNull(result2.apiRateLimit()); + assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); + assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); + assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); + assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); + assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); + } + + @Test + public void testListUploadPresets() throws Exception { + // should allow creating and listing upload_presets + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); + + ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); + assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); + assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); + assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); + api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + } + + @Test + public void testGetUploadPreset() throws Exception { + // should allow getting a single upload_preset + String[] tags = {"a", "b", "c"}; + Map context = ObjectUtils.asMap("a", "b", "c", "d"); + Transformation transformation = new Transformation(); + transformation.width(100).crop("scale"); + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", + context)); + 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"); + Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); + assertEquals(outTransformation.get("width"), 100); + assertEquals(outTransformation.get("crop"), "scale"); + Object[] outTags = ((java.util.ArrayList) settings.get("tags")).toArray(); + assertArrayEquals(tags, outTags); + Map outContext = (Map) settings.get("context"); + assertEquals(context, outContext); + } + + @Test + public void testDeleteUploadPreset() throws Exception { + // should allow deleting upload_presets", :upload_preset => true do + api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + boolean error = false; + try { + api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + } catch (Exception e) { + error = true; + } + assertTrue(error); + } + + @Test + public void testUpdateUploadPreset() throws Exception { + // should allow updating upload_presets + 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)); + 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, preset.get("settings")); + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); + } + + @Test + public void testListByModerationUpdate() throws Exception { + // "should support listing by moderation kind and value + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); + Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); + Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); + Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); + assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); + assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); + } + + // For this test to work, "Auto-create folders" should be enabled in the + // Upload Settings. + // Uncomment @Test if you really want to test it. + // @Test + public void testFolderApi() throws Exception { + // should allow deleting all resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + Map result = api.rootFolders(null); + assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); + assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); + result = api.subFolders("test_folder1", null); + assertEquals("test_folder1/test_subfolder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("path")); + assertEquals("test_folder1/test_subfolder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("path")); + try { + api.subFolders("test_folder", null); + } catch (Exception e) { + assertTrue(e instanceof NotFound); + } + api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); + } + + @Test + public void testRestore() throws Exception { + // should support restoring resources + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", "api_test_restore", "backup", true)); + Map resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + api.deleteResources(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 0); + assertTrue((Boolean) resource.get("placeholder")); + Map response = api.restore(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + Map info = (Map) response.get("api_test_restore"); + assertNotNull(info); + assertEquals(info.get("bytes"), 3381); + resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + } + + @Test + public void testUploadMapping() throws Exception { + try { + api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + } catch (Exception e) { + + } + api.createUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://cloudinary.com")); + Map result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + assertEquals(result.get("template"), "http://cloudinary.com"); + api.updateUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://res.cloudinary.com")); + result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + assertEquals(result.get("template"), "http://res.cloudinary.com"); + result = api.uploadMappings(ObjectUtils.emptyMap()); + ListIterator mappings = ((ArrayList) result.get("mappings")).listIterator(); + boolean found = false; + while (mappings.hasNext()) { + Map mapping = (Map) mappings.next(); + if (mapping.get("folder").equals("api_test_upload_mapping") + && mapping.get("template").equals("http://res.cloudinary.com")) { + found = true; + break; + } + } + assertTrue(found); + api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + result = api.uploadMappings(ObjectUtils.emptyMap()); + found = false; + while (mappings.hasNext()) { + Map mapping = (Map) mappings.next(); + if (mapping.get("folder").equals("api_test_upload_mapping") + && mapping.get("template").equals("http://res.cloudinary.com")) { + found = true; + break; + } + } + assertTrue(!found); + } + + + private void assertContains(Object object, Collection list) { + assertTrue(list.contains(object)); + } } 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 7a497b83..a5fd9bec 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 @@ -4,6 +4,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -36,42 +37,45 @@ abstract public class AbstractUploaderTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; - private static final String ARCHIVE_TAG = "archive_tag_" + String.valueOf(System.currentTimeMillis()); + private static final String ARCHIVE_TAG = "archive_tag_" + String.valueOf(System.currentTimeMillis()); + public static final int SRC_TEST_IMAGE_W = 241; + public static final int SRC_TEST_IMAGE_H = 51; private Cloudinary cloudinary; - @BeforeClass - public static void setUpClass() throws IOException { - Cloudinary cloudinary = new Cloudinary(); - if (cloudinary.config.apiSecret == null) { - System.err.println("Please setup environment for Upload test to run"); - } - - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", ARCHIVE_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("tags", ARCHIVE_TAG, - "transformation", new Transformation().crop("scale").width(10))); - } - - @AfterClass - public static void tearDownClass() throws Exception { - Cloudinary cloudinary = new Cloudinary(); - cloudinary.api().deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); - } - - @Rule public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " +this.getClass().getName()+"."+ currentTest.getMethodName()); - this.cloudinary = new Cloudinary(); - assumeNotNull(cloudinary.config.apiSecret); + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", ARCHIVE_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("tags", ARCHIVE_TAG, + "transformation", new Transformation().crop("scale").width(10))); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Cloudinary cloudinary = new Cloudinary(); + cloudinary.api().deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); } @Test - public void testUpload() throws IOException { + public void testUpload() throws IOException { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true)); - assertEquals(result.get("width"), 241); - assertEquals(result.get("height"), 51); + assertEquals(result.get("width"), SRC_TEST_IMAGE_W); + assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); assertNotNull(result.get("predominant")); Map to_sign = new HashMap(); @@ -82,10 +86,10 @@ public void testUpload() throws IOException { } @Test - public void testUploadUrl() throws IOException { + public void testUploadUrl() throws IOException { Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.emptyMap()); - assertEquals(result.get("width"), 241); - assertEquals(result.get("height"), 51); + assertEquals(result.get("width"), SRC_TEST_IMAGE_W); + assertEquals(result.get("height"), SRC_TEST_IMAGE_H); Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); @@ -94,7 +98,7 @@ public void testUploadUrl() throws IOException { } @Test - public void testUploadDataUri() throws IOException { + public void testUploadDataUri() throws IOException { Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); assertEquals(result.get("width"), 16); assertEquals(result.get("height"), 16); @@ -104,7 +108,7 @@ public void testUploadDataUri() throws IOException { String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); assertEquals(result.get("signature"), expected_signature); } - + @Test public void testUploadUTF8() throws IOException { Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); @@ -113,7 +117,7 @@ public void testUploadUTF8() throws IOException { } @Test - public void testRename() throws Exception { + public void testRename() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); Object publicId = result.get("public_id"); @@ -134,34 +138,35 @@ public void testRename() throws Exception { } @Test - public void testUniqueFilename() throws Exception { + public void testUniqueFilename() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); - assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); + assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); - assertEquals(result.get("public_id"), "old_logo"); - } + assertEquals(result.get("public_id"), "old_logo"); + } + @Test - public void testExplicit() throws IOException { + public void testExplicit() throws IOException { Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); - String eagerUrl = (String) ((Map) ((List)result.get("eager")).get(0)).get("url"); + String eagerUrl = (String) ((Map) ((List) result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); } @Test - public void testEager() throws IOException { + public void testEager() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } @Test - public void testHeaders() throws IOException { + public void testHeaders() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"})); cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } @Test - public void testText() throws IOException { + public void testText() throws IOException { Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); assertTrue(((Integer) result.get("width")) > 1); assertTrue(((Integer) result.get("height")) > 1); @@ -169,13 +174,13 @@ public void testText() throws IOException { @Test public void testImageUploadTag() { - String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); - assertTrue(tag.contains("type='file'")); - assertTrue(tag.contains("data-cloudinary-field='test-field'")); - assertTrue(tag.contains("class='cloudinary-fileupload'")); - assertTrue(tag.contains("htmlattr='htmlvalue'")); - tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); - assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); + String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); + assertTrue(tag.contains("type='file'")); + assertTrue(tag.contains("data-cloudinary-field='test-field'")); + assertTrue(tag.contains("class='cloudinary-fileupload'")); + assertTrue(tag.contains("htmlattr='htmlvalue'")); + tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); + assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } @Test @@ -206,9 +211,9 @@ public void testMulti() throws IOException { @Test public void testTags() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - String public_id = (String)result.get("public_id"); + String public_id = (String) result.get("public_id"); Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); - String public_id2 = (String)result2.get("public_id"); + String public_id2 = (String) result2.get("public_id"); cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); @@ -225,196 +230,196 @@ public void testTags() throws Exception { @Test public void testAllowedFormats() throws Exception { - //should allow whitelisted formats if allowed_formats - String[] formats = {"png"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); - assertEquals(result.get("format"), "png"); + //should allow whitelisted formats if allowed_formats + String[] formats = {"png"}; + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + assertEquals(result.get("format"), "png"); } @Test public void testAllowedFormatsWithIllegalFormat() throws Exception { - //should prevent non whitelisted formats from being uploaded if allowed_formats is specified - boolean errorFound = false; - String[] formats = {"jpg"}; - try{ - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); - } catch(Exception e) { - errorFound=true; + //should prevent non whitelisted formats from being uploaded if allowed_formats is specified + boolean errorFound = false; + String[] formats = {"jpg"}; + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + } catch (Exception e) { + errorFound = true; } assertTrue(errorFound); } @Test public void testAllowedFormatsWithFormat() throws Exception { - //should allow non whitelisted formats if type is specified and convert to that type - String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); - assertEquals("jpg", result.get("format")); + //should allow non whitelisted formats if type is specified and convert to that type + String[] formats = {"jpg"}; + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); + assertEquals("jpg", result.get("format")); } @Test public void testFaceCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121,31,110,151); - Rectangle rect2 = new Rectangle(120,30,109,150); - coordinates.addRect(rect1); - coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); - ArrayList resultFaces = ((ArrayList) result.get("faces")); - assertEquals(2, resultFaces.size()); - - Object[] resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); - - assertEquals(rect1.x, resultCoordinates[0]); - assertEquals(rect1.y, resultCoordinates[1]); - assertEquals(rect1.width, resultCoordinates[2]); - assertEquals(rect1.height, resultCoordinates[3]); - - resultCoordinates =((ArrayList)resultFaces.get(1)).toArray(); - - assertEquals(rect2.x, resultCoordinates[0]); - assertEquals(rect2.y, resultCoordinates[1]); - assertEquals(rect2.width, resultCoordinates[2]); - assertEquals(rect2.height, resultCoordinates[3]); - - Coordinates differentCoordinates = new Coordinates(); - Rectangle rect3 = new Rectangle(122,32,111,152); - differentCoordinates.addRect(rect3); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); - - resultFaces = (ArrayList) info.get("faces"); - assertEquals(1, resultFaces.size()); - resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); - - assertEquals(rect3.x, resultCoordinates[0]); - assertEquals(rect3.y, resultCoordinates[1]); - assertEquals(rect3.width, resultCoordinates[2]); - assertEquals(rect3.height, resultCoordinates[3]); + //should allow sending face coordinates + Coordinates coordinates = new Coordinates(); + Rectangle rect1 = new Rectangle(121, 31, 110, 151); + Rectangle rect2 = new Rectangle(120, 30, 109, 150); + coordinates.addRect(rect1); + coordinates.addRect(rect2); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); + ArrayList resultFaces = ((ArrayList) result.get("faces")); + assertEquals(2, resultFaces.size()); + + Object[] resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect1.x, resultCoordinates[0]); + assertEquals(rect1.y, resultCoordinates[1]); + assertEquals(rect1.width, resultCoordinates[2]); + assertEquals(rect1.height, resultCoordinates[3]); + + resultCoordinates = ((ArrayList) resultFaces.get(1)).toArray(); + + assertEquals(rect2.x, resultCoordinates[0]); + assertEquals(rect2.y, resultCoordinates[1]); + assertEquals(rect2.width, resultCoordinates[2]); + assertEquals(rect2.height, resultCoordinates[3]); + + Coordinates differentCoordinates = new Coordinates(); + Rectangle rect3 = new Rectangle(122, 32, 111, 152); + differentCoordinates.addRect(rect3); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); + + resultFaces = (ArrayList) info.get("faces"); + assertEquals(1, resultFaces.size()); + resultCoordinates = ((ArrayList) resultFaces.get(0)).toArray(); + + assertEquals(rect3.x, resultCoordinates[0]); + assertEquals(rect3.y, resultCoordinates[1]); + assertEquals(rect3.width, resultCoordinates[2]); + assertEquals(rect3.height, resultCoordinates[3]); } @Test public void testCustomCoordinates() throws Exception { - //should allow sending face coordinates - Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - int[] expected = new int[]{121,31,110,151}; - Object[] actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } - - coordinates = new Coordinates(new int[]{122,32,110,152}); - cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); - result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); - expected = new int[]{122,32,110,152}; - actual = ((ArrayList)((ArrayList)((Map)result.get("coordinates")).get("custom")).get(0)).toArray(); - for (int i = 0; i < expected.length; i++){ - assertEquals(expected[i], actual[i]); - } + //should allow sending face coordinates + Coordinates coordinates = new Coordinates("121,31,300,151"); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + int[] expected = new int[]{121, 31, SRC_TEST_IMAGE_W, SRC_TEST_IMAGE_H}; + Object[] actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual[i]); + } + + coordinates = new Coordinates(new int[]{122, 32, SRC_TEST_IMAGE_W + 100, SRC_TEST_IMAGE_H + 100}); + cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); + result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + expected = new int[]{122, 32, SRC_TEST_IMAGE_W + 100, SRC_TEST_IMAGE_H + 100}; + actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual[i]); + } } @Test public void testContext() throws Exception { - //should allow sending context - Map context = ObjectUtils.asMap("caption", "some cäption", "alt", "alternativè"); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); - Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); - info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); + //should allow sending context + Map context = ObjectUtils.asMap("caption", "some cäption", "alt", "alternativè"); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); + Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); + Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); + cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); + info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); + assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); } @Test public void testModerationRequest() throws Exception { - //should support requesting manual moderation - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); - assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); + //should support requesting manual moderation + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); + assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); } @Test public void testRawConvertRequest() { - //should support requesting raw conversion - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + //should support requesting raw conversion + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } @Test public void testCategorizationRequest() { - //should support requesting categorization - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + //should support requesting categorization + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } @Test public void testDetectionRequest() { - //should support requesting detection - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); - } catch(Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + //should support requesting detection + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); + } catch (Exception e) { + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } } @Test public void testAutoTaggingRequest() { - //should support requesting auto tagging - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); - } catch(Exception e) { - assertTrue(e.getMessage().matches("^Must use(.*)")); + //should support requesting auto tagging + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); + } catch (Exception e) { + assertTrue(e.getMessage().matches("^Must use(.*)")); } } @Test - public void testUploadLarge() throws Exception { - // support uploading large files - + public void testUploadLarge() throws Exception { + // support uploading large files + File temp = File.createTempFile("cldupload.test.", ""); FileOutputStream out = new FileOutputStream(temp); - int[] header = new int[]{0x42,0x4D,0x4A,0xB9,0x59,0x00,0x00,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x78,0x05,0x00,0x00,0x78,0x05,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0xC0,0xB8,0x59,0x00,0x61,0x0F,0x00,0x00,0x61,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xB8,0x1E,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC4,0xF5,0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + int[] header = new int[]{0x42, 0x4D, 0x4A, 0xB9, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB8, 0x59, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xB8, 0x1E, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF5, 0x28, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; byte[] byteHeader = new byte[138]; for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; byte[] piece = new byte[10]; Arrays.fill(piece, (byte) 0xff); out.write(byteHeader); for (int i = 1; i <= 588000; i++) { - out.write(piece); + out.write(piece); } out.close(); assertEquals(5880138, temp.length()); ArrayList tags = new java.util.ArrayList(); tags.add("upload_large_tag"); - + Map resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); assertEquals(tags, resource.get("tags")); assertEquals("raw", resource.get("resource_type")); - + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5243000, "tags", tags)); assertEquals(tags, resource.get("tags")); assertEquals("image", resource.get("resource_type")); assertEquals(1400, resource.get("width")); assertEquals(1400, resource.get("height")); - + resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); assertEquals(tags, resource.get("tags")); assertEquals("image", resource.get("resource_type")); assertEquals(1400, resource.get("width")); assertEquals(1400, resource.get("height")); - + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); assertEquals(tags, resource.get("tags")); assertEquals("image", resource.get("resource_type")); @@ -423,20 +428,20 @@ public void testUploadLarge() throws Exception { } @Test - public void testUnsignedUpload() throws Exception { - // should support unsigned uploading using presets + public void testUnsignedUpload() throws Exception { + // should support unsigned uploading using presets Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.emptyMap()); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } - + @Test public void testFilenameOption() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif")); - assertEquals("emanelif", result.get("original_filename")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif")); + assertEquals("emanelif", result.get("original_filename")); } - + @Test public void testResponsiveBreakpoints() throws Exception { ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false); @@ -444,54 +449,54 @@ public void testResponsiveBreakpoints() throws Exception { // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", breakpoint - )); - java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); - java.util.ArrayList breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); - assertEquals(2, breakpoints.size()); + )); + java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); // an array of breakpoints - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", - new ResponsiveBreakpoint [] {breakpoint} - )); - breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); - breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); - assertEquals(2, breakpoints.size()); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + new ResponsiveBreakpoint[]{breakpoint} + )); + breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); // a JSONArray of breakpoints JSONArray array = new JSONArray(); array.put(breakpoint); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array - )); - breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); - breakpoints = (java.util.ArrayList)((Map) breakpointsResponse.get(0)).get("breakpoints"); - assertEquals(2, breakpoints.size()); - } - - @Test - public void testCreateArchive() throws Exception { - Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[] { ARCHIVE_TAG })); - assertEquals(2, result.get("file_count")); - result = cloudinary.uploader().createArchive( - new ArchiveParams().tags(new String[] { ARCHIVE_TAG }).transformations( - new Transformation[] { new Transformation().width(0.5), new Transformation().width(2.0) })); - assertEquals(4, result.get("file_count")); - } - - @Test - public void testDownloadArchive() throws Exception { - String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[] { ARCHIVE_TAG })); - URL url = new java.net.URL(result); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - ZipInputStream in = new ZipInputStream(new BufferedInputStream(urlConnection.getInputStream())); - int files = 0; - try { - while ((in.getNextEntry()) != null) { - files += 1; - } - } finally { - in.close(); - } - assertEquals(2, files); - } + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array + )); + breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); + breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); + assertEquals(2, breakpoints.size()); + } + + @Test + public void testCreateArchive() throws Exception { + Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); + assertEquals(2, result.get("file_count")); + result = cloudinary.uploader().createArchive( + new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).transformations( + new Transformation[]{new Transformation().width(0.5), new Transformation().width(2.0)})); + assertEquals(4, result.get("file_count")); + } + + @Test + public void testDownloadArchive() throws Exception { + String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); + URL url = new java.net.URL(result); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + ZipInputStream in = new ZipInputStream(new BufferedInputStream(urlConnection.getInputStream())); + int files = 0; + try { + while ((in.getNextEntry()) != null) { + files += 1; + } + } finally { + in.close(); + } + assertEquals(2, files); + } } From 86ec1988db5b1a2290ac3ac986ce7f8112677c1a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 10 Mar 2016 08:54:45 +0200 Subject: [PATCH 125/592] Add Conditional Transformations --- .../java/com/cloudinary/Transformation.java | 53 +++- .../cloudinary/transformation/Condition.java | 108 +++++++ .../com/cloudinary/TransformationTest.java | 144 +++++++++ .../com/cloudinary/test/CloudinaryTest.java | 286 +++++++++--------- 4 files changed, 441 insertions(+), 150 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 5c47558d..0ddfce18 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -1,15 +1,11 @@ package com.cloudinary; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.cloudinary.transformation.AbstractLayer; +import com.cloudinary.transformation.Condition; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -352,6 +348,41 @@ public Transformation responsiveWidth(boolean value) { return param("responsive_width", value); } + public Condition ifCondition() { + return new Condition().setParent(this); + } + public Transformation ifCondition(String condition) { + return param("if", condition); + } + public Transformation ifElse() { + chain(); + return param("if", "else"); + } + + public Transformation endIf() { + + chain(); + int transSize = this.transformations.size(); + for (int i = transSize - 1; i >= 0; i--) { + Map segment = this.transformations.get(i); // [..., {if: "w_gt_1000",c: "fill", w: 500}, ...] + Object value = segment.get("if"); + if (value != null) { // if: "w_gt_1000" + String ifValue = value.toString(); + if (ifValue.equals("end")) break; + if (segment.size() > 1) { + segment.remove("if"); // {c: fill, w: 500} + transformations.set(i, segment); // [..., {c: fill, w: 500}, ...] + transformations.add(i, ObjectUtils.asMap("if", value)); // // [..., "if_w_gt_1000", {c: fill, w: 500}, ...] + } + if (!"else".equals(ifValue)) break; // otherwise keep looking for if_condition + } + } + + param("if", "end"); + return chain(); + } + + public boolean isResponsive() { return this.isResponsive; } @@ -403,7 +434,9 @@ public String toString() { public String generate(Iterable optionsList) { List components = new ArrayList(); for (Map options : optionsList) { - components.add(generate(options)); + if(options.size() > 0){ + components.add(generate(options)); + } } return StringUtils.join(components, "/"); } @@ -539,6 +572,12 @@ public String generate(Map options) { if (raw_transformation != null) { components.add(raw_transformation); } + + String ifValue = (String) options.get("if"); + if(ifValue != null){ + components.add(0, "if_" + new Condition(ifValue).toString()); + } + if (!components.isEmpty()) { transformations.add(StringUtils.join(components, ",")); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java new file mode 100644 index 00000000..d31c077c --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -0,0 +1,108 @@ +package com.cloudinary.transformation; + +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class Condition { + public static final Map OPERATORS = ObjectUtils.asMap( + "=", "eq", + "!=", "ne", + "<", "lt", + ">", "gt", + "<=", "lte", + ">=", "gte", + "&&", "and", + "||", "or"); + + protected List predicateList = null; + private Transformation parent = null; + + public Condition( ) { + predicateList = new ArrayList(); + + } + public Condition( String conditionStr) { + this(); + if (conditionStr != null) { + predicateList.add( literal(conditionStr)); + } + } + + private String literal(String conditionStr) { + String[] list = conditionStr.split("[ _]+"); + String[] translated = new String[list.length]; + for (int i = 0, j = 0; i < list.length; i++) { + String s = list[i]; + if (OPERATORS.containsKey(s)) { + translated[j++] = (String) OPERATORS.get(s); + } else { + translated[j++] = s; + } + } + return StringUtils.join(translated, "_"); + } + public Transformation getParent() { return parent;} + public Condition setParent( Transformation parent) { + this.parent = parent; + return this; + } + + public String serialize() { return StringUtils.join(predicateList, "_");} + + @Override + public String toString() { + return serialize(); + } + + protected Condition predicate( String name, String operator, String value) { + if (OPERATORS.containsKey(operator)) { + operator = (String) OPERATORS.get(operator); + } + predicateList.add(String.format("%s_%s_%s",name, operator, value)); + return this; + } + + public Condition and() { + predicateList.add("and"); + return this; + } + + public Condition or() { + predicateList.add("or"); + return this; + } + + public Transformation then() { + getParent().ifCondition( serialize()); + return getParent(); + } + + public Condition width(String operator, Object value) { + predicateList.add("w_"+ operator + "_" + value); + return this; + } + + public Condition height(String operator, Object value) { + predicateList.add("h_"+ operator + "_" + value); + return this; + } + + public Condition aspectRatio(String operator, Object value) { + predicateList.add("ar_"+ operator + "_" + value); + return this; + } + + + public Condition pages(String operator, Object value) { + predicateList.add("pg_"+ operator + "_" + value); + return this; + } +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java new file mode 100644 index 00000000..b1df71a3 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -0,0 +1,144 @@ +package com.cloudinary; + +import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONArray; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * + */ +@SuppressWarnings("unchecked") +public class TransformationTest { + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + + } + + @Test + public void withLiteral() throws Exception { + Transformation transformation = new Transformation().ifCondition("w_lt_200").crop("fill").height(120).width(80); + assertEquals("should include the if parameter as the first component in the transformation string", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString()); + + transformation = new Transformation().crop("fill").height(120).ifCondition("w_lt_200").width(80); + assertEquals("should include the if parameter as the first component in the transformation string", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString()); + + String chained = "[{if: \"w_lt_200\",crop: \"fill\",height: 120, width: 80}, {if: \"w_gt_400\",crop: \"fit\",width: 150,height: 150},{effect: \"sepia\"}]"; + List transformations = ObjectUtils.toList(new JSONArray(chained)); + + transformation = new Transformation(transformations); + assertEquals("should allow multiple conditions when chaining transformations", "if_w_lt_200,c_fill,h_120,w_80/if_w_gt_400,c_fit,h_150,w_150/e_sepia", transformation.toString()); + } + + @Test + public void literalWithSpaces() throws Exception { + Map map = ObjectUtils.asMap("if", "w < 200", "crop", "fill", "height", 120, "width", 80); + List list = new ArrayList(); + list.add(map); + Transformation transformation = new Transformation(list); + + assertEquals("should translate operators", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString()); + } + + @Test + public void endIf() throws Exception { + String chained = "[{if: \"w_lt_200\"},\n" + + " {crop: \"fill\", height: 120, width: 80,effect: \"sharpen\"},\n" + + " {effect: \"brightness:50\"},\n" + + " {effect: \"shadow\",color: \"red\"}, {if: \"end\"}]"; + List transformations = ObjectUtils.toList(new JSONArray(chained)); + + Transformation transformation = new Transformation(transformations); + assertEquals("should include the if_end as the last parameter in its component", "if_w_lt_200/c_fill,e_sharpen,h_120,w_80/e_brightness:50/co_red,e_shadow/if_end", transformation.toString()); + + } + + @Test + public void ifElse() throws Exception { + String chained = "[{if: \"w_lt_200\",crop: \"fill\",height: 120,width: 80},\n" + + " {if: \"else\",crop: \"fill\",height: 90, width: 100}]"; + List transformations = ObjectUtils.toList(new JSONArray(chained)); + + Transformation transformation = new Transformation(transformations); + + assertEquals("should support if_else with transformation parameters", "if_w_lt_200,c_fill,h_120,w_80/if_else,c_fill,h_90,w_100", transformation.toString()); + + chained = "[{if: \"w_lt_200\"},\n" + + " {crop: \"fill\",height: 120,width: 80},\n" + + " {if: \"else\"},\n" + + " {crop: \"fill\",height: 90,width: 100}]"; + transformations = ObjectUtils.toList(new JSONArray(chained)); + + transformation = new Transformation(transformations); + assertEquals("if_else should be without any transformation parameters", "if_w_lt_200/c_fill,h_120,w_80/if_else/c_fill,h_90,w_100", transformation.toString()); + } + + @Test + public void chainedConditions() throws Exception { + Transformation transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").then().width(100).crop("scale"); + assertEquals("passing an operator and a value adds a condition", "if_ar_gt_3:4,c_scale,w_100", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").and().width("gt", 100).then().width(50).crop("scale"); + assertEquals("should chaining condition with `and`", "if_ar_gt_3:4_and_w_gt_100,c_scale,w_50", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").and().width("gt", 100).or().width("gt", 200).then().width(50).crop("scale"); + assertEquals("should chain conditions with `or`", "if_ar_gt_3:4_and_w_gt_100_or_w_gt_200,c_scale,w_50", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio(">", "3:4").and().width("<=", 100).or().width("gt", 200).then().width(50).crop("scale"); + assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio(">", "3:4").and().width("<=", 100).or().width(">", 200).then().width(50).crop("scale"); + assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio(">=", "3:4").and().pages(">=", 100).or().pages("!=", 0).then().width(50).crop("scale"); + assertEquals("should translate operators", "if_ar_gte_3:4_and_pg_gte_100_or_pg_ne_0,c_scale,w_50", transformation.toString()); + + } + + @Test + public void shouldSupportAndTranslateOperators() { + + String allOperators = + "if_" + + "w_eq_0_and" + + "_w_ne_0_or" + + "_w_lt_0_and" + + "_w_gt_0_and" + + "_w_lte_0_and" + + "_w_gte_0" + + ",e_grayscale"; + assertEquals("should support and translate operators: '=', '!=', '<', '>', '<=', '>=', '&&', '||'", + allOperators, new Transformation().ifCondition() + .width("=", 0).and() + .width("!=", 0).or() + .width("<", 0).and() + .width(">", 0).and() + .width("<=", 0).and() + .width(">=", 0) + .then().effect("grayscale").toString()); + + assertEquals(allOperators, new Transformation().ifCondition("w = 0 && w != 0 || w < 0 and w > 0 and w <= 0 and w >= 0") + .effect("grayscale") + .toString()); + } + + @Test + public void endIf2() throws Exception { + Transformation transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").endIf(); + assertEquals("should serialize to 'if_end'", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_end", transformation.toString()); + transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").endIf(); + assertEquals("force the if clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_end", transformation.toString()); + transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").ifElse().width(100).crop("crop").endIf(); + assertEquals("force the if_else clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_else/c_crop,w_100/if_end", transformation.toString()); + + } + +} \ No newline at end of file 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 874a46ba..289a42ec 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -109,6 +109,149 @@ public void testFormat() { assertEquals(DEFAULT_UPLOAD_PATH + "test.jpg", result); } + @Test + 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); + } + + @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); + } + + @Test + public void testIgnoreHttp() { + // should ignore http links only if type is not given or is asset + String result = cloudinary.url().generate("http://test"); + assertEquals("http://test", result); + 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); + } + + @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); + } + + @Test + public void testCname() { + // should support external cname + String result = cloudinary.url().cname("hello.com").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"); + assertEquals("http://a2.hello.com/test123/image/upload/test", result); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInSharedDistribution() { + cloudinary.url().suffix("hello").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixInNonUploadTypes() { + cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithSlash() { + cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisallowUrlSuffixWithDot() { + cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); + } + + @Test + public void testSupportUrlSuffixForPrivateCdn() { + String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); + assertEquals("http://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); + + } + + @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); + } + + @Test + public void testNotSignTheUrlSuffix() { + + Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); + String url = cloudinary.url().format("jpg").signed(true).generate("test"); + Matcher matcher = pattern.matcher(url); + matcher.find(); + 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); + + url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); + matcher = pattern.matcher(url); + matcher.find(); + expectedSignature = url.substring(matcher.start(), matcher.end()); + + 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); + } + + @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); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathInSharedDistribution() { + cloudinary.url().useRootPath(true).generate("test"); + } + + @Test + public void testSupportUseRootPathForPrivateCdn() { + String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); + assertEquals("http://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); + } + + @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); + + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForFacebook() { + cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDisllowUseRootPathIfNotImageUploadForRaw() { + cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); + } + @Test public void testCrop() { Transformation transformation = new Transformation().width(100).height(101); @@ -171,52 +314,6 @@ public void testNoEmptyTransformation() { assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/test", result); } - @Test - 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); - } - - @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); - } - - @Test - public void testIgnoreHttp() { - // should ignore http links only if type is not given or is asset - String result = cloudinary.url().generate("http://test"); - assertEquals("http://test", result); - 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); - } - - @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); - } - - @Test - public void testCname() { - // should support external cname - String result = cloudinary.url().cname("hello.com").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"); - assertEquals("http://a2.hello.com/test123/image/upload/test", result); - } - @Test public void testHttpEscape() { // should escape http urls @@ -480,103 +577,6 @@ public void testResponsiveWidth() { Transformation.setResponsiveWidthTransformation(null); } - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInSharedDistribution() { - cloudinary.url().suffix("hello").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInNonUploadTypes() { - cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithSlash() { - cloudinary.url().suffix("hello/world").privateCdn(true).generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixWithDot() { - cloudinary.url().suffix("hello.world").privateCdn(true).generate("test"); - } - - @Test - public void testSupportUrlSuffixForPrivateCdn() { - String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); - assertEquals("http://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); - - } - - @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); - } - - @Test - public void testNotSignTheUrlSuffix() { - - Pattern pattern = Pattern.compile("s--[0-9A-Za-z_-]{8}--"); - String url = cloudinary.url().format("jpg").signed(true).generate("test"); - Matcher matcher = pattern.matcher(url); - matcher.find(); - 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); - - url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); - matcher = pattern.matcher(url); - matcher.find(); - expectedSignature = url.substring(matcher.start(), matcher.end()); - - 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); - } - - @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); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathInSharedDistribution() { - cloudinary.url().useRootPath(true).generate("test"); - } - - @Test - public void testSupportUseRootPathForPrivateCdn() { - String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); - assertEquals("http://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); - } - - @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); - - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForFacebook() { - cloudinary.url().useRootPath(true).privateCdn(true).type("facebook").generate("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathIfNotImageUploadForRaw() { - cloudinary.url().useRootPath(true).privateCdn(true).resourceType("raw").generate("test"); - } - @Test public void testVideoCodec() { // should support a string value From 228a6f4659e8706cb3f8088ad285ffa4b629e8b9 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 17 Mar 2016 15:15:27 +0200 Subject: [PATCH 126/592] Modify categorization test result value. --- .../src/main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 1 file changed, 1 insertion(+), 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 a5fd9bec..f7a26812 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 @@ -360,7 +360,7 @@ public void testCategorizationRequest() { try { cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); } } From d7b8bd2354beee1783c32910f884f07443fc3b5a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 17 Mar 2016 17:09:03 +0200 Subject: [PATCH 127/592] Modify explicit test - don't use twitter --- .../java/com/cloudinary/test/UploaderTest.java | 14 +++++++------- .../com/cloudinary/test/AbstractUploaderTest.java | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 8a75f2ba..22441541 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -149,11 +149,11 @@ public void testRename() throws Exception { public void testExplicit() throws Exception { if (cloudinary.config.apiSecret == null) return; - JSONObject result = new JSONObject(cloudinary.uploader().explicit("cloudinary", - ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name"))); - String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png") - .version(result.get("version")).generate("cloudinary"); - assertEquals(result.getJSONArray("eager").getJSONObject(0).get("url"), url); + JSONObject result = new JSONObject(cloudinary.uploader().explicit("sample", + ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload"))); + String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg") + .version(result.get("version")).generate("sample"); + assertEquals(url, result.getJSONArray("eager").getJSONObject(0).get("url")); } public void testEager() throws Exception { @@ -293,7 +293,7 @@ public void testCategorizationRequest() { try { cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); } } @@ -304,7 +304,7 @@ public void testDetectionRequest() { try { cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); + assertTrue(e.getMessage().matches(".*(Illegal value|not a valid|invalid).*")); } } 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 f7a26812..739e603f 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 @@ -147,8 +147,8 @@ public void testUniqueFilename() throws Exception { @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("cloudinary", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "twitter_name")); - String url = cloudinary.url().type("twitter_name").transformation(new Transformation().crop("scale").width(2.0)).format("png").version(result.get("version")).generate("cloudinary"); + Map result = cloudinary.uploader().explicit("sample", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload")); + String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg").version(result.get("version")).generate("sample"); String eagerUrl = (String) ((Map) ((List) result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); From baee7f90c0890bcf7803b8264ef6f6e073c7f845 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 19 Mar 2016 10:10:07 +0200 Subject: [PATCH 128/592] Add Condition builder for faces --- .../main/java/com/cloudinary/transformation/Condition.java | 4 ++++ 1 file changed, 4 insertions(+) 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 d31c077c..093f5a90 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -100,6 +100,10 @@ public Condition aspectRatio(String operator, Object value) { return this; } + public Condition faces(String operator, Object value) { + predicateList.add("faces_"+ operator + "_" + value); + return this; + } public Condition pages(String operator, Object value) { predicateList.add("pg_"+ operator + "_" + value); From df9d4dffb7ce9a49b525bc55c9aa314125ca0349 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 19 Mar 2016 10:25:38 +0200 Subject: [PATCH 129/592] Prepare for version 1.4.0 --- CHANGELOG.md | 10 ++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0859f56b..42e54a5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +cloudinary-parent-1.4.0 / 2016-01-19 +==================================== + * Add Condition builder for faces + * Modify explicit test - don't use twitter + * Modify categorization test result value + * Add Conditional Transformations + * Cleanup Whitespace + * Use variables for public_id's in rename tests + * Fix uploadLarge to use X-Unique-Upload-Id instead of updating params. Solves #18 + * Fix support for non-ascii chars in upload URL cloudinary-parent-1.3.0 / 2016-01-19 ==================================== diff --git a/README.md b/README.md index 0bc7df46..088d4f3b 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.2.2 + 1.4.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.2.2/cloudinary-core-1.2.2.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.2.2/cloudinary-http44-1.2.2.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.0/cloudinary-core-1.4.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.0/cloudinary-http44-1.4.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 312103d4..2bb8f3ef 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.3.0"; + public final static String VERSION = "1.4.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 72fb9c707e92ec248635d1b8bbfc47c233728079 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 19 Mar 2016 10:42:02 +0200 Subject: [PATCH 130/592] [maven-release-plugin] prepare release 1.4.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index bee7596b..b5dd8bd5 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 com.cloudinary cloudinary-android-test - 1.3.1-SNAPSHOT + 1.4.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.3.1-SNAPSHOT + 1.4.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 70305a72..056656d6 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index fcd0c6bc..55f5382a 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 26f36f37..7209fd75 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index b937527d..4b0ec30e 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 3e75f9e4..50791eaa 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index b99f1646..4288d727 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index a7ba1c25..ce051670 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index e033ecf0..47f823fc 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.3.1-SNAPSHOT + 1.4.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.0 From dfc62f4317ee3a66a3fdf6df7811eef31acafd43 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 19 Mar 2016 10:42:09 +0200 Subject: [PATCH 131/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index b5dd8bd5..a69f50fc 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.0 + 1.4.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.0 + 1.4.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 056656d6..37fc1dfc 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 55f5382a..2981d9fa 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 7209fd75..ec4b58b4 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 4b0ec30e..b9ba9f6d 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 50791eaa..51fefb1d 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 4288d727..8ab400a5 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index ce051670..20daa60a 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 47f823fc..9fd2be53 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.0 + 1.4.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.0 + HEAD From 64d44e0ef38e3d1054c95e9ca8206e8bccd74edf Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 23 Mar 2016 08:47:14 +0200 Subject: [PATCH 132/592] Rename conditional parameters `faces` and `pages` to `faceCount` and `pageCount` --- .../java/com/cloudinary/Transformation.java | 14 ++- .../cloudinary/transformation/Condition.java | 88 ++++++++++++++----- .../com/cloudinary/TransformationTest.java | 30 +++---- 3 files changed, 94 insertions(+), 38 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 0ddfce18..96ef9203 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -20,7 +20,7 @@ public class Transformation { protected static boolean defaultIsResponsive = false; protected static Object defaultDPR = null; - private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); + private static final Map DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = ObjectUtils.asMap("width", "auto", "crop", "limit"); protected static Map responsiveWidthTransformation = null; private static final Pattern RANGE_VALUE_RE = Pattern.compile("^((?:\\d+\\.)?\\d+)([%pP])?$"); private static final Pattern RANGE_RE = Pattern.compile("^(\\d+\\.)?\\d+[%pP]?\\.\\.(\\d+\\.)?\\d+[%pP]?$"); @@ -348,19 +348,29 @@ public Transformation responsiveWidth(boolean value) { return param("responsive_width", value); } + /** + * Start defining a condition, which will be completed with a call {@link Condition#then()} + * @return condition + */ public Condition ifCondition() { return new Condition().setParent(this); } + + /** + * Define a conditional transformation defined by the condition string + * @param condition a condition string + * @return the transformation for chaining + */ public Transformation ifCondition(String condition) { return param("if", condition); } + public Transformation ifElse() { chain(); return param("if", "else"); } public Transformation endIf() { - chain(); int transSize = this.transformations.size(); for (int i = transSize - 1; i >= 0; i--) { 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 093f5a90..bb28eab5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -7,9 +7,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * + * Represents a condition for {@link Transformation#ifCondition()} */ public class Condition { public static final Map OPERATORS = ObjectUtils.asMap( @@ -22,35 +24,61 @@ public class Condition { "&&", "and", "||", "or"); + public static final Map PARAMETERS = ObjectUtils.asMap( + "width", "w", + "height", "h", + "aspect_ratio", "ar", + "aspectRatio", "ar", + "page_count", "pc", + "pageCount", "pc", + "face_count", "fc", + "faceCount", "fc" + ); + protected List predicateList = null; private Transformation parent = null; - public Condition( ) { + public Condition() { predicateList = new ArrayList(); } - public Condition( String conditionStr) { + + /** + * Create a Condition Object. The conditionStr string will be translated to a serialized condition. + * + * @param conditionStr condition in string format + */ + public Condition(String conditionStr) { this(); if (conditionStr != null) { - predicateList.add( literal(conditionStr)); + predicateList.add(literal(conditionStr)); } } private String literal(String conditionStr) { - String[] list = conditionStr.split("[ _]+"); - String[] translated = new String[list.length]; - for (int i = 0, j = 0; i < list.length; i++) { - String s = list[i]; - if (OPERATORS.containsKey(s)) { - translated[j++] = (String) OPERATORS.get(s); + + String replacement; + conditionStr = conditionStr.replaceAll("[ _]+", "_"); + Pattern replaceRE = Pattern.compile("(" + StringUtils.join(PARAMETERS.keySet(), "|") + "|[=<>&|!]+)"); + Matcher matcher = replaceRE.matcher(conditionStr); + StringBuffer result = new StringBuffer(conditionStr.length()); + while (matcher.find()) { + if (OPERATORS.containsKey(matcher.group())) { + replacement = (String) OPERATORS.get(matcher.group()); + } else if (PARAMETERS.containsKey(matcher.group())) { + replacement = (String) PARAMETERS.get(matcher.group()); } else { - translated[j++] = s; + replacement = matcher.group(); } + matcher.appendReplacement(result, replacement); } - return StringUtils.join(translated, "_"); + matcher.appendTail(result); + return result.toString(); } + public Transformation getParent() { return parent;} - public Condition setParent( Transformation parent) { + + public Condition setParent(Transformation parent) { this.parent = parent; return this; } @@ -62,11 +90,11 @@ public String toString() { return serialize(); } - protected Condition predicate( String name, String operator, String value) { + protected Condition predicate(String name, String operator, String value) { if (OPERATORS.containsKey(operator)) { operator = (String) OPERATORS.get(operator); } - predicateList.add(String.format("%s_%s_%s",name, operator, value)); + predicateList.add(String.format("%s_%s_%s", name, operator, value)); return this; } @@ -80,33 +108,51 @@ public Condition or() { return this; } + /** + * Terminates the definition of the condition and continue with Transformation definition. + * @return the Transformation object this Condition is attached to. + */ public Transformation then() { - getParent().ifCondition( serialize()); + getParent().ifCondition(serialize()); return getParent(); } public Condition width(String operator, Object value) { - predicateList.add("w_"+ operator + "_" + value); + predicateList.add("w_" + operator + "_" + value); return this; } public Condition height(String operator, Object value) { - predicateList.add("h_"+ operator + "_" + value); + predicateList.add("h_" + operator + "_" + value); return this; } public Condition aspectRatio(String operator, Object value) { - predicateList.add("ar_"+ operator + "_" + value); + predicateList.add("ar_" + operator + "_" + value); return this; } + /** + * @deprecated Use {@link #faceCount(String, Object)} instead + */ public Condition faces(String operator, Object value) { - predicateList.add("faces_"+ operator + "_" + value); + return faceCount(operator, value); + } + + public Condition faceCount(String operator, Object value) { + predicateList.add("fc_" + operator + "_" + value); return this; } + /** + * @deprecated Use {@link #pageCount(String, Object)} instead + */ public Condition pages(String operator, Object value) { - predicateList.add("pg_"+ operator + "_" + value); + return pageCount(operator, value); + } + + public Condition pageCount(String operator, Object value) { + predicateList.add("pc_" + operator + "_" + value); return this; } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index b1df71a3..506891c5 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -45,7 +45,7 @@ public void withLiteral() throws Exception { @Test public void literalWithSpaces() throws Exception { - Map map = ObjectUtils.asMap("if", "w < 200", "crop", "fill", "height", 120, "width", 80); + Map map = ObjectUtils.asMap("if", "width < 200", "crop", "fill", "height", 120, "width", 80); List list = new ArrayList(); list.add(map); Transformation transformation = new Transformation(list); @@ -98,8 +98,8 @@ public void chainedConditions() throws Exception { assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString()); transformation = new Transformation().ifCondition().aspectRatio(">", "3:4").and().width("<=", 100).or().width(">", 200).then().width(50).crop("scale"); assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString()); - transformation = new Transformation().ifCondition().aspectRatio(">=", "3:4").and().pages(">=", 100).or().pages("!=", 0).then().width(50).crop("scale"); - assertEquals("should translate operators", "if_ar_gte_3:4_and_pg_gte_100_or_pg_ne_0,c_scale,w_50", transformation.toString()); + transformation = new Transformation().ifCondition().aspectRatio(">=", "3:4").and().pageCount(">=", 100).or().pageCount("!=", 0).then().width(50).crop("scale"); + assertEquals("should translate operators", "if_ar_gte_3:4_and_pc_gte_100_or_pc_ne_0,c_scale,w_50", transformation.toString()); } @@ -107,25 +107,25 @@ public void chainedConditions() throws Exception { public void shouldSupportAndTranslateOperators() { String allOperators = - "if_" + - "w_eq_0_and" + - "_w_ne_0_or" + - "_w_lt_0_and" + - "_w_gt_0_and" + - "_w_lte_0_and" + - "_w_gte_0" + + "if_" + + "w_eq_0_and" + + "_h_ne_0_or" + + "_ar_lt_0_and" + + "_pc_gt_0_and" + + "_fc_lte_0_and" + + "_w_gte_0" + ",e_grayscale"; assertEquals("should support and translate operators: '=', '!=', '<', '>', '<=', '>=', '&&', '||'", allOperators, new Transformation().ifCondition() .width("=", 0).and() - .width("!=", 0).or() - .width("<", 0).and() - .width(">", 0).and() - .width("<=", 0).and() + .height("!=", 0).or() + .aspectRatio("<", 0).and() + .pageCount(">", 0).and() + .faceCount("<=", 0).and() .width(">=", 0) .then().effect("grayscale").toString()); - assertEquals(allOperators, new Transformation().ifCondition("w = 0 && w != 0 || w < 0 and w > 0 and w <= 0 and w >= 0") + assertEquals(allOperators, new Transformation().ifCondition("w = 0 && height != 0 || aspectRatio < 0 and pageCount > 0 and faceCount <= 0 and width >= 0") .effect("grayscale") .toString()); } From c31235f6eecf824a7bf46742c6bcc68b828763b5 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 23 Mar 2016 13:50:24 +0200 Subject: [PATCH 133/592] prepare for version 1.4.1 --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e54a5c..590641cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -cloudinary-parent-1.4.0 / 2016-01-19 +cloudinary-parent-1.4.1 / 2016-03-23 +==================================== + * Rename conditional parameters `faces` and `pages` to `faceCount` and `pageCount` + + +cloudinary-parent-1.4.0 / 2016-03-19 ==================================== * Add Condition builder for faces * Modify explicit test - don't use twitter diff --git a/README.md b/README.md index 088d4f3b..cb4c4b75 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.0 + 1.4.1 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.0/cloudinary-core-1.4.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.0/cloudinary-http44-1.4.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.1/cloudinary-core-1.4.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.1/cloudinary-http44-1.4.1.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 2bb8f3ef..9cd37413 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.0"; + public final static String VERSION = "1.4.1"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 7fdbde30807a61caae168306a91b857c0d5a5790 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 23 Mar 2016 14:33:32 +0200 Subject: [PATCH 134/592] [maven-release-plugin] prepare release 1.4.1 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index a69f50fc..1e96e96f 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 com.cloudinary cloudinary-android-test - 1.4.1-SNAPSHOT + 1.4.1 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.1-SNAPSHOT + 1.4.1 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 37fc1dfc..5bbdd419 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 2981d9fa..2de0fd62 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index ec4b58b4..7c76eb74 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index b9ba9f6d..5748e698 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 51fefb1d..71e874d1 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 8ab400a5..7bfd842b 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 20daa60a..596459ad 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 9fd2be53..cc5680e9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.1-SNAPSHOT + 1.4.1 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.1 From 501559f7185ace8419480a68da3dc877485b24de Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 23 Mar 2016 14:33:38 +0200 Subject: [PATCH 135/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 1e96e96f..5931bea0 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.1 + 1.4.2-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.1 + 1.4.2-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 5bbdd419..0698fa14 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 2de0fd62..2f3dccee 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 7c76eb74..417d8266 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 5748e698..3c454919 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 71e874d1..9d07dbc7 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 7bfd842b..e66f1bdc 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 596459ad..cc0bbf45 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index cc5680e9..ae4b6bbf 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.1 + 1.4.2-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.1 + HEAD From 388b20074907afc994315ee7aee904fd83d7179e Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 24 Apr 2016 15:20:00 +0300 Subject: [PATCH 136/592] Sent params as entities for PUT, POST --- .../com/cloudinary/http44/ApiStrategy.java | 71 +++++++++++++------ .../com/cloudinary/test/AbstractApiTest.java | 58 ++++++++------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index 77c9c308..1e4e195d 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -1,30 +1,30 @@ package com.cloudinary.http44; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import org.apache.http.HttpHost; -import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.*; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.HttpClientConnectionManager; 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.message.BasicNameValuePair; import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; import com.cloudinary.Api; -import com.cloudinary.Uploader; import com.cloudinary.Api.HttpMethod; import com.cloudinary.Cloudinary; import com.cloudinary.api.ApiResponse; @@ -70,6 +70,7 @@ public void init(Api api) { @SuppressWarnings({"rawtypes", "unchecked"}) public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { + URI apiUri; if (options == null) options = ObjectUtils.emptyMap(); @@ -87,29 +88,26 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map 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())); - } - } - - URI apiUri = apiUrlBuilder.build(); HttpUriRequest request = null; + switch (method) { case GET: + apiUri = prepareUrlParams(apiUrlBuilder, params); request = new HttpGet(apiUri); break; case PUT: - request = new HttpPut(apiUri); + apiUri = apiUrlBuilder.build(); + HttpEntityEnclosingRequestBase put = new HttpPut(apiUri); + request = perpareEntityParams(put, params); + break; case POST: - request = new HttpPost(apiUri); + apiUri = apiUrlBuilder.build(); + HttpEntityEnclosingRequestBase post = new HttpPost(apiUri); + request = perpareEntityParams(post, params); break; case DELETE: + apiUri = prepareUrlParams(apiUrlBuilder, params); request = new HttpDelete(apiUri); break; } @@ -148,4 +146,35 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params) throws UnsupportedEncodingException { + HttpUriRequest request;List entities = new ArrayList(params.size()); + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof Iterable) { + for (String single : (Iterable) param.getValue()) { + entities.add(new BasicNameValuePair(param.getKey() + "[]", single)); + } + } else { + entities.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); + } + } + put.setEntity(new UrlEncodedFormEntity(entities)); + request = put; + return request; + } + + private URI prepareUrlParams(URIBuilder urlBuilder, Map params) throws URISyntaxException { + URI apiUri; + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof Iterable) { + for (String single : (Iterable) param.getValue()) { + urlBuilder.addParameter(param.getKey() + "[]", single); + } + } else { + urlBuilder.addParameter(param.getKey(), ObjectUtils.asString(param.getValue())); + } + } + apiUri = urlBuilder.build(); + return apiUri; + } } 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 c4dba3c7..df5f8a4c 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 @@ -38,6 +38,11 @@ abstract public class AbstractApiTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; + public static final String API_TEST = "api_test"; + public static final String API_TEST_1 = "api_test1"; + public static final String API_TEST_2 = "api_test2"; + public static final String API_TEST_3 = "api_test3"; + public static final String API_TEST_5 = "api_test5"; private Cloudinary cloudinary; protected Api api; private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); @@ -51,7 +56,7 @@ public static void setUpClass() throws IOException { } Api api = cloudinary.api(); try { - api.deleteResources(Arrays.asList("api_test", "api_test1", "api_test2", "api_test3", "api_test5"), ObjectUtils.emptyMap()); + api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); } catch (Exception e) { } try { @@ -82,10 +87,10 @@ public static void setUpClass() throws IOException { api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); } catch (Exception e) { } - Map options = ObjectUtils.asMap("public_id", "api_test", "tags", new String[]{"api_test_tag", uniqueTag}, "context", "key=value", "eager", + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{"api_test_tag", uniqueTag}, "context", "key=value", "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options.put("public_id", "api_test1"); + options.put("public_id", API_TEST_1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } @@ -122,7 +127,7 @@ public void test01ResourceTypes() throws Exception { public void test02Resources() throws Exception { // should allow listing resources Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); assertEquals(resource.get("type"), "upload"); } @@ -133,7 +138,7 @@ public void testTimeoutParameter() throws Exception { Map options = new HashMap(); options.put("timeout", Integer.valueOf(5000)); Map result = api.resources(options); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); assertEquals(resource.get("type"), "upload"); } @@ -161,17 +166,17 @@ public void test03ResourcesCursor() throws Exception { public void test04ResourcesByType() throws Exception { // should allow listing resources by type Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); } @Test public void test05ResourcesByPrefix() throws Exception { // should allow listing resources by prefix - Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", "api_test", "tags", true, "context", true)); + Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", API_TEST, "tags", true, "context", true)); List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr(resources, "public_id", API_TEST)); + assertNotNull(findByAttr(resources, "public_id", API_TEST_1)); assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); boolean found = false; for (Map r : resources) { @@ -208,11 +213,11 @@ public void testResourcesListingStartAt() throws Exception { @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids - Map result = api.resourcesByIds(Arrays.asList("api_test", "api_test1", "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); + Map result = api.resourcesByIds(Arrays.asList(API_TEST, API_TEST_1, "bogus"), ObjectUtils.asMap("type", "upload", "tags", true, "context", true)); List resources = (List) result.get("resources"); assertEquals(2, resources.size()); - assertNotNull(findByAttr(resources, "public_id", "api_test")); - assertNotNull(findByAttr(resources, "public_id", "api_test1")); + assertNotNull(findByAttr(resources, "public_id", API_TEST)); + assertNotNull(findByAttr(resources, "public_id", API_TEST_1)); assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); boolean found = false; for (Map r : resources) { @@ -226,7 +231,7 @@ public void testResourcesByPublicIds() throws Exception { public void test06ResourcesTag() throws Exception { // should allow listing resources by tag Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); + Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); assertNotNull(resource); @@ -242,9 +247,9 @@ public void test06ResourcesTag() throws Exception { @Test public void test07ResourceMetadata() throws Exception { // should allow get resource metadata - Map resource = api.resource("api_test", ObjectUtils.emptyMap()); + Map resource = api.resource(API_TEST, ObjectUtils.emptyMap()); assertNotNull(resource); - assertEquals(resource.get("public_id"), "api_test"); + assertEquals(resource.get("public_id"), API_TEST); assertEquals(resource.get("bytes"), 3381); assertEquals(((List) resource.get("derived")).size(), 1); } @@ -253,14 +258,14 @@ public void test07ResourceMetadata() throws Exception { public void test08DeleteDerived() throws Exception { // should allow deleting derived resource cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test3", "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + ObjectUtils.asMap("public_id", API_TEST_3, "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + Map resource = api.resource(API_TEST_3, ObjectUtils.emptyMap()); assertNotNull(resource); List derived = (List) resource.get("derived"); assertEquals(derived.size(), 1); String derived_resource_id = (String) derived.get(0).get("id"); api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); - resource = api.resource("api_test3", ObjectUtils.emptyMap()); + resource = api.resource(API_TEST_3, ObjectUtils.emptyMap()); assertNotNull(resource); derived = (List) resource.get("derived"); assertEquals(derived.size(), 0); @@ -269,11 +274,12 @@ public void test08DeleteDerived() throws Exception { @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test3")); - Map resource = api.resource("api_test3", ObjectUtils.emptyMap()); + String public_id = "api_,test3"; + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id)); + Map resource = api.resource(public_id, ObjectUtils.emptyMap()); assertNotNull(resource); - api.deleteResources(Arrays.asList("apit_test", "api_test2", "api_test3"), ObjectUtils.emptyMap()); - api.resource("api_test3", ObjectUtils.emptyMap()); + api.deleteResources(Arrays.asList(API_TEST, API_TEST_2, public_id), ObjectUtils.emptyMap()); + api.resource(public_id, ObjectUtils.emptyMap()); } @Test(expected = NotFound.class) @@ -308,7 +314,7 @@ public void test10Tags() throws Exception { @Test public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", "api_test")); + Map result = api.tags(ObjectUtils.asMap("prefix", API_TEST)); List tags = (List) result.get("tags"); assertContains("api_test_tag", tags); result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); @@ -420,11 +426,11 @@ public void test19Ping() throws Exception { public void testDeleteAllResources() throws Exception { // should allow deleting all resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test5", "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - Map result = api.resource("api_test5", ObjectUtils.emptyMap()); + ObjectUtils.asMap("public_id", API_TEST_5, "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + Map result = api.resource(API_TEST_5, ObjectUtils.emptyMap()); assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); - result = api.resource("api_test5", ObjectUtils.emptyMap()); + result = api.resource(API_TEST_5, ObjectUtils.emptyMap()); // assertEquals(0, ((org.cloudinary.json.JSONArray) // result.get("derived")).size()); } From 33f1060f4bbeaeb31233ee0a41ff2af98b235942 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 12 May 2016 20:37:41 +0300 Subject: [PATCH 137/592] Remove API limits test --- .../java/com/cloudinary/test/AbstractApiTest.java | 15 --------------- 1 file changed, 15 deletions(-) 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 df5f8a4c..199327d1 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 @@ -517,21 +517,6 @@ public void testUpdateCustomCoordinates() throws IOException, Exception { } } - @Test - public void testApiLimits() throws Exception { - // should support reporting the current API limits found in the response - // header - ApiResponse result1 = api.transformations(ObjectUtils.emptyMap()); - ApiResponse result2 = api.transformations(ObjectUtils.emptyMap()); - assertNotNull(result1.apiRateLimit()); - assertNotNull(result2.apiRateLimit()); - assertEquals(result1.apiRateLimit().getRemaining() - 1, result2.apiRateLimit().getRemaining()); - assertTrue(result2.apiRateLimit().getLimit() > result2.apiRateLimit().getRemaining()); - assertEquals(result1.apiRateLimit().getLimit(), result2.apiRateLimit().getLimit()); - assertEquals(result1.apiRateLimit().getReset(), result2.apiRateLimit().getReset()); - assertTrue(result2.apiRateLimit().getReset().after(new java.util.Date())); - } - @Test public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets From 21ce4747dfe895e62945790e9400d6fb405cd1cd Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Fri, 13 May 2016 00:38:37 +0300 Subject: [PATCH 138/592] Add `next_cursor` to `Api#transformation()` --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- 1 file changed, 1 insertion(+), 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 ac45b1b2..3f4539ab 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -165,7 +165,7 @@ public ApiResponse transformations(Map options) throws Exception { public ApiResponse transformation(String transformation, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("transformations", transformation), ObjectUtils.only(options, "max_results"), options); + return callApi(HttpMethod.GET, Arrays.asList("transformations", transformation), ObjectUtils.only(options, "next_cursor", "max_results"), options); } public ApiResponse deleteTransformation(String transformation, Map options) throws Exception { From 537efbb755b27db2e0ccdd5cbccdcdab742400b3 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 26 Apr 2016 16:50:42 +0300 Subject: [PATCH 139/592] Use "_method" with "delete" instead of HttpDelete. --- .../com/cloudinary/http43/ApiStrategy.java | 133 +++++++++++------- .../com/cloudinary/http44/ApiStrategy.java | 129 +++++++++-------- 2 files changed, 145 insertions(+), 117 deletions(-) diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index b4d38375..ba382c0a 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -1,38 +1,37 @@ package com.cloudinary.http43; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.TimeUnit; - +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.HttpHost; -import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; +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.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; -import com.cloudinary.Api; -import com.cloudinary.Uploader; -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 java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { @@ -69,8 +68,9 @@ public void init(Api api) { } @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) options = ObjectUtils.emptyMap(); + 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); @@ -85,33 +85,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map 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())); - } - } - - 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; - } + HttpUriRequest request = prepareRequest(method, apiUrl, params); request.setHeader("Authorization", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); @@ -147,4 +121,59 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map params) throws URISyntaxException, UnsupportedEncodingException { + URI apiUri; + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + List parameters; + HttpUriRequest request; + parameters = prepareParams(params); + if(method == HttpMethod.GET) { + apiUrlBuilder.setParameters(parameters); + 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 + parameters.add(new BasicNameValuePair("_method", "delete")); + //continue with POST + case POST: + request = new HttpPost(apiUri); + break; + default: + throw new IllegalArgumentException("Unknown HTTP method"); + } + ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(parameters)); + } + return request; + } + + private 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/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index 1e4e195d..97694bed 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -1,20 +1,19 @@ package com.cloudinary.http44; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - +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.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.config.RequestConfig; import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; @@ -24,15 +23,15 @@ import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; -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 java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { @@ -69,8 +68,7 @@ public void init(Api api) { } @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - URI apiUri; + public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); @@ -87,30 +85,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map params) throws UnsupportedEncodingException { - HttpUriRequest request;List entities = new ArrayList(params.size()); - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (String single : (Iterable) param.getValue()) { - entities.add(new BasicNameValuePair(param.getKey() + "[]", single)); - } - } else { - entities.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); + /** + * 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) throws URISyntaxException, UnsupportedEncodingException { + URI apiUri; + URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); + List parameters; + HttpUriRequest request; + parameters = prepareParams(params); + if(method == HttpMethod.GET) { + apiUrlBuilder.setParameters(parameters); + 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 + parameters.add(new BasicNameValuePair("_method", "delete")); + //continue with POST + case POST: + request = new HttpPost(apiUri); + break; + default: + throw new IllegalArgumentException("Unknown HTTP method"); } + ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(parameters)); } - put.setEntity(new UrlEncodedFormEntity(entities)); - request = put; return request; } - private URI prepareUrlParams(URIBuilder urlBuilder, Map params) throws URISyntaxException { - URI apiUri; - for (Map.Entry param : params.entrySet()) { + private List prepareParams(Map params) { + List requestParams = new ArrayList(params.size()); + for (Map.Entry param : params.entrySet()) { if (param.getValue() instanceof Iterable) { - for (String single : (Iterable) param.getValue()) { - urlBuilder.addParameter(param.getKey() + "[]", single); + for (Object single : (Iterable) param.getValue()) { + requestParams.add(new BasicNameValuePair(param.getKey() + "[]", ObjectUtils.asString(single))); } } else { - urlBuilder.addParameter(param.getKey(), ObjectUtils.asString(param.getValue())); + requestParams.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); } } - apiUri = urlBuilder.build(); - return apiUri; + + + return requestParams; } } From dfc884137849222e9a06d230b001f44d80cb9bf7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 16 May 2016 11:29:42 +0300 Subject: [PATCH 140/592] Add SDK_TEST_TAG to all resources being created. --- .../com/cloudinary/test/AbstractApiTest.java | 94 +++++------- .../cloudinary/test/AbstractUploaderTest.java | 135 ++++++++---------- 2 files changed, 100 insertions(+), 129 deletions(-) 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 199327d1..ffb83faa 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 @@ -1,30 +1,5 @@ package com.cloudinary.test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; @@ -33,6 +8,14 @@ import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.utils.ObjectUtils; +import org.junit.*; +import org.junit.rules.TestName; + +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeNotNull; @SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractApiTest { @@ -43,9 +26,10 @@ abstract public class AbstractApiTest { public static final String API_TEST_2 = "api_test2"; public static final String API_TEST_3 = "api_test3"; public static final String API_TEST_5 = "api_test5"; + public static final String SDK_TEST_TAG = "cloudinary_java_test"; private Cloudinary cloudinary; protected Api api; - private static String uniqueTag = String.format("api_test_tag_%d", new java.util.Date().getTime()); + private static String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); @BeforeClass public static void setUpClass() throws IOException { @@ -87,7 +71,7 @@ public static void setUpClass() throws IOException { api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); } catch (Exception e) { } - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{"api_test_tag", uniqueTag}, "context", "key=value", "eager", + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "key=value", "eager", Collections.singletonList(new Transformation().width(100).crop("scale"))); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options.put("public_id", API_TEST_1); @@ -181,7 +165,7 @@ public void test05ResourcesByPrefix() throws Exception { boolean found = false; for (Map r : resources) { ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); + found = found || tags.contains(SDK_TEST_TAG); } assertTrue(found); } @@ -204,7 +188,7 @@ public void testResourcesListingStartAt() throws Exception { Thread.sleep(2000L); java.util.Date startAt = new java.util.Date(); Thread.sleep(2000L); - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); List resources = (List) listResources.get("resources"); assertEquals(response.get("public_id"), resources.get(0).get("public_id")); @@ -222,7 +206,7 @@ public void testResourcesByPublicIds() throws Exception { boolean found = false; for (Map r : resources) { ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); + found = found || tags.contains(SDK_TEST_TAG); } assertTrue(found); } @@ -230,7 +214,7 @@ public void testResourcesByPublicIds() throws Exception { @Test public void test06ResourcesTag() throws Exception { // should allow listing resources by tag - Map result = api.resourcesByTag("api_test_tag", ObjectUtils.asMap("tags", true, "context", true)); + Map result = api.resourcesByTag(SDK_TEST_TAG, ObjectUtils.asMap("tags", true, "context", true)); Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); @@ -239,7 +223,7 @@ public void test06ResourcesTag() throws Exception { boolean found = false; for (Map r : resources) { ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains("api_test_tag"); + found = found || tags.contains(SDK_TEST_TAG); } assertTrue(found); } @@ -258,7 +242,7 @@ public void test07ResourceMetadata() throws Exception { public void test08DeleteDerived() throws Exception { // should allow deleting derived resource cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", API_TEST_3, "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + ObjectUtils.asMap("public_id", API_TEST_3, "tags", SDK_TEST_TAG, "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); Map resource = api.resource(API_TEST_3, ObjectUtils.emptyMap()); assertNotNull(resource); List derived = (List) resource.get("derived"); @@ -275,7 +259,7 @@ public void test08DeleteDerived() throws Exception { public void test09DeleteResources() throws Exception { // should allow deleting resources String public_id = "api_,test3"; - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id, "tags", SDK_TEST_TAG)); Map resource = api.resource(public_id, ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResources(Arrays.asList(API_TEST, API_TEST_2, public_id), ObjectUtils.emptyMap()); @@ -285,7 +269,7 @@ public void test09DeleteResources() throws Exception { @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix", "tags", SDK_TEST_TAG)); Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); @@ -308,15 +292,15 @@ public void test10Tags() throws Exception { // should allow listing tags Map result = api.tags(ObjectUtils.emptyMap()); List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); + assertContains(SDK_TEST_TAG, tags); } @Test public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", API_TEST)); + Map result = api.tags(ObjectUtils.asMap("prefix", SDK_TEST_TAG.substring(0,SDK_TEST_TAG.length()-1))); List tags = (List) result.get("tags"); - assertContains("api_test_tag", tags); + assertContains(SDK_TEST_TAG, tags); result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); tags = (List) result.get("tags"); assertEquals(0, tags.size()); @@ -426,7 +410,7 @@ public void test19Ping() throws Exception { public void testDeleteAllResources() throws Exception { // should allow deleting all resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", API_TEST_5, "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + ObjectUtils.asMap("public_id", API_TEST_5, "tags", SDK_TEST_TAG, "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); Map result = api.resource(API_TEST_5, ObjectUtils.emptyMap()); assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); @@ -438,8 +422,8 @@ public void testDeleteAllResources() throws Exception { @Test public void testManualModeration() throws Exception { // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved", "tags", SDK_TEST_TAG)); assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); } @@ -447,7 +431,7 @@ public void testManualModeration() throws Exception { public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -459,7 +443,7 @@ public void testOcrUpdate() { public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -471,7 +455,7 @@ public void testRawConvertUpdate() { public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -483,7 +467,7 @@ public void testCategorizationUpdate() { public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -495,7 +479,7 @@ public void testDetectionUpdate() { public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -507,7 +491,7 @@ public void testSimilaritySearchUpdate() { public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[]{121, 31, 110, 151}; @@ -591,9 +575,9 @@ public void testUpdateUploadPreset() throws Exception { @Test public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value - Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); - Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); @@ -616,10 +600,10 @@ public void testListByModerationUpdate() throws Exception { // @Test public void testFolderApi() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item", "tags", SDK_TEST_TAG)); Map result = api.rootFolders(null); assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); @@ -638,7 +622,7 @@ public void testFolderApi() throws Exception { public void testRestore() throws Exception { // should support restoring resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test_restore", "backup", true)); + ObjectUtils.asMap("public_id", "api_test_restore", "backup", true, "tags", SDK_TEST_TAG)); Map resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); assertEquals(resource.get("bytes"), 3381); api.deleteResources(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); 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 739e603f..76a11ebe 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,36 +1,20 @@ package com.cloudinary.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeNotNull; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.ZipInputStream; -import java.net.*; - import com.cloudinary.*; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.AfterClass; -import org.junit.Rule; -import org.junit.Test; +import org.junit.*; import org.junit.rules.TestName; -import com.cloudinary.ResponsiveBreakpoint; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.Rectangle; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.*; +import java.util.zip.ZipInputStream; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeNotNull; @SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractUploaderTest { @@ -38,6 +22,7 @@ abstract public class AbstractUploaderTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; private static final String ARCHIVE_TAG = "archive_tag_" + String.valueOf(System.currentTimeMillis()); + private static final String SDK_TEST_TAG = "cloudinary_java_test"; public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; private Cloudinary cloudinary; @@ -49,9 +34,9 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); } - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", ARCHIVE_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG})); cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("tags", ARCHIVE_TAG, + ObjectUtils.asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, "transformation", new Transformation().crop("scale").width(10))); } @@ -73,7 +58,7 @@ public void setUp() { @Test public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true, "tags", SDK_TEST_TAG)); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); @@ -87,7 +72,7 @@ public void testUpload() throws IOException { @Test public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); Map to_sign = new HashMap(); @@ -99,7 +84,7 @@ public void testUploadUrl() throws IOException { @Test public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertEquals(result.get("width"), 16); assertEquals(result.get("height"), 16); Map to_sign = new HashMap(); @@ -111,37 +96,37 @@ public void testUploadDataUri() throws IOException { @Test public void testUploadUTF8() throws IOException { - Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é")); + Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é", "tags", SDK_TEST_TAG)); assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); - cloudinary.uploader().upload(result.get("url"), ObjectUtils.emptyMap()); + cloudinary.uploader().upload(result.get("url"), ObjectUtils.asMap("tags", SDK_TEST_TAG)); } @Test public void testRename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); Object publicId = result.get("public_id"); String publicId2 = "folder/" + publicId + "2"; cloudinary.uploader().rename((String) publicId, publicId2, ObjectUtils.emptyMap()); assertNotNull(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap())); - Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", ObjectUtils.emptyMap()); + Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", ObjectUtils.asMap("tags", SDK_TEST_TAG)); boolean error_found=false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.emptyMap()); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("tags", SDK_TEST_TAG)); } catch(Exception e) { error_found=true; } assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("overwrite", true)); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("overwrite", true, "tags", SDK_TEST_TAG)); assertEquals(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap()).get("format"), "ico"); } @Test public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "tags", SDK_TEST_TAG)); assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false)); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false, "tags", SDK_TEST_TAG)); assertEquals(result.get("public_id"), "old_logo"); } @@ -156,18 +141,18 @@ public void testExplicit() throws IOException { @Test public void testEager() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", SDK_TEST_TAG)); } @Test public void testHeaders() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"}, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"), "tags", SDK_TEST_TAG)); } @Test public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().text("hello world", ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertTrue(((Integer) result.get("width")) > 1); assertTrue(((Integer) result.get("height")) > 1); } @@ -185,21 +170,22 @@ public void testImageUploadTag() { @Test public void testSprite() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - Map result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap()); + final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2")); + Map result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertEquals(2, ((Map) result.get("image_infos")).size()); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100")); + result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", "w_100")); assertTrue(((String) result.get("css_url")).contains("w_100")); - result = cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); + result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); } @Test public void testMulti() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap()); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_2")); + Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertTrue(((String) result.get("url")).endsWith(".gif")); result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); assertTrue(((String) result.get("url")).contains("w_100")); @@ -232,7 +218,7 @@ public void testTags() throws Exception { public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); assertEquals(result.get("format"), "png"); } @@ -242,7 +228,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { boolean errorFound = false; String[] formats = {"jpg"}; try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); } catch (Exception e) { errorFound = true; } @@ -253,7 +239,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg", "tags", SDK_TEST_TAG)); assertEquals("jpg", result.get("format")); } @@ -265,7 +251,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120, 30, 109, 150); coordinates.addRect(rect1); coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true, "tags", SDK_TEST_TAG)); ArrayList resultFaces = ((ArrayList) result.get("faces")); assertEquals(2, resultFaces.size()); @@ -304,7 +290,7 @@ public void testFaceCoordinates() throws Exception { public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,300,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates, "tags", SDK_TEST_TAG)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[]{121, 31, SRC_TEST_IMAGE_W, SRC_TEST_IMAGE_H}; Object[] actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); @@ -326,7 +312,7 @@ public void testCustomCoordinates() throws Exception { public void testContext() throws Exception { //should allow sending context Map context = ObjectUtils.asMap("caption", "some cäption", "alt", "alternativè"); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context, "tags", SDK_TEST_TAG)); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); @@ -338,7 +324,7 @@ public void testContext() throws Exception { @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); } @@ -348,7 +334,7 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { //should support requesting raw conversion try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -358,7 +344,7 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { //should support requesting categorization try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); } @@ -368,7 +354,7 @@ public void testCategorizationRequest() { public void testDetectionRequest() { //should support requesting detection try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -378,7 +364,7 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { //should support requesting auto tagging try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f, "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("^Must use(.*)")); } @@ -401,27 +387,28 @@ public void testUploadLarge() throws Exception { } out.close(); assertEquals(5880138, temp.length()); - ArrayList tags = new java.util.ArrayList(); - tags.add("upload_large_tag"); + + String [] tags = new String [] {"upload_large_tag", SDK_TEST_TAG}; Map resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); - assertEquals(tags, resource.get("tags")); + assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); + assertEquals("raw", resource.get("resource_type")); resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5243000, "tags", tags)); - assertEquals(tags, resource.get("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")); resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); - assertEquals(tags, resource.get("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")); resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); - assertEquals(tags, resource.get("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")); @@ -431,14 +418,14 @@ public void testUploadLarge() throws Exception { public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.emptyMap()); + Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.asMap("tags", SDK_TEST_TAG)); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } @Test public void testFilenameOption() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif", "tags", SDK_TEST_TAG)); assertEquals("emanelif", result.get("original_filename")); } @@ -448,7 +435,7 @@ public void testResponsiveBreakpoints() throws Exception { // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", - breakpoint + breakpoint, "tags", SDK_TEST_TAG )); java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); @@ -456,7 +443,7 @@ public void testResponsiveBreakpoints() throws Exception { // an array of breakpoints result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", - new ResponsiveBreakpoint[]{breakpoint} + new ResponsiveBreakpoint[]{breakpoint}, "tags", SDK_TEST_TAG )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); @@ -465,7 +452,7 @@ public void testResponsiveBreakpoints() throws Exception { // a JSONArray of breakpoints JSONArray array = new JSONArray(); array.put(breakpoint); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array, "tags", SDK_TEST_TAG )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); From 4a4c6d5d74f39b2071b2ba1c2158d25197d65a2e Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 16 May 2016 12:43:44 +0300 Subject: [PATCH 141/592] Use dynamic tag in sprites test --- .../main/java/com/cloudinary/test/UploaderTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 22441541..963646b0 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -181,13 +181,14 @@ public void testText() throws Exception { public void testSprite() throws Exception { if (cloudinary.config.apiSecret == null) return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "sprite_test_tag", "public_id", "sprite_test_tag_2")); - JSONObject result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.emptyMap())); + final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_2")); + JSONObject result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.emptyMap())); assertEquals(2, result.getJSONObject("image_infos").length()); - result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", ObjectUtils.asMap("transformation", "w_100"))); + result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", "w_100"))); assertTrue((result.getString("css_url")).contains("w_100")); - result = new JSONObject(cloudinary.uploader().generate_sprite("sprite_test_tag", + result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg"))); assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); } From a6dc0bc3b20178f51ada3913c597c14b1f4371a2 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 16 May 2016 12:44:19 +0300 Subject: [PATCH 142/592] Add script to create unsigned upload preset for the Android test --- .../scripts/create_unsigned_preset.sh | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 cloudinary-test-common/scripts/create_unsigned_preset.sh diff --git a/cloudinary-test-common/scripts/create_unsigned_preset.sh b/cloudinary-test-common/scripts/create_unsigned_preset.sh new file mode 100644 index 00000000..30e0c238 --- /dev/null +++ b/cloudinary-test-common/scripts/create_unsigned_preset.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Create the unsigned upload preset required for tests +# Currently only required for the Android test since Android API cannot create the preset + +UNSIGNED_PRESET="cloudinary_java_test" +SDK_TEST_TAG="cloudinary_java_test" + +if [ -z ${CLOUDINARY_URL+x} ] + then echo "The variable CLOUDINARY_URL must be set!" +else + + API_CRED=${CLOUDINARY_URL%@*} + API_CRED=${API_CRED#*//} + if curl -s "https://${API_CRED#*//}@api.cloudinary.com/v1_1/${CLOUDINARY_URL#*@}/upload_presets/${UNSIGNED_PRESET}" | \ + grep --quiet "Can't find upload preset named" + then curl --data "name=${UNSIGNED_PRESET}&unsigned=true&tags=${TAG}" \ + "https://${API_CRED#*//}@api.cloudinary.com/v1_1/${CLOUDINARY_URL#*@}/upload_presets" + echo + else + echo "Preset already exists" + fi +fi \ No newline at end of file From d82eddcb449e695a31b55856049193249799b1bd Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 16 May 2016 12:48:59 +0300 Subject: [PATCH 143/592] Add script to create unsigned upload preset for the Android test --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 590641cd..4e74bd91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ + +cloudinary-parent-1.4.2 / 2016-05-16 +==================================== + + * Sent params as entities for PUT, POST + * Use "_method" with "delete" instead of HttpDelete. + * Add `next_cursor` to `Api#transformation()` + * Add script to create unsigned upload preset for the Android test + * Use dynamic tag in sprites test + * Add SDK_TEST_TAG to all resources being created. + * Remove API limits test + cloudinary-parent-1.4.1 / 2016-03-23 ==================================== * Rename conditional parameters `faces` and `pages` to `faceCount` and `pageCount` From 227ae6e318bcc98078da77eaa4b625111b258725 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 16 May 2016 16:01:37 +0300 Subject: [PATCH 144/592] Update Google App Engine demo --- CHANGELOG.md | 1 + samples/photo_album_gae/pom.xml | 11 ++++++++--- .../java/cloudinary/controllers/PhotoController.java | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e74bd91..7b89182f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ cloudinary-parent-1.4.2 / 2016-05-16 * Sent params as entities for PUT, POST * Use "_method" with "delete" instead of HttpDelete. * Add `next_cursor` to `Api#transformation()` + * Update Google App Engine demo * Add script to create unsigned upload preset for the Android test * Use dynamic tag in sprites test * Add SDK_TEST_TAG to all resources being created. diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index 4dbfe92a..2d0c00a3 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -8,9 +8,9 @@ photo_album_gae - 3.2.0.RELEASE + 3.2.16.RELEASE 1 - 1.8.9 + 1.9.37 UTF-8 @@ -38,7 +38,12 @@ com.cloudinary cloudinary-taglib - 1.1.0 + 1.4.1 + + + com.cloudinary + cloudinary-http42 + 1.4.1 org.springframework 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 4dbfa915..53e8b537 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 @@ -55,7 +55,7 @@ public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult ObjectUtils.asMap("resource_type", "auto")); photoUpload.setPublicId((String) uploadResult.get("public_id")); - photoUpload.setVersion((Long) uploadResult.get("version")); + photoUpload.setVersion(((Integer) uploadResult.get("version")).longValue()); photoUpload.setSignature((String) uploadResult.get("signature")); photoUpload.setFormat((String) uploadResult.get("format")); photoUpload.setResourceType((String) uploadResult.get("resource_type")); @@ -87,4 +87,4 @@ public String directUploadPhotoForm(ModelMap model) { model.addAttribute("photo", new PhotoUpload()); return "direct_upload_form"; } -} \ No newline at end of file +} From b2bd353686bd3b926c31934c86cf5e62e78dd666 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 18 May 2016 08:51:47 +0300 Subject: [PATCH 145/592] Prepare for version 1.4.2 --- README.md | 4 ++-- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb4c4b75..81ed2380 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.1 + 1.4.2 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.1/cloudinary-core-1.4.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.1/cloudinary-http44-1.4.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.2/cloudinary-core-1.4.2.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.2/cloudinary-http44-1.4.2.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 9cd37413..3fd8a174 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.1"; + public final static String VERSION = "1.4.2"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From ba3db93c15eb478e286f831c876c19cbc6446131 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 18 May 2016 09:09:25 +0300 Subject: [PATCH 146/592] [maven-release-plugin] prepare release 1.4.2 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 5931bea0..7edf4467 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 com.cloudinary cloudinary-android-test - 1.4.2-SNAPSHOT + 1.4.2 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.2-SNAPSHOT + 1.4.2 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 0698fa14..0b95301e 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 2f3dccee..eae3928a 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 417d8266..cdb3c634 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 3c454919..b4a10a4f 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 9d07dbc7..afdb5a78 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index e66f1bdc..191d34ca 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index cc0bbf45..c33de2b8 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 cloudinary-test-common diff --git a/pom.xml b/pom.xml index ae4b6bbf..03f9d725 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.2-SNAPSHOT + 1.4.2 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.2 From f68866bef839822b09df7c70e226aaed7aeccf0c Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 18 May 2016 09:09:30 +0300 Subject: [PATCH 147/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 7edf4467..fe7e1cd5 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.2 + 1.4.3-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.2 + 1.4.3-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 0b95301e..29613666 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index eae3928a..f86131a7 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index cdb3c634..b185fd6e 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index b4a10a4f..37a787e6 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index afdb5a78..0e549274 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 191d34ca..7589e10d 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index c33de2b8..f82976a6 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 03f9d725..ce2bad88 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.2 + 1.4.3-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.2 + HEAD From 2ce2a7eab8e85c40f12acb3892491d776a907212 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 28 Jul 2016 08:59:57 +0300 Subject: [PATCH 148/592] Add tests for auto width and original width and height ( "ow", "oh") values --- .../java/com/cloudinary/Transformation.java | 19 ++++++++-- .../com/cloudinary/test/CloudinaryTest.java | 36 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 96ef9203..a9598924 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -469,10 +469,10 @@ public String generate(Map options) { String angle = StringUtils.join(ObjectUtils.asArray(options.get("angle")), "."); boolean noHtmlSizes = hasLayer || StringUtils.isNotBlank(angle) || "fit".equals(crop) || "limit".equals(crop); - if (width != null && (width.equals("auto") || Float.parseFloat(width) < 1 || noHtmlSizes || isResponsive)) { + if (width != null && (width.startsWith("auto") || !isValidAttrValue(width) || noHtmlSizes || isResponsive)) { this.htmlWidth = null; } - if (height != null && (Float.parseFloat(height) < 1 || noHtmlSizes || isResponsive)) { + if (height != null && (!isValidAttrValue(height) || noHtmlSizes || isResponsive)) { this.htmlHeight = null; } @@ -607,6 +607,21 @@ public String generate(Map options) { return StringUtils.join(transformations, "/"); } + /** + * Check if the value is a float >= 1 + * @param value + * @return true if the value is a float >= 1 + */ + private boolean isValidAttrValue(String value) { + final float parseFloat; + try { + parseFloat = Float.parseFloat(value); + } catch (NumberFormatException e) { + return false; + } + return parseFloat >= 1; + } + public String getHtmlWidth() { return htmlWidth; } 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 289a42ec..ad3c2fca 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -272,6 +272,20 @@ public void testVariousOptions() { assertEquals(DEFAULT_UPLOAD_PATH + "g_center,p_a,q_0.4,r_3,x_1,y_2/test", result); } + @Test + public void testQuality() { + // should use x, y, radius, prefix, gravity and quality from options + Transformation transformation = new Transformation().quality(0.4); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "q_0.4/test", result); + transformation = new Transformation().quality("auto"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "q_auto/test", result); + transformation = new Transformation().quality("auto:good"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "q_auto:good/test", result); + } + @Test public void testTransformationSimple() { // should support named transformation @@ -576,6 +590,28 @@ public void testResponsiveWidth() { assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_pad,w_auto/test", result); Transformation.setResponsiveWidthTransformation(null); } + @Test + public void testShouldSupportAutoWidth(){ + String trans; + + trans = new Transformation().width("auto:20").crop("fill").generate(); + assertEquals("c_fill,w_auto:20", trans); + trans = new Transformation().width("auto:20:350").crop("fill").generate(); + assertEquals("c_fill,w_auto:20:350", trans); + trans = new Transformation().width("auto:breakpoints").crop("fill").generate(); + assertEquals("c_fill,w_auto:breakpoints", trans); + trans = new Transformation().width("auto:breakpoints_100_1900_20_15").crop("fill").generate(); + assertEquals("c_fill,w_auto:breakpoints_100_1900_20_15", trans); + trans = new Transformation().width("auto:breakpoints:json").crop("fill").generate(); + assertEquals("c_fill,w_auto:breakpoints:json", trans); + } + + + @Test + public void testShouldSupportOhOw(){ + String trans = new Transformation().width("ow").height("oh").crop("crop").generate(); + assertEquals("c_crop,h_oh,w_ow", trans); + } @Test public void testVideoCodec() { From 5fd48bb9bf914b3ff385df29d3175533a89600d0 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sat, 30 Jul 2016 22:54:41 +0300 Subject: [PATCH 149/592] Support Client Hints. --- .../java/com/cloudinary/Configuration.java | 39 ++++++++++++++++++- .../src/main/java/com/cloudinary/Url.java | 2 +- .../com/cloudinary/test/CloudinaryTest.java | 20 ++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 10d22752..97dffcb4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -38,6 +38,7 @@ public class Configuration { public boolean useRootPath; public int timeout; public boolean loadStrategies = true; + public boolean clientHints = false; public Configuration() { } @@ -90,8 +91,34 @@ public void update(Map config) { this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); 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); } + @SuppressWarnings("rawtypes") + public Map asMap() { + Map map = new HashMap(); + map.put("cloud_name", cloudName); + map.put("api_key", apiKey); + map.put("api_secret", apiSecret); + map.put("secure_distribution", secureDistribution); + map.put("cname", cname); + map.put("secure", secure); + map.put("private_cdn", privateCdn); + map.put("cdn_subdomain", cdnSubdomain); + map.put("shorten", shorten); + map.put("upload_prefix", uploadPrefix); + map.put("callback", callback); + map.put("proxy_host", proxyHost); + map.put("proxy_port", proxyPort); + map.put("secure_cdn_subdomain", secureCdnSubdomain); + map.put("use_root_path", useRootPath); + map.put("load_strategies", loadStrategies); + map.put("timeout", timeout); + map.put("client_hints", clientHints); + return map; + } + + public Configuration(Configuration other) { this.cloudName = other.cloudName; this.apiKey = other.apiKey; @@ -109,6 +136,7 @@ public Configuration(Configuration other) { this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; this.timeout = other.timeout; + this.clientHints = other.clientHints; } /** @@ -198,6 +226,7 @@ public static class Builder { private boolean useRootPath; private boolean loadStrategies = true; private int timeout; + private boolean clientHints = false; /** * Set the HTTP connection timeout. @@ -214,7 +243,9 @@ public Builder setTimeout(int timeout) { * Creates a {@link Configuration} with the arguments supplied to this builder */ public Configuration build() { - return new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies); + final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies); + configuration.clientHints = clientHints; + return configuration; } /** @@ -317,6 +348,11 @@ public Builder setLoadStrategies(boolean loadStrategies) { return this; } + public Builder setClientHints(boolean clientHints) { + this.clientHints = clientHints; + return this; + } + /** * Initialize builder from existing {@link Configuration} * @@ -341,6 +377,7 @@ public Builder from(Configuration other) { this.useRootPath = other.useRootPath; this.loadStrategies = other.loadStrategies; this.timeout = other.timeout; + this.clientHints = other.clientHints; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index c26d128f..628d4aae 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -506,7 +506,7 @@ public String imageTag(String source, Map attributes) { boolean hiDPI = transformation().isHiDPI(); boolean responsive = transformation().isResponsive(); - if (hiDPI || responsive) { + if (!config.clientHints && (hiDPI || responsive)) { attributes.put("data-src", url); String extraClass = responsive ? "cld-responsive" : "cld-hidpi"; attributes.put("class", (StringUtils.isBlank(attributes.get("class")) ? "" : attributes.get("class") + " ") + extraClass); 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 ad3c2fca..206456c5 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -494,6 +494,26 @@ public void testImageTag() { result); } + @Test + public void testClientHints() { + String testTag; + String message = "should not implement responsive behaviour if client hints is true"; + cloudinary.config.clientHints = true; + Transformation trans = new Transformation() + .crop("scale") + .width("auto") + .dpr("auto"); + testTag = cloudinary.url().transformation(trans).imageTag("sample.jpg"); + assertTrue(testTag.startsWith(" Date: Sun, 31 Jul 2016 11:28:41 +0300 Subject: [PATCH 150/592] Add Hamcrest tests. --- cloudinary-test-common/pom.xml | 5 + .../com/cloudinary/test/AbstractApiTest.java | 207 ++++++++++-------- pom.xml | 6 + 3 files changed, 123 insertions(+), 95 deletions(-) diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index f82976a6..72482fb0 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -17,6 +17,11 @@ cloudinary-core ${project.version} + + org.hamcrest + java-hamcrest + 2.0.0.0 + junit junit 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 ffb83faa..08985d31 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 @@ -8,28 +8,41 @@ import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.utils.ObjectUtils; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.equalTo; import org.junit.*; import org.junit.rules.TestName; import java.io.IOException; import java.util.*; +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; -@SuppressWarnings({"rawtypes", "unchecked"}) +@SuppressWarnings({"rawtypes", "unchecked", "JavaDoc"}) abstract public class AbstractApiTest { - public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; - public static final String API_TEST = "api_test"; - public static final String API_TEST_1 = "api_test1"; - public static final String API_TEST_2 = "api_test2"; - public static final String API_TEST_3 = "api_test3"; - public static final String API_TEST_5 = "api_test5"; - public static final String SDK_TEST_TAG = "cloudinary_java_test"; + private static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; + private static final int SUFFIX = new Random().nextInt(99999); + private static final String API_TEST = "api_test_" + SUFFIX; + private static final String API_TEST_1 = API_TEST + "_1"; + private static final String API_TEST_2 = API_TEST + "_2"; + private static final String API_TEST_3 = API_TEST + "_3"; + private static final String API_TEST_5 = API_TEST + "_5"; + private static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; + public static final String API_TEST_TRANSFORMATION = "api_test_transformation_" + SUFFIX; + public static final String API_TEST_TRANSFORMATION_2 = API_TEST_TRANSFORMATION + "2"; + public static final String API_TEST_TRANSFORMATION_3 = API_TEST_TRANSFORMATION + "3"; + public static final String API_TEST_UPLOAD_PRESET = "api_test_upload_preset_" + SUFFIX; + public static final String API_TEST_UPLOAD_PRESET_2 = API_TEST_UPLOAD_PRESET + "2"; + public static final String API_TEST_UPLOAD_PRESET_3 = API_TEST_UPLOAD_PRESET + "3"; + public static final String API_TEST_UPLOAD_PRESET_4 = API_TEST_UPLOAD_PRESET + "4"; private Cloudinary cloudinary; protected Api api; - private static String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); + private static final String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); @BeforeClass public static void setUpClass() throws IOException { @@ -38,46 +51,51 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); return; } + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "key=value", "eager", + Collections.singletonList(new Transformation().width(100).crop("scale"))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options.put("public_id", API_TEST_1); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + } + + @AfterClass + public static void tearDownClass() { + Cloudinary cloudinary = new Cloudinary(); Api api = cloudinary.api(); try { api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); - } catch (Exception e) { + } catch (Exception ignored) { } try { - api.deleteTransformation("api_test_transformation", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteTransformation(API_TEST_TRANSFORMATION, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteTransformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteTransformation("api_test_transformation3", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteTransformation(API_TEST_TRANSFORMATION_3, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_2, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_3, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } try { - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - } catch (Exception e) { + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_4, ObjectUtils.emptyMap()); + } catch (Exception ignored) { } - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "key=value", "eager", - Collections.singletonList(new Transformation().width(100).crop("scale"))); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options.put("public_id", API_TEST_1); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - } + } @Rule public TestName currentTest = new TestName(); @@ -104,16 +122,15 @@ public Map findByAttr(List elements, String attr, Object value) { public void test01ResourceTypes() throws Exception { // should allow listing resource_types Map result = api.resourceTypes(ObjectUtils.emptyMap()); - assertContains("image", (Collection) result.get("resource_types")); + assertThat( (Collection) result.get("resource_types"), hasItem("image")); } @Test public void test02Resources() throws Exception { // should allow listing resources Map result = api.resources(ObjectUtils.emptyMap()); - Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); + final List resources = (List>) result.get("resources"); + assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST),hasEntry("type", "upload")))); } @Test @@ -122,9 +139,8 @@ public void testTimeoutParameter() throws Exception { Map options = new HashMap(); options.put("timeout", Integer.valueOf(5000)); Map result = api.resources(options); - Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); - assertNotNull(resource); - assertEquals(resource.get("type"), "upload"); + List resources = (List>) result.get("resources"); + assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST),hasEntry("type", "upload")))); } @Test @@ -150,24 +166,22 @@ public void test03ResourcesCursor() throws Exception { public void test04ResourcesByType() throws Exception { // should allow listing resources by type Map result = api.resources(ObjectUtils.asMap("type", "upload")); - Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); - assertNotNull(resource); + List resources = (List>) result.get("resources"); + assertThat(resources, hasItem(hasEntry("public_id", API_TEST))); } @Test public void test05ResourcesByPrefix() throws Exception { // should allow listing resources by prefix Map result = api.resources(ObjectUtils.asMap("type", "upload", "prefix", API_TEST, "tags", true, "context", true)); - List resources = (List) result.get("resources"); - assertNotNull(findByAttr(resources, "public_id", API_TEST)); - assertNotNull(findByAttr(resources, "public_id", API_TEST_1)); - assertNotNull(findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value")))); - boolean found = false; - for (Map r : resources) { - ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains(SDK_TEST_TAG); - } - assertTrue(found); + List resources = (List>) result.get("resources"); + System.out.println(resources); + assertThat(resources, hasItem(hasEntry("public_id", (Object) API_TEST))); + assertThat(resources, hasItem(hasEntry("public_id", (Object) API_TEST_1))); +// resources = (List>) result.get("resources"); + assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST),hasEntry("type", "upload")))); + assertThat(resources, hasItem(hasEntry("context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))))); + assertThat(resources, hasItem(hasEntry(equalTo("tags"), hasItem( SDK_TEST_TAG)))); } @Test @@ -248,7 +262,7 @@ public void test08DeleteDerived() throws Exception { List derived = (List) resource.get("derived"); assertEquals(derived.size(), 1); String derived_resource_id = (String) derived.get(0).get("id"); - api.deleteDerivedResources(Arrays.asList(derived_resource_id), ObjectUtils.emptyMap()); + api.deleteDerivedResources(Collections.singletonList(derived_resource_id), ObjectUtils.emptyMap()); resource = api.resource(API_TEST_3, ObjectUtils.emptyMap()); assertNotNull(resource); derived = (List) resource.get("derived"); @@ -280,19 +294,19 @@ public void test09aDeleteResourcesByPrefix() throws Exception { public void test09aDeleteResourcesByTags() throws Exception { // should allow deleting resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test4", "tags", Arrays.asList("api_test_tag_for_delete"))); - Map resource = api.resource("api_test4", ObjectUtils.emptyMap()); + ObjectUtils.asMap("public_id", API_TEST + "_4", "tags", Collections.singletonList("api_test_tag_for_delete"))); + Map resource = api.resource(API_TEST + "_4", ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); - api.resource("api_test4", ObjectUtils.emptyMap()); + api.resource(API_TEST + "_4", ObjectUtils.emptyMap()); } @Test public void test10Tags() throws Exception { // should allow listing tags - Map result = api.tags(ObjectUtils.emptyMap()); + Map result = api.tags(ObjectUtils.asMap("max_results", 500)); List tags = (List) result.get("tags"); - assertContains(SDK_TEST_TAG, tags); + assertThat( tags, hasItem(SDK_TEST_TAG)); } @Test @@ -300,7 +314,7 @@ public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix Map result = api.tags(ObjectUtils.asMap("prefix", SDK_TEST_TAG.substring(0,SDK_TEST_TAG.length()-1))); List tags = (List) result.get("tags"); - assertContains(SDK_TEST_TAG, tags); + assertThat( tags, hasItem(SDK_TEST_TAG)); result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); tags = (List) result.get("tags"); assertEquals(0, tags.size()); @@ -340,8 +354,8 @@ public void test14TransformationUpdate() throws Exception { @Test public void test15TransformationCreate() throws Exception { // should allow creating named transformation - api.createTransformation("api_test_transformation", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation", ObjectUtils.emptyMap()); + api.createTransformation(API_TEST_TRANSFORMATION, new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + Map transformation = api.transformation(API_TEST_TRANSFORMATION, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), true); assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(102).generate()); @@ -351,10 +365,10 @@ public void test15TransformationCreate() throws Exception { @Test public void test15aTransformationUnsafeUpdate() throws Exception { // should allow unsafe update of named transformation - api.createTransformation("api_test_transformation3", new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); - api.updateTransformation("api_test_transformation3", ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), + api.createTransformation(API_TEST_TRANSFORMATION_3, new Transformation().crop("scale").width(102).generate(), ObjectUtils.emptyMap()); + api.updateTransformation(API_TEST_TRANSFORMATION_3, ObjectUtils.asMap("unsafe_update", new Transformation().crop("scale").width(103).generate()), ObjectUtils.emptyMap()); - Map transformation = api.transformation("api_test_transformation3", ObjectUtils.emptyMap()); + Map transformation = api.transformation(API_TEST_TRANSFORMATION_3, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(new Transformation((List) transformation.get("info")).generate(), new Transformation().crop("scale").width(103).generate()); assertEquals(transformation.get("used"), false); @@ -363,14 +377,14 @@ public void test15aTransformationUnsafeUpdate() throws Exception { @Test public void test16aTransformationDelete() throws Exception { // should allow deleting named transformation - api.createTransformation("api_test_transformation2", new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); - api.deleteTransformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.createTransformation(API_TEST_TRANSFORMATION_2, new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); + api.transformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); + api.deleteTransformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); } @Test(expected = NotFound.class) public void test16bTransformationDelete() throws Exception { - api.transformation("api_test_transformation2", ObjectUtils.emptyMap()); + api.transformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); } @Test @@ -504,17 +518,17 @@ public void testUpdateCustomCoordinates() throws IOException, Exception { @Test public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset", "folder", "folder")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset2", "folder", "folder2")); - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset3", "folder", "folder3")); + api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET, "folder", "folder")); + api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET_2, "folder", "folder2")); + api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET_3, "folder", "folder3")); ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); - assertEquals(((Map) presets.get(0)).get("name"), "api_test_upload_preset3"); - assertEquals(((Map) presets.get(1)).get("name"), "api_test_upload_preset2"); - assertEquals(((Map) presets.get(2)).get("name"), "api_test_upload_preset"); - api.deleteUploadPreset("api_test_upload_preset", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset2", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset3", ObjectUtils.emptyMap()); + assertEquals(((Map) presets.get(0)).get("name"), API_TEST_UPLOAD_PRESET_3); + assertEquals(((Map) presets.get(1)).get("name"), API_TEST_UPLOAD_PRESET_2); + assertEquals(((Map) presets.get(2)).get("name"), API_TEST_UPLOAD_PRESET); + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET, ObjectUtils.emptyMap()); + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_2, ObjectUtils.emptyMap()); + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_3, ObjectUtils.emptyMap()); } @Test @@ -544,12 +558,12 @@ public void testGetUploadPreset() throws Exception { @Test public void testDeleteUploadPreset() throws Exception { // should allow deleting upload_presets", :upload_preset => true do - api.createUploadPreset(ObjectUtils.asMap("name", "api_test_upload_preset4", "folder", "folder")); - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); - api.deleteUploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET_4, "folder", "folder")); + api.uploadPreset(API_TEST_UPLOAD_PRESET_4, ObjectUtils.emptyMap()); + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_4, ObjectUtils.emptyMap()); boolean error = false; try { - api.uploadPreset("api_test_upload_preset4", ObjectUtils.emptyMap()); + api.uploadPreset(API_TEST_UPLOAD_PRESET_4, ObjectUtils.emptyMap()); } catch (Exception e) { error = true; } @@ -575,6 +589,8 @@ public void testUpdateUploadPreset() throws Exception { @Test public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value + List resources; + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); @@ -583,15 +599,21 @@ public void testListByModerationUpdate() throws Exception { Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); Map rejected = api.resourcesByModeration("manual", "rejected", ObjectUtils.asMap("max_results", 1000)); Map pending = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("max_results", 1000)); - assertNotNull(findByAttr((List) approved.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) approved.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNotNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result2.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) rejected.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNotNull(findByAttr((List) pending.get("resources"), "public_id", (String) result3.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result1.get("public_id"))); - assertNull(findByAttr((List) pending.get("resources"), "public_id", (String) result2.get("public_id"))); + + resources = (List>) approved.get("resources"); + assertThat(resources, hasItem(hasEntry("public_id", result1.get("public_id")))); + assertThat(resources, not(hasItem(hasEntry("public_id", result2.get("public_id"))))); + assertThat(resources, not(hasItem(hasEntry("public_id", result3.get("public_id"))))); + + resources = (List>) rejected.get("resources"); + assertThat(resources, not(hasItem(hasEntry("public_id", result1.get("public_id"))))); + assertThat(resources, hasItem(hasEntry("public_id", result2.get("public_id")))); + assertThat(resources, not(hasItem(hasEntry("public_id", result3.get("public_id"))))); + + resources = (List>) pending.get("resources"); + assertThat(resources, not(hasItem(hasEntry("public_id", result1.get("public_id"))))); + assertThat(resources, not(hasItem(hasEntry("public_id", result2.get("public_id"))))); + assertThat(resources, hasItem(hasEntry("public_id", result3.get("public_id")))); } // For this test to work, "Auto-create folders" should be enabled in the @@ -625,11 +647,11 @@ public void testRestore() throws Exception { ObjectUtils.asMap("public_id", "api_test_restore", "backup", true, "tags", SDK_TEST_TAG)); Map resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); assertEquals(resource.get("bytes"), 3381); - api.deleteResources(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + api.deleteResources(Collections.singletonList("api_test_restore"), ObjectUtils.emptyMap()); resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); assertEquals(resource.get("bytes"), 0); assertTrue((Boolean) resource.get("placeholder")); - Map response = api.restore(Arrays.asList("api_test_restore"), ObjectUtils.emptyMap()); + Map response = api.restore(Collections.singletonList("api_test_restore"), ObjectUtils.emptyMap()); Map info = (Map) response.get("api_test_restore"); assertNotNull(info); assertEquals(info.get("bytes"), 3381); @@ -641,7 +663,7 @@ public void testRestore() throws Exception { public void testUploadMapping() throws Exception { try { api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); - } catch (Exception e) { + } catch (Exception ignored) { } api.createUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://cloudinary.com")); @@ -675,9 +697,4 @@ public void testUploadMapping() throws Exception { } assertTrue(!found); } - - - private void assertContains(Object object, Collection list) { - assertTrue(list.contains(object)); - } } diff --git a/pom.xml b/pom.xml index ce2bad88..e6c70ddd 100644 --- a/pom.xml +++ b/pom.xml @@ -110,6 +110,12 @@ + + org.hamcrest + java-hamcrest + 2.0.0.0 + test + junit junit From d60906ae78ba9647e08ca28a647314c0cc087881 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 Aug 2016 11:36:50 +0300 Subject: [PATCH 151/592] Update Junit version and add JUnitParams. --- cloudinary-test-common/pom.xml | 2 +- pom.xml | 8 +++++++- samples/photo_album/pom.xml | 2 +- samples/photo_album_gae/pom.xml | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 72482fb0..f7496f82 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -25,7 +25,7 @@ junit junit - 4.10 + 4.12 diff --git a/pom.xml b/pom.xml index e6c70ddd..f96ae1d1 100644 --- a/pom.xml +++ b/pom.xml @@ -116,10 +116,16 @@ 2.0.0.0 test + + pl.pragmatists + JUnitParams + 1.0.5 + test + junit junit - 4.10 + 4.12 test diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 9441acd9..4f7a0d60 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -71,7 +71,7 @@ junit junit - 4.8.2 + 4.12 test diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index 2d0c00a3..f6d84dad 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -86,7 +86,7 @@ junit junit - 4.10 + 4.12 test From 07da62b6f8c004cc2ccc27d0ae4586ef576879fe Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 Aug 2016 11:38:23 +0300 Subject: [PATCH 152/592] Refactor Quality and Width tests. --- .../com/cloudinary/test/CloudinaryTest.java | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) 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 206456c5..925bb4af 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1,27 +1,32 @@ package com.cloudinary.test; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import com.cloudinary.Cloudinary; import com.cloudinary.ResponsiveBreakpoint; -import org.cloudinary.json.JSONArray; +import com.cloudinary.Transformation; +import com.cloudinary.transformation.*; +import com.cloudinary.utils.ObjectUtils; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import junitparams.naming.TestCaseName; import org.cloudinary.json.JSONObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import org.junit.runner.RunWith; -import com.cloudinary.Cloudinary; -import com.cloudinary.Transformation; -import com.cloudinary.transformation.*; -import com.cloudinary.utils.ObjectUtils; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.junit.Assert.*; +@RunWith(JUnitParamsRunner.class) public class CloudinaryTest { private static final String DEFAULT_ROOT_PATH = "http://res.cloudinary.com/test123/"; private static final String DEFAULT_UPLOAD_PATH = DEFAULT_ROOT_PATH + "image/upload/"; @@ -257,8 +262,8 @@ public void testCrop() { Transformation transformation = new Transformation().width(100).height(101); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "h_101,w_100/test", result); - assertEquals("101", transformation.getHtmlHeight().toString()); - assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("101", transformation.getHtmlHeight()); + assertEquals("100", transformation.getHtmlWidth()); transformation = new Transformation().width(100).height(101).crop("crop"); result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_101,w_100/test", result); @@ -273,18 +278,21 @@ public void testVariousOptions() { } @Test - public void testQuality() { - // should use x, y, radius, prefix, gravity and quality from options - Transformation transformation = new Transformation().quality(0.4); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "q_0.4/test", result); - transformation = new Transformation().quality("auto"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "q_auto/test", result); - transformation = new Transformation().quality("auto:good"); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "q_auto:good/test", result); + @TestCaseName("{method}: {params}") + @Parameters + public void testQuality( Object quality, String result) { + Transformation transformation = new Transformation().quality(quality); + assertEquals(result, transformation.generate()); } + private Object parametersForTestQuality() { + Object [][] q = { + {0.4, "q_0.4"}, + {"0.4", "q_0.4"}, + {"auto", "q_auto"}, + {"auto:good", "q_auto:good"}}; + return q; + + }; @Test public void testTransformationSimple() { @@ -610,23 +618,21 @@ public void testResponsiveWidth() { assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_pad,w_auto/test", result); Transformation.setResponsiveWidthTransformation(null); } + + @Parameters({ + "auto:20|c_fill\\,w_auto:20", + "auto:20:350|c_fill\\,w_auto:20:350", + "auto:breakpoints|c_fill\\,w_auto:breakpoints", + "auto:breakpoints_100_1900_20_15|c_fill\\,w_auto:breakpoints_100_1900_20_15", + "auto:breakpoints:json|c_fill\\,w_auto:breakpoints:json"}) + @TestCaseName("Width {0}: {1}") @Test - public void testShouldSupportAutoWidth(){ + public void testShouldSupportAutoWidth(String width, String result){ String trans; - - trans = new Transformation().width("auto:20").crop("fill").generate(); - assertEquals("c_fill,w_auto:20", trans); - trans = new Transformation().width("auto:20:350").crop("fill").generate(); - assertEquals("c_fill,w_auto:20:350", trans); - trans = new Transformation().width("auto:breakpoints").crop("fill").generate(); - assertEquals("c_fill,w_auto:breakpoints", trans); - trans = new Transformation().width("auto:breakpoints_100_1900_20_15").crop("fill").generate(); - assertEquals("c_fill,w_auto:breakpoints_100_1900_20_15", trans); - trans = new Transformation().width("auto:breakpoints:json").crop("fill").generate(); - assertEquals("c_fill,w_auto:breakpoints:json", trans); + trans = new Transformation().width(width).crop("fill").generate(); + assertEquals(result, trans); } - @Test public void testShouldSupportOhOw(){ String trans = new Transformation().width("ow").height("oh").crop("crop").generate(); From bab5ad3c9c6e2cba91c96ecf8d77cf71e41f491b Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 Aug 2016 11:56:38 +0300 Subject: [PATCH 153/592] Add deprecation message to Layer classes. --- .../main/java/com/cloudinary/transformation/LayerBuilder.java | 2 +- .../com/cloudinary/transformation/SubtitlesLayerBuilder.java | 2 +- .../java/com/cloudinary/transformation/TextLayerBuilder.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java index 81aa962c..5a4ea9df 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java @@ -1,7 +1,7 @@ package com.cloudinary.transformation; /** - * @deprecated + * @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 index 099bb3b3..22a78625 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java @@ -1,7 +1,7 @@ package com.cloudinary.transformation; /** - * @deprecated + * @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 index 777f12b5..0db485ce 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java @@ -1,7 +1,7 @@ package com.cloudinary.transformation; /** - * @deprecated + * @deprecated Use {@link TextLayer} instead */ public class TextLayerBuilder extends TextLayer { } From 4fa896e70052bd34742cf22bdd0bdbd3fba114a5 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 Aug 2016 12:00:26 +0300 Subject: [PATCH 154/592] Add static import of `asMap` and `emptyMap`. Suppress deprecation warnings for backward compatibility tests. --- .../com/cloudinary/test/CloudinaryTest.java | 110 +++++++----------- 1 file changed, 41 insertions(+), 69 deletions(-) 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 925bb4af..89c068b2 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -24,6 +24,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.cloudinary.utils.ObjectUtils.asMap; +import static com.cloudinary.utils.ObjectUtils.emptyMap; import static org.junit.Assert.*; @RunWith(JUnitParamsRunner.class) @@ -284,15 +286,15 @@ public void testQuality( Object quality, String result) { Transformation transformation = new Transformation().quality(quality); assertEquals(result, transformation.generate()); } + @SuppressWarnings("unused") private Object parametersForTestQuality() { - Object [][] q = { + return new Object[][]{ {0.4, "q_0.4"}, {"0.4", "q_0.4"}, {"auto", "q_auto"}, {"auto:good", "q_auto:good"}}; - return q; - }; + } @Test public void testTransformationSimple() { @@ -315,7 +317,7 @@ public void testBaseTransformations() { // should support base transformation Transformation transformation = new Transformation().x(100).y(100).crop("fill").chain().crop("crop").width(100); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("100", transformation.getHtmlWidth()); assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,x_100,y_100/c_crop,w_100/test", result); } @@ -324,7 +326,7 @@ public void testBaseTransformationArray() { // should support array of base transformations Transformation transformation = new Transformation().x(100).y(100).width(200).crop("fill").chain().radius(10).chain().crop("crop").width(100); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals("100", transformation.getHtmlWidth().toString()); + assertEquals("100", transformation.getHtmlWidth()); assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,w_200,x_100,y_100/r_10/c_crop,w_100/test", result); } @@ -373,37 +375,6 @@ public void testAngle() { assertEquals(DEFAULT_UPLOAD_PATH + "a_exif.12/test", result); } - @Test - public void testOverlay() { - // should support overlay - Transformation transformation = new Transformation().overlay("text:hello"); - String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:hello/test", result); - // should not pass width/height to html if overlay - transformation = new Transformation().overlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,l_text:hello,w_100/test", result); - - transformation = new Transformation().overlay(new TextLayer().text("goodbye")); - result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "l_text:goodbye/test", result); - } - - @Test - 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); - // should not pass width/height to html if underlay - transformation = new Transformation().underlay("text:hello").width(100).height(100); - result = cloudinary.url().transformation(transformation).generate("test"); - assertNull(transformation.getHtmlHeight()); - assertNull(transformation.getHtmlWidth()); - assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); - } - @Test public void testFetchFormat() { // should support format for fetch urls @@ -480,23 +451,23 @@ public void testOpacity() { @Test public void testImageTag() { Transformation transformation = new Transformation().width(100).height(101).crop("crop"); - String result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + String result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image")); assertEquals("my image", result); transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image")); + result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image")); assertEquals( "my image", result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "class", "extra")); + result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "class", "extra")); assertEquals( "my image", result); transformation = new Transformation().width("auto").crop("crop"); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "blank")); + result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "responsive_placeholder", "blank")); assertEquals( "my image", result); - result = cloudinary.url().transformation(transformation).imageTag("test", ObjectUtils.asMap("alt", "my image", "responsive_placeholder", "other.gif")); + result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "responsive_placeholder", "other.gif")); assertEquals( "my image", result); @@ -513,14 +484,14 @@ public void testClientHints() { .dpr("auto"); testTag = cloudinary.url().transformation(trans).imageTag("sample.jpg"); assertTrue(testTag.startsWith(" parameters = getUrlParameters(uri); assertEquals("imgÿ=&é", parameters.get("public_id")); @@ -560,7 +531,7 @@ public void testPrivateDownload() throws Exception { @SuppressWarnings("unchecked") @Test public void testZipDownload() throws Exception { - String url = cloudinary.zipDownload("ttag", ObjectUtils.emptyMap()); + String url = cloudinary.zipDownload("ttag", emptyMap()); URI uri = new URI(url); Map parameters = getUrlParameters(uri); assertEquals("ttag", parameters.get("tag")); @@ -580,7 +551,7 @@ public void testSpriteCss() { @Test public void testEscapePublicId() { // should escape public_ids - Map tests = ObjectUtils.asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); + Map tests = asMap("a b", "a%20b", "a+b", "a%2Bb", "a%20b", "a%20b", "a-b", "a-b", "a??b", "a%3F%3Fb"); for (Map.Entry entry : tests.entrySet()) { String result = cloudinary.url().generate(entry.getKey()); assertEquals(DEFAULT_UPLOAD_PATH + "" + entry.getValue(), result); @@ -611,7 +582,7 @@ public void testResponsiveWidth() { String result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,h_100,w_100/c_limit,w_auto/test", result); - Transformation.setResponsiveWidthTransformation(ObjectUtils.asMap("width", "auto", "crop", "pad")); + Transformation.setResponsiveWidthTransformation(asMap("width", "auto", "crop", "pad")); trans = new Transformation().width(100).height(100).crop("crop").responsiveWidth(true); result = cloudinary.url().transformation(trans).generate("test"); assertTrue(trans.isResponsive()); @@ -648,7 +619,7 @@ public void testVideoCodec() { // should support a hash value actual = cloudinary.url().resourceType("video") .transformation( - new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264", "profile", "basic", "level", "3.1")) + new Transformation().videoCodec(asMap("codec", "h264", "profile", "basic", "level", "3.1")) ).generate("video_id"); assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); } @@ -784,13 +755,13 @@ public void testVideoTag() { + "" + ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); - assertEquals(expectedTag, cloudinary.url().videoTag("movie", ObjectUtils.emptyMap())); + assertEquals(expectedTag, cloudinary.url().videoTag("movie", emptyMap())); assertEquals(expectedTag, cloudinary.url().publicId("movie").videoTag()); } @Test public void testVideoTagWithAttributes() { - Map attributes = ObjectUtils.asMap( + Map attributes = asMap( "autoplay", true, "controls", null, "loop", null, @@ -808,13 +779,13 @@ public void testVideoTagWithAttributes() { @Test public void testVideoTagWithTransformation() { - Transformation transformation = new Transformation().videoCodec(ObjectUtils.asMap("codec", "h264")) + Transformation transformation = new Transformation().videoCodec(asMap("codec", "h264")) .audioCodec("acc").startOffset(3); String expectedUrl = VIDEO_UPLOAD_PATH + "ac_acc,so_3.0,vc_h264/movie"; String expectedTag = ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl); String actualTag = cloudinary.url().transformation(transformation).sourceTypes(new String[]{"mp4"}) - .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + .videoTag("movie", asMap("html_height", "100", "html_width", "200")); assertEquals(expectedTag, actualTag); expectedTag = ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap("html_height", "100", "html_width", "200")); + .videoTag("movie", asMap("html_height", "100", "html_width", "200")); assertEquals(expectedTag, actualTag); transformation.width(250); @@ -836,7 +807,7 @@ public void testVideoTagWithTransformation() { + ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap()); + .videoTag("movie", asMap()); assertEquals(expectedTag, actualTag); transformation.crop("fit"); @@ -848,7 +819,7 @@ public void testVideoTagWithTransformation() { + ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); actualTag = cloudinary.url().transformation(transformation) - .videoTag("movie", ObjectUtils.asMap()); + .videoTag("movie", asMap()); assertEquals(expectedTag, actualTag); } @@ -859,13 +830,13 @@ public void testVideoTagWithFallback() { String expectedTag = ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, fallback); String actualTag = cloudinary.url().fallbackContent(fallback).sourceTypes(new String[]{"mp4"}) - .videoTag("movie", ObjectUtils.emptyMap()); + .videoTag("movie", emptyMap()); assertEquals(expectedTag, actualTag); expectedTag = ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl, fallback); - actualTag = cloudinary.url().fallbackContent(fallback).videoTag("movie", ObjectUtils.emptyMap()); + actualTag = cloudinary.url().fallbackContent(fallback).videoTag("movie", emptyMap()); assertEquals(expectedTag, actualTag); } @@ -876,7 +847,7 @@ public void testVideoTagWithSourceTypes() { + "" + ""; expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl); String actualTag = cloudinary.url().sourceTypes(new String[]{"ogv", "mp4"}) - .videoTag("movie.mp4", ObjectUtils.emptyMap()); + .videoTag("movie.mp4", emptyMap()); assertEquals(expectedTag, actualTag); } @@ -894,7 +865,7 @@ public void testVideoTagWithSourceTransformation() { String actualTag = cloudinary.url().transformation(new Transformation().quality(50).chain().width(100)) .sourceTransformationFor("mp4", new Transformation().quality(30)) .sourceTransformationFor("ogv", new Transformation().quality(70)) - .videoTag("movie", ObjectUtils.emptyMap()); + .videoTag("movie", emptyMap()); assertEquals(expectedTag, actualTag); expectedTag = " com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 29613666..ed9e35b2 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index f86131a7..eaa424f8 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index b185fd6e..d4e58d76 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 37a787e6..e3534aaf 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 0e549274..9f198af5 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 7589e10d..79d60695 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index f7496f82..c0c3d28a 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 cloudinary-test-common diff --git a/pom.xml b/pom.xml index f96ae1d1..3a4be34e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.3-SNAPSHOT + 1.4.3 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.3 From 856f623efe61fcda5a9e001c951a89a2feede284 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Wed, 14 Sep 2016 20:29:55 +0300 Subject: [PATCH 171/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 9e0e0b52..402fce6e 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.3 + 1.4.4-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.3 + 1.4.4-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index ed9e35b2..a4795fca 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index eaa424f8..9db52934 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index d4e58d76..a54af438 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index e3534aaf..0d1eea72 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 9f198af5..29afcaf2 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 79d60695..2a9a8fa0 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index c0c3d28a..ad1d5fac 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 3a4be34e..a87e9033 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.3 + 1.4.4-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.3 + HEAD From 658b107df66fff00efbfb218d4b72b69ed773c08 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Sep 2016 15:25:30 +0300 Subject: [PATCH 172/592] cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java Fix issue with uploading urls with \n. Fix handling of 404 from upload api --- CHANGELOG.md | 3 +++ README.md | 4 ++-- .../main/java/com/cloudinary/android/UploaderStrategy.java | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- .../main/java/com/cloudinary/http42/UploaderStrategy.java | 4 ++-- .../main/java/com/cloudinary/http43/UploaderStrategy.java | 4 ++-- .../main/java/com/cloudinary/http44/UploaderStrategy.java | 4 ++-- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 6 ++++++ 8 files changed, 20 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d45c14..83cfbe2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +cloudinary-parent-1.4.4 / 2016-09-15 +==================================== + * Fix issue when uploading URL with \n cloudinary-parent-1.4.3 / 2016-09-09 ==================================== diff --git a/README.md b/README.md index 484619b2..583d0ea1 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.3 + 1.4.4 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.3/cloudinary-core-1.4.3.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.3/cloudinary-http44-1.4.3.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.4/cloudinary-core-1.4.4.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.4/cloudinary-http44-1.4.4.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 6a50c0de..ac3662c6 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -62,7 +62,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } String filename = (String) options.get("filename"); @@ -91,7 +91,7 @@ public Map callApi(String action, Map params, Map options, Objec String responseData = readFully(responseStream); connection.disconnect(); - if (code != 200 && code != 400 && code != 500) { + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 7bf6e900..741ad37e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.3"; + public final static String VERSION = "1.4.4"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 95998b17..14bed3ca 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -84,7 +84,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } String filename = (String) options.get("filename"); @@ -107,7 +107,7 @@ public Map callApi(String action, Map params, Map options, Objec InputStream responseStream = response.getEntity().getContent(); String responseData = StringUtils.read(responseStream); - if (code != 200 && code != 400 && code != 500) { + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index f110148f..74039106 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -97,7 +97,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } String filename = (String) options.get("filename"); @@ -127,7 +127,7 @@ public Map callApi(String action, Map params, Map options, Objec response.close(); } - if (code != 200 && code != 400 && code != 500) { + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 0cbadc5d..d3516add 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -99,7 +99,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { file = new File((String) file); } String filename = (String) options.get("filename"); @@ -129,7 +129,7 @@ public Map callApi(String action, Map params, Map options, Objec response.close(); } - if (code != 200 && code != 400 && code != 500) { + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } 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 4669f3f4..2b9bc54b 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 @@ -499,5 +499,11 @@ public void testDownloadArchive() throws Exception { } assertEquals(2, files); } + + @Test + public void testUploadInvalidUrl() throws IOException { + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", ObjectUtils.asMap("return_error", true)); + assertEquals(result.get("http_code"), 404); + } } From f1f39e21c85acc3ca9a30b69ac8766c56e074dc7 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Sep 2016 17:04:18 +0300 Subject: [PATCH 173/592] [maven-release-plugin] prepare release 1.4.4 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 402fce6e..48ecb0d4 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 com.cloudinary cloudinary-android-test - 1.4.4-SNAPSHOT + 1.4.4 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.4-SNAPSHOT + 1.4.4 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index a4795fca..8cd33afc 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 9db52934..bf32e819 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index a54af438..8210840d 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 0d1eea72..fca6fd06 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 29afcaf2..0706ca8f 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 2a9a8fa0..7d98bbfb 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index ad1d5fac..66584c5d 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 cloudinary-test-common diff --git a/pom.xml b/pom.xml index a87e9033..9f4cf347 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.4-SNAPSHOT + 1.4.4 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.4 From f459dea9ff51c20bc65a48f8a8eff994ffedb718 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Sep 2016 17:07:23 +0300 Subject: [PATCH 174/592] Fix testUploadInvalidUrl test --- .../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 2b9bc54b..08d48929 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 @@ -503,7 +503,8 @@ public void testDownloadArchive() throws Exception { @Test public void testUploadInvalidUrl() throws IOException { Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", ObjectUtils.asMap("return_error", true)); - assertEquals(result.get("http_code"), 404); + Map error = (Map) result.get("error"); + assertEquals(error.get("http_code"), 404); } } From 6cd571a49ef981a1f9e9dc9a1ca17efef341bc60 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Thu, 15 Sep 2016 17:04:25 +0300 Subject: [PATCH 175/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 48ecb0d4..40aeb249 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.4 + 1.4.5-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.4 + 1.4.5-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 8cd33afc..4ca0dc52 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index bf32e819..e3dff02f 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 8210840d..f4bdeed3 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index fca6fd06..9b928881 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 0706ca8f..2185e8aa 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 7d98bbfb..6901f869 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 66584c5d..bdafbf5f 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 9f4cf347..086dcd8a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.4 + 1.4.5-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.4 + HEAD From 6311f6641e3c08c32049b14e21446e1505617d23 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 16 Sep 2016 16:06:40 +0300 Subject: [PATCH 176/592] Better check for bad local files --- .../java/com/cloudinary/http42/UploaderStrategy.java | 10 +++++++--- .../java/com/cloudinary/http43/UploaderStrategy.java | 8 ++++++-- .../java/com/cloudinary/http44/UploaderStrategy.java | 8 ++++++-- .../com/cloudinary/test/AbstractUploaderTest.java | 12 +++++++----- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 14bed3ca..ac41d990 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -84,8 +84,12 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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) { @@ -98,7 +102,7 @@ public Map callApi(String action, Map params, Map options, Objec } else if (file == null) { // no-problem } else { - throw new IOException("Uprecognized file parameter " + file); + throw new IOException("Unrecognized file parameter " + file); } postMethod.setEntity(multipart); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 74039106..5e7ff3e9 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -97,8 +97,12 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index d3516add..d4fc5b6c 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -99,8 +99,12 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); + if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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) { 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 08d48929..a50dfe72 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 @@ -500,11 +500,13 @@ public void testDownloadArchive() throws Exception { assertEquals(2, files); } - @Test - public void testUploadInvalidUrl() throws IOException { - Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", ObjectUtils.asMap("return_error", true)); - Map error = (Map) result.get("error"); - assertEquals(error.get("http_code"), 404); + public void testUploadInvalidUrl() { + try { + cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", ObjectUtils.asMap("return_error", true)); + fail("Expected exception was not thrown"); + } catch(IOException e) { + assertEquals(e.getMessage(), "File not found or unreadable: " + REMOTE_TEST_IMAGE + "\n"); + } } } From 9e8fb0a36e981bd0c42079347cd1d9e3770a6606 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 16 Sep 2016 16:08:51 +0300 Subject: [PATCH 177/592] Increment to version 1.4.5 Better handling of missing/unreadable local files --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 12 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83cfbe2d..77299294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +cloudinary-parent-1.4.5 / 2016-09-16 +==================================== + * Better handling of missing/unreadable local files + cloudinary-parent-1.4.4 / 2016-09-15 ==================================== * Fix issue when uploading URL with \n diff --git a/README.md b/README.md index 583d0ea1..be792996 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.4 + 1.4.5 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.4/cloudinary-core-1.4.4.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.4/cloudinary-http44-1.4.4.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.5/cloudinary-core-1.4.5.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.5/cloudinary-http44-1.4.5.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 40aeb249..d7693702 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 com.cloudinary cloudinary-android-test - 1.4.5-SNAPSHOT + 1.4.5 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.5-SNAPSHOT + 1.4.5 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 4ca0dc52..ec9fb589 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index e3dff02f..8a331108 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-core diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 741ad37e..54260362 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.4"; + public final static String VERSION = "1.4.5"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index f4bdeed3..17953f36 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 9b928881..6c7be496 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 2185e8aa..d453150c 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 6901f869..bc0cdbe0 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index bdafbf5f..b73c10ba 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 086dcd8a..a3ad54f8 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.5-SNAPSHOT + 1.4.5 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.5 From 56bb360b98e64be824806a53dfd879dcafa01fd3 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 16 Sep 2016 17:13:19 +0300 Subject: [PATCH 178/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index d7693702..f9dfc4f2 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.5 + 1.4.6-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.5 + 1.4.6-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index ec9fb589..0231b058 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 8a331108..5a7478b3 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 17953f36..0406a1b2 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 6c7be496..00001a75 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index d453150c..bc73dccf 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index bc0cdbe0..f68ca513 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index b73c10ba..e5e55496 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index a3ad54f8..a44b29ca 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.5 + 1.4.6-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.5 + HEAD From d36415b62702dd044fea7c80791919436e572c64 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 27 Oct 2016 08:22:34 +0300 Subject: [PATCH 179/592] Add streaming profiles API --- .../src/main/java/com/cloudinary/Api.java | 155 +++++++++++++++++- .../test/StreamingProfilesApiTest.java | 7 + .../test/StreamingProfilesApiTest.java | 7 + .../test/StreamingProfilesApiTest.java | 7 + .../AbstractStreamingProfilesApiTest.java | 128 +++++++++++++++ 5 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index f1f82fa6..25fa8d33 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -1,10 +1,7 @@ package com.cloudinary; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ExecutionException; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; @@ -16,11 +13,13 @@ import com.cloudinary.api.exceptions.RateLimited; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONArray; @SuppressWarnings({"rawtypes", "unchecked"}) public class Api { - public enum HttpMethod {GET, POST, PUT, DELETE} + + public enum HttpMethod {GET, POST, PUT, DELETE;} public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); static { @@ -34,8 +33,8 @@ public enum HttpMethod {GET, POST, PUT, DELETE} } public final Cloudinary cloudinary; - private AbstractApiStrategy strategy; + private AbstractApiStrategy strategy; protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { return this.strategy.callApi(method, uri, params, options); } @@ -299,4 +298,146 @@ private ApiResponse publishResource(String byKey, Object value, Map options) thr params.putAll(ObjectUtils.only(options, "invalidate", "overwrite")); return callApi(HttpMethod.POST, uri, params, options); } + + /** + * Create a new streaming profile + * + * @param name the of the profile + * @param displayName the display name of the profile + * @param representations a collection of Maps with a transformation key + * @param options additional options + * @return the new streaming profile + * @throws Exception an exception + */ + public ApiResponse createStreamingProfile(String name, String displayName, List representations, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + List serializedRepresentations = new ArrayList(representations.size()); + for (Map t : representations) { + final Object transformation = t.get("transformation"); + serializedRepresentations.add(ObjectUtils.asMap("transformation", transformation.toString())); + } + List uri = Collections.singletonList("streaming_profiles"); + final Map params = ObjectUtils.asMap( + "name", name, + "representations", new JSONArray(serializedRepresentations.toArray()) + ); + if (displayName != null) { + params.put("display_name", displayName); + } + return callApi(HttpMethod.POST, uri, params, options); + } + + /** + * @see Api#createStreamingProfile(String, String, List, Map) + */ + public ApiResponse createStreamingProfile(String name, String displayName, List representations) throws Exception { + return createStreamingProfile(name, displayName, representations, null); + } + + /** + * Get a streaming profile information + * @param name the name of the profile to fetch + * @param options additional options + * @return a streaming profile + * @throws Exception an exception + */ + public ApiResponse getStreamingProfile(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + List uri = Arrays.asList("streaming_profiles", name); + + return callApi(HttpMethod.GET, uri, ObjectUtils.emptyMap(), options); + + } + + /** + * @see Api#getStreamingProfile(String, Map) + */ + public ApiResponse getStreamingProfile(String name) throws Exception { + return getStreamingProfile(name, null); + } + + /** + * List Streaming profiles + * @param options additional options + * @return a list of all streaming profiles defined for the current cloud + * @throws Exception an exception + */ + public ApiResponse listStreamingProfiles(Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + List uri = Collections.singletonList("streaming_profiles"); + return callApi(HttpMethod.GET, uri, ObjectUtils.emptyMap(), options); + + } + + /** + * @see Api#listStreamingProfiles(Map) + */ + public ApiResponse listStreamingProfiles() throws Exception { + return listStreamingProfiles(null); + } + + /** + * Delete a streaming profile information. Predefined profiles are restored to the default setting. + * @param name the name of the profile to delete + * @param options additional options + * @return a streaming profile + * @throws Exception an exception + */ + public ApiResponse deleteStreamingProfile(String name, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + List uri = Arrays.asList("streaming_profiles", name); + + return callApi(HttpMethod.DELETE, uri, ObjectUtils.emptyMap(), options); + + } + + /** + * @see Api#deleteStreamingProfile(String, Map) + */ + public ApiResponse deleteStreamingProfile(String name) throws Exception { + return getStreamingProfile(name, null); + } + + /** + * Create a new streaming profile + * + * @param name the of the profile + * @param displayName the display name of the profile + * @param representations a collection of Maps with a transformation key + * @param options additional options + * @return the new streaming profile + * @throws Exception an exception + */ + public ApiResponse updateStreamingProfile(String name, String displayName, List representations, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + List serializedRepresentations; + final Map params = new HashMap(); + List uri = Arrays.asList("streaming_profiles", name); + + if (representations != null) { + serializedRepresentations = new ArrayList(representations.size()); + for (Map t : representations) { + final Object transformation = t.get("transformation"); + serializedRepresentations.add(ObjectUtils.asMap("transformation", transformation.toString())); + } + params.put("representations", new JSONArray(serializedRepresentations.toArray())); + } + if (displayName != null) { + params.put("display_name", displayName); + } + return callApi(HttpMethod.PUT, uri, params, options); + } + + /** + * @see Api#updateStreamingProfile(String, String, List, Map) + */ + public ApiResponse updateStreamingProfile(String name, String displayName, List representations) throws Exception { + return createStreamingProfile(name, displayName, representations); + } + } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java new file mode 100644 index 00000000..4e763579 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java @@ -0,0 +1,7 @@ +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/StreamingProfilesApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java new file mode 100644 index 00000000..4e763579 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java @@ -0,0 +1,7 @@ +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/StreamingProfilesApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java new file mode 100644 index 00000000..4e763579 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java @@ -0,0 +1,7 @@ +package com.cloudinary.test; + +/** + * Created by amir on 25/10/2016. + */ +public class StreamingProfilesApiTest extends AbstractStreamingProfilesApiTest { +} diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java new file mode 100644 index 00000000..590100a3 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java @@ -0,0 +1,128 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.AlreadyExists; +import com.cloudinary.utils.ObjectUtils; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.io.IOException; +import java.util.*; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +abstract public class AbstractStreamingProfilesApiTest extends MockableTest { + private static final String PROFILE_NAME = "api_test_streaming_profile" + SUFFIX; + protected Api api; + private static final List PREDEFINED_PROFILES = Arrays.asList("4k", "full_hd", "hd", "sd", "full_hd_wifi", "full_hd_lean", "hd_lean"); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + } + + @Test + public void testCreate() throws Exception { + final String name = PROFILE_NAME + "_create"; + ApiResponse result = api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") + )), ObjectUtils.emptyMap()); + + assertTrue(result.containsKey("data")); + Map profile = (Map) result.get("data"); + assertThat(profile, (Matcher) hasEntry("name", (Object) name)); + } + + @Test + public void testGet() throws Exception { + ApiResponse result = api.getStreamingProfile(PREDEFINED_PROFILES.get(0)); + assertTrue(result.containsKey("data")); + Map profile = (Map) result.get("data"); + assertThat(profile, (Matcher) hasEntry("name", (Object) (PREDEFINED_PROFILES.get(0)))); + + } + + @Test + public void testList() throws Exception { + ApiResponse result = api.listStreamingProfiles(); + assertTrue(result.containsKey("data")); + List profiles = (List) result.get("data"); + // check that the list contains all predefined profiles + for (String p : + PREDEFINED_PROFILES) { + assertThat(profiles, (Matcher) hasItem(hasEntry("name", p))); + } + } + + @Test + public void testDelete() throws Exception { + ApiResponse result; + final String name = PROFILE_NAME + "_delete"; + try { + api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") + )), ObjectUtils.emptyMap()); + } catch (AlreadyExists ignored) { + } + + result = api.deleteStreamingProfile(name); + assertTrue(result.containsKey("data")); + Map profile = (Map) result.get("data"); + assertThat(profile, (Matcher) hasEntry("name", (Object) (name))); + + } + + @Test + public void testUpdate() throws Exception { + final String name = PROFILE_NAME + "_update"; + try { + api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") + )), ObjectUtils.emptyMap()); + } catch (AlreadyExists ignored) { + } + Map result = api.updateStreamingProfile(name, null, Collections.singletonList( + ObjectUtils.asMap("transformation", + new Transformation().crop("limit").width(800).height(800).bitRate("5m") + )), ObjectUtils.emptyMap()); + + assertTrue(result.containsKey("data")); + assertThat(result, (Matcher) hasEntry("message", (Object) "updated")); + Map profile = (Map) result.get("data"); + assertThat(profile, (Matcher) hasEntry("name", (Object) (name))); + assertThat(profile, Matchers.hasEntry(equalTo("representations"), (Matcher) hasItem(hasKey("transformation")))); + final Map representation = (Map) ((List) profile.get("representations")).get(0); + Map transformation = (Map) ((List)representation.get("transformation")).get(0); + assertThat(transformation, allOf( + (Matcher) hasEntry("width", 800), + (Matcher) hasEntry("height", 800), + (Matcher) hasEntry("crop", "limit"), + (Matcher) hasEntry("bit_rate", "5m") + )); + } +} From 6db27cc0fee35e4b7f2c71ab36dfd647d21d6809 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 27 Oct 2016 10:42:10 +0300 Subject: [PATCH 180/592] Update `CHANGELOG.md` --- CHANGELOG.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77299294..38cdbcae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,23 @@ -cloudinary-parent-1.4.5 / 2016-09-16 + +1.4.6 / 2016-10-27 +================== + + * Add streaming profiles API + +1.4.6 / 2016-10-27 +==================================== + + * Add streaming profiles API + +1.4.5 / 2016-09-16 ==================================== * Better handling of missing/unreadable local files -cloudinary-parent-1.4.4 / 2016-09-15 +1.4.4 / 2016-09-15 ==================================== * Fix issue when uploading URL with \n -cloudinary-parent-1.4.3 / 2016-09-09 +1.4.3 / 2016-09-09 ==================================== New functionality @@ -29,7 +40,7 @@ Other changes * Add tests for auto width and original width and height ( `ow`, `oh`) values * Add `timeout`, `connect_timeout` and `connection_request_timeout` to HTTP43 and HTTP44 Api. -cloudinary-parent-1.4.2 / 2016-05-16 +1.4.2 / 2016-05-16 ==================================== * Sent params as entities for PUT, POST @@ -41,12 +52,12 @@ cloudinary-parent-1.4.2 / 2016-05-16 * Add SDK_TEST_TAG to all resources being created. * Remove API limits test -cloudinary-parent-1.4.1 / 2016-03-23 +1.4.1 / 2016-03-23 ==================================== * Rename conditional parameters `faces` and `pages` to `faceCount` and `pageCount` -cloudinary-parent-1.4.0 / 2016-03-19 +1.4.0 / 2016-03-19 ==================================== * Add Condition builder for faces * Modify explicit test - don't use twitter @@ -57,7 +68,7 @@ cloudinary-parent-1.4.0 / 2016-03-19 * Fix uploadLarge to use X-Unique-Upload-Id instead of updating params. Solves #18 * Fix support for non-ascii chars in upload URL -cloudinary-parent-1.3.0 / 2016-01-19 +1.3.0 / 2016-01-19 ==================================== * Add `responsive_breakpoints` paramater From 7dc88d900ad7bcbc5f5af78cfafa73ee44ca2adf Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 28 Oct 2016 09:59:27 +0300 Subject: [PATCH 181/592] Increment to version 1.4.6 - support streaming profiles API --- CHANGELOG.md | 6 ------ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38cdbcae..94409de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,3 @@ - -1.4.6 / 2016-10-27 -================== - - * Add streaming profiles API - 1.4.6 / 2016-10-27 ==================================== diff --git a/README.md b/README.md index be792996..7f873514 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.5 + 1.4.6 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.5/cloudinary-core-1.4.5.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.5/cloudinary-http44-1.4.5.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.6/cloudinary-core-1.4.6.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.6/cloudinary-http44-1.4.6.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 54260362..efdabd6b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.5"; + public final static String VERSION = "1.4.6"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 3c9f2b4844f0a9b418cb6ba17ece1743f16841dd Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 28 Oct 2016 10:21:56 +0300 Subject: [PATCH 182/592] [maven-release-plugin] prepare release 1.4.6 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f9dfc4f2..5b6ecf7b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 com.cloudinary cloudinary-android-test - 1.4.6-SNAPSHOT + 1.4.6 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.6-SNAPSHOT + 1.4.6 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 0231b058..f307d6c6 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 5a7478b3..b939a137 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 0406a1b2..e880ecf6 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 00001a75..23000526 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index bc73dccf..9abc5461 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index f68ca513..b59f522a 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index e5e55496..990f812d 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 cloudinary-test-common diff --git a/pom.xml b/pom.xml index a44b29ca..d725564e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.6-SNAPSHOT + 1.4.6 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.4.6 From 247b5fd65d5deaa2dd19e540cd4a2a02bf712b73 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Fri, 28 Oct 2016 10:22:04 +0300 Subject: [PATCH 183/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 5b6ecf7b..676787f4 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT com.cloudinary cloudinary-android-test - 1.4.6 + 1.4.7-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.6 + 1.4.7-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index f307d6c6..d3c9ec38 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index b939a137..85fc38f7 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index e880ecf6..c6578da0 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 23000526..15f648a5 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 9abc5461..ca05f86e 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index b59f522a..dc5160e4 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 990f812d..dc0dd732 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index d725564e..bc2c6a44 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.6 + 1.4.7-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.4.6 + HEAD From 5d2c01b2a8e0d1956998b34a8c0bf09c3ddac587 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 17 Nov 2016 09:52:37 +0200 Subject: [PATCH 184/592] Add `removeAllTags` API --- .../src/main/java/com/cloudinary/Uploader.java | 10 +++++++++- .../java/com/cloudinary/test/AbstractUploaderTest.java | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 5a10cef4..b44a732a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -277,6 +277,12 @@ public Map removeTag(String tag, String[] publicIds, Map options) throws IOExcep return callTagsApi(tag, "remove", publicIds, options); } + public Map removeAllTags(String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + return callTagsApi(null, "remove_all", publicIds, options); + } + public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); @@ -287,7 +293,9 @@ public Map callTagsApi(String tag, String command, String[] publicIds, Map optio if (options == null) options = ObjectUtils.emptyMap(); Map params = new HashMap(); - params.put("tag", tag); + if (tag != null) { + params.put("tag", tag); + } params.put("command", command); params.put("type", (String) options.get("type")); params.put("public_ids", Arrays.asList(publicIds)); 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 a50dfe72..f3934c52 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 @@ -4,6 +4,8 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; import org.junit.*; import org.junit.rules.TestName; @@ -13,6 +15,7 @@ import java.util.*; import java.util.zip.ZipInputStream; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; @@ -226,6 +229,12 @@ public void testTags() throws Exception { cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); + result = cloudinary.uploader().removeAllTags(new String[]{public_id, public_id2, "noSuchId"}, ObjectUtils.emptyMap()); + List publicIds = (List) result.get("public_ids"); + assertThat(publicIds, containsInAnyOrder(public_id, public_id2)); // = and not containing "noSuchId" + result = cloudinary.api().resource(public_id, ObjectUtils.emptyMap()); + assertThat((Map) result, not(hasKey("tags"))); + } @Test From 3505fcd8526e998cd83e9a1f6faea4875b90a72b Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 17 Nov 2016 10:57:32 +0200 Subject: [PATCH 185/592] Escape `\` and `=` in context --- .../main/java/com/cloudinary/Uploader.java | 2 +- .../src/main/java/com/cloudinary/Util.java | 24 ++++++++++++++----- .../test/java/com/cloudinary/UtilTest.java | 21 ++++++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 2 +- 4 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 cloudinary-core/src/test/java/com/cloudinary/UtilTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index b44a732a..6045dbc3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -188,7 +188,7 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); } if (options.get("context") != null) { - params.put("context", ObjectUtils.encodeMap(options.get("context"))); + params.put("context", Util.encodeContext(options.get("context"))); } if (options.get("responsive_breakpoints") != null) { params.put("responsive_breakpoints", JSONObject.wrap(options.get("responsive_breakpoints"))); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 129914a0..ea7e6fb3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -1,10 +1,6 @@ package com.cloudinary; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -100,7 +96,7 @@ public static final void processWriteParameters(Map options, Map if (options.get("custom_coordinates") != null) params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); if (options.get("context") != null) - params.put("context", ObjectUtils.encodeMap(options.get("context"))); + params.put("context", encodeContext(options.get("context"))); putObject("ocr", options, params); putObject("raw_convert", options, params); putObject("categorization", options, params); @@ -111,6 +107,22 @@ public static final void processWriteParameters(Map options, Map params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); } + protected static String encodeContext(Object context) { + if (context != null && context instanceof Map) { + Map mapArg = (Map) context; + HashSet out = new HashSet(); + for (Map.Entry entry : mapArg.entrySet()) { + final String value = entry.getValue().replaceAll("([=\\|])","\\\\$1"); + out.add(entry.getKey() + "=" + value); + } + return StringUtils.join(out.toArray(), "|"); + } else if (context == null) { + return null; + } else { + return context.toString(); + } + } + @SuppressWarnings("unchecked") protected static final String buildCustomHeaders(Object headers) { if (headers == null) { diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java new file mode 100644 index 00000000..0f6b08fb --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -0,0 +1,21 @@ +package com.cloudinary; + +import com.cloudinary.utils.ObjectUtils; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Created by amir on 17/11/2016. + */ +public class UtilTest { + @Test + public void encodeContext() throws Exception { + Map context = ObjectUtils.asMap("caption", "different = caption", "alt2", "alt|alternative"); + String result = Util.encodeContext(context); + assertEquals("caption=different \\= caption|alt2=alt\\|alternative", result); + } + +} \ No newline at end of file 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 f3934c52..70ffdc93 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 @@ -338,7 +338,7 @@ public void testContext() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context, "tags", SDK_TEST_TAG)); Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); - Map differentContext = ObjectUtils.asMap("caption", "different caption", "alt2", "alternative alternative"); + Map differentContext = ObjectUtils.asMap("caption", "different = caption", "alt2", "alt|alternative alternative"); cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); From 99e73d2bf19ab700727c699b4ad068c8579990f7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sat, 19 Nov 2016 18:24:14 +0200 Subject: [PATCH 186/592] Add context API --- .../main/java/com/cloudinary/Uploader.java | 71 ++++++++- .../java/com/cloudinary/test/ContextTest.java | 5 + .../java/com/cloudinary/test/ContextTest.java | 5 + .../cloudinary/test/AbstractContextTest.java | 102 +++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 137 ++++++++---------- 5 files changed, 242 insertions(+), 78 deletions(-) create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 6045dbc3..15a8d614 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -18,6 +18,17 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Uploader { + + private final class Command { + final static String add = "add"; + final static String remove = "remove"; + final static String replace = "replace"; + final static String removeAll = "remove_all"; + + private Command() { + } + } + public Map callApi(String action, Map params, Map options, Object file) throws IOException { return strategy.callApi(action, params, options, file); } @@ -267,26 +278,26 @@ public Map addTag(String tag, String[] publicIds, Map options) throws IOExceptio if (options == null) options = ObjectUtils.emptyMap(); boolean exclusive = ObjectUtils.asBoolean(options.get("exclusive"), false); - String command = exclusive ? "set_exclusive" : "add"; + String command = exclusive ? "set_exclusive" : Command.add; return callTagsApi(tag, command, publicIds, options); } public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); - return callTagsApi(tag, "remove", publicIds, options); + return callTagsApi(tag, Command.remove, publicIds, options); } public Map removeAllTags(String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); - return callTagsApi(null, "remove_all", publicIds, options); + return callTagsApi(null, Command.removeAll, publicIds, options); } public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); - return callTagsApi(tag, "replace", publicIds, options); + return callTagsApi(tag, Command.replace, publicIds, options); } public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { @@ -302,6 +313,58 @@ public Map callTagsApi(String tag, String command, String[] publicIds, Map optio return callApi("tags", params, options, null); } + /** + * Add a context keys and values. If a particular key already exists, the value associated with the key is updated. + * @param context a map of key and value. Serialized to "key1=value1|key2=value2" + * @param publicIds the public IDs of the resources to update + * @param options additional options passed to the request + * @return a list of public IDs that were updated + * @throws IOException + */ + public Map addContext(Map context, String[] publicIds, Map options) throws IOException { + return callContextApi(context, Command.add, publicIds, options); + } + + /** + * Add a context keys and values. If a particular key already exists, the value associated with the key is updated. + * @param context Serialized context in the form of "key1=value1|key2=value2" + * @param publicIds the public IDs of the resources to update + * @param options additional options passed to the request + * @return a list of public IDs that were updated + * @throws IOException + */ + public Map addContext(String context, String[] publicIds, Map options) throws IOException { + return callContextApi(context, Command.add, publicIds, options); + } + + /** + * Remove all custom context from the specified public IDs. + * @param publicIds the public IDs of the resources to update + * @param options additional options passed to the request + * @return a list of public IDs that were updated + * @throws IOException + */ + public Map removeAllContext(String[] publicIds, Map options) throws IOException { + return callContextApi((String)null, Command.removeAll, publicIds, options); + } + + protected Map callContextApi(Map context, String command, String[] publicIds, Map options) throws IOException { + return callContextApi(Util.encodeContext(context), command, publicIds, options); + } + + protected Map callContextApi(String context, String command, String[] publicIds, Map options) throws IOException { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + if (context != null) { + params.put("context", context); + } + params.put("command", command); + params.put("type", (String) options.get("type")); + params.put("public_ids", Arrays.asList(publicIds)); + return callApi("context", params, options, null); + } + private final static String[] TEXT_PARAMS = {"public_id", "font_family", "font_size", "font_color", "text_align", "font_weight", "font_style", "background", "opacity", "text_decoration"}; diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java new file mode 100644 index 00000000..4841e9f6 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java @@ -0,0 +1,5 @@ +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/ContextTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java new file mode 100644 index 00000000..4841e9f6 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java @@ -0,0 +1,5 @@ +package com.cloudinary.test; + +public class ContextTest extends AbstractContextTest { + +} \ No newline at end of file diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java new file mode 100644 index 00000000..10af2c39 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java @@ -0,0 +1,102 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import com.cloudinary.Uploader; +import com.cloudinary.utils.ObjectUtils; +import org.junit.*; +import org.junit.rules.TestName; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.cloudinary.utils.ObjectUtils.asMap; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeNotNull; + +@SuppressWarnings({"rawtypes", "unchecked"}) +abstract public class AbstractContextTest extends MockableTest { + + private static final String CONTEXT_TAG = "context_tag_" + String.valueOf(System.currentTimeMillis()); + private static Map resource; + public static final Map CONTEXT = asMap("caption", "some cäption", "alt", "alternativè"); + private Uploader uploader; + + @BeforeClass + public static void setUpClass() throws Exception { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + + resource = cloudinary.uploader().upload(SRC_TEST_IMAGE, + asMap( "tags", new String[]{SDK_TEST_TAG, CONTEXT_TAG}, + "context", CONTEXT, + "transformation", new Transformation().crop("scale").width(10))); + final String publicId = (String) resource.get("public_id"); + resource = cloudinary.api().resource(publicId, asMap("context", true)); + assertEquals(asMap("custom", CONTEXT), resource.get("context")); + + } + + @AfterClass + public static void tearDownClass() { + Api api = MockableTest.cleanUp(); + Cloudinary cloudinary = new Cloudinary(); + try { + cloudinary.api().deleteResourcesByTag(CONTEXT_TAG, ObjectUtils.emptyMap()); + } catch (Exception ignored) { + } + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() throws Exception { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + cloudinary = new Cloudinary(); + uploader = cloudinary.uploader(); + assumeNotNull(cloudinary.config.apiSecret); + + } + + @Test + public void testExplicit() throws Exception { + //should allow sending context + + Map differentContext = asMap("caption", "different = caption", "alt2", "alt|alternative alternative"); + Map result = uploader.explicit(publicId(), asMap("type", "upload", "context", differentContext)); + assertEquals("explicit API should return the new context", asMap("custom", differentContext), result.get("context")); + resource = cloudinary.api().resource(publicId(), asMap("context", true)); + assertEquals("explicit API should replace the context", asMap("custom", differentContext), resource.get("context")); + } + + @Test + public void testAddContext() throws Exception { + Map context = new HashMap((Map)((Map)resource.get("context")).get("custom")); + context.put("caption", "new caption"); + Map result = uploader.addContext(asMap("caption", "new caption"), new String[]{publicId(), "no-such-id"}, null); + assertThat("addContext should return a list of modified public IDs", (List) result.get("public_ids"), contains(publicId())); + + resource = cloudinary.api().resource(publicId(), asMap("context", true)); + assertEquals(asMap("custom", context), resource.get("context")); + } + + @Test + public void testRemoveAllContext() throws Exception { + Map result = uploader.removeAllContext(new String[]{publicId(), "no-such-id"}, null); + assertThat((List) result.get("public_ids"), contains(publicId())); + + resource = cloudinary.api().resource(publicId(), asMap("context", true)); + assertThat((Map)resource, not(hasKey("context"))); + } + + private String publicId(){ + return (String) resource.get("public_id"); + } +} 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 70ffdc93..11af0023 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 @@ -15,6 +15,8 @@ import java.util.*; import java.util.zip.ZipInputStream; +import static com.cloudinary.utils.ObjectUtils.asArray; +import static com.cloudinary.utils.ObjectUtils.asMap; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; @@ -33,9 +35,9 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); } - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG})); cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, + asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, "transformation", new Transformation().crop("scale").width(10))); } @@ -61,7 +63,7 @@ public void setUp() { @Test public void testUtf8Upload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true, "tags", SDK_TEST_TAG, "public_id", "aåßéƒ")); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", SDK_TEST_TAG, "public_id", "aåßéƒ")); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); @@ -75,7 +77,7 @@ public void testUtf8Upload() throws IOException { @Test public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("colors", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", SDK_TEST_TAG)); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); @@ -89,7 +91,7 @@ public void testUpload() throws IOException { @Test public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); Map to_sign = new HashMap(); @@ -101,7 +103,7 @@ public void testUploadUrl() throws IOException { @Test public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", asMap("tags", SDK_TEST_TAG)); assertEquals(result.get("width"), 16); assertEquals(result.get("height"), 16); Map to_sign = new HashMap(); @@ -113,43 +115,43 @@ public void testUploadDataUri() throws IOException { @Test public void testUploadUTF8() throws IOException { - Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", ObjectUtils.asMap("public_id", "Plattenkreiss_ñg-é", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", asMap("public_id", "Plattenkreiss_ñg-é", "tags", SDK_TEST_TAG)); assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); - cloudinary.uploader().upload(result.get("url"), ObjectUtils.asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(result.get("url"), asMap("tags", SDK_TEST_TAG)); } @Test public void testRename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); Object publicId = result.get("public_id"); String publicId2 = "folder/" + publicId + "2"; cloudinary.uploader().rename((String) publicId, publicId2, ObjectUtils.emptyMap()); assertNotNull(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap())); - Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", asMap("tags", SDK_TEST_TAG)); boolean error_found=false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("tags", SDK_TEST_TAG)); } catch(Exception e) { error_found=true; } assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, ObjectUtils.asMap("overwrite", true, "tags", SDK_TEST_TAG)); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("overwrite", true, "tags", SDK_TEST_TAG)); assertEquals(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap()).get("format"), "ico"); } @Test public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "tags", SDK_TEST_TAG)); assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("use_filename", true, "unique_filename", false, "tags", SDK_TEST_TAG)); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "unique_filename", false, "tags", SDK_TEST_TAG)); assertEquals(result.get("public_id"), "old_logo"); } @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("sample", ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload")); + Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload")); String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg").version(result.get("version")).generate("sample"); String eagerUrl = (String) ((Map) ((List) result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; @@ -158,55 +160,55 @@ public void testExplicit() throws IOException { @Test public void testEager() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", SDK_TEST_TAG)); } @Test public void testHeaders() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", new String[]{"Link: 1"}, "tags", SDK_TEST_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"), "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", new String[]{"Link: 1"}, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", asMap("Link", "1"), "tags", SDK_TEST_TAG)); } @Test public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().text("hello world", asMap("tags", SDK_TEST_TAG)); assertTrue(((Integer) result.get("width")) > 1); assertTrue(((Integer) result.get("height")) > 1); } @Test public void testImageUploadTag() { - String tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("htmlattr", "htmlvalue")); + String tag = cloudinary.uploader().imageUploadTag("test-field", asMap("callback", "http://localhost/cloudinary_cors.html"), asMap("htmlattr", "htmlvalue")); assertTrue(tag.contains("type='file'")); assertTrue(tag.contains("data-cloudinary-field='test-field'")); assertTrue(tag.contains("class='cloudinary-fileupload'")); assertTrue(tag.contains("htmlattr='htmlvalue'")); - tag = cloudinary.uploader().imageUploadTag("test-field", ObjectUtils.asMap("callback", "http://localhost/cloudinary_cors.html"), ObjectUtils.asMap("class", "myclass")); + tag = cloudinary.uploader().imageUploadTag("test-field", asMap("callback", "http://localhost/cloudinary_cors.html"), asMap("class", "myclass")); assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } @Test public void testSprite() throws IOException { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2")); - Map result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2")); + Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", SDK_TEST_TAG)); assertEquals(2, ((Map) result.get("image_infos")).size()); - result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", "w_100")); + result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); assertTrue(((String) result.get("css_url")).contains("w_100")); - result = cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg")); + result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", new Transformation().width(100), "format", "jpg")); assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); } @Test public void testMulti() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_1")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_2")); + Map result = cloudinary.uploader().multi("multi_test_tag", asMap("tags", SDK_TEST_TAG)); assertTrue(((String) result.get("url")).endsWith(".gif")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100")); + result = cloudinary.uploader().multi("multi_test_tag", asMap("transformation", "w_100")); assertTrue(((String) result.get("url")).contains("w_100")); - result = cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf")); + result = cloudinary.uploader().multi("multi_test_tag", asMap("transformation", new Transformation().width(111), "format", "pdf")); assertTrue(((String) result.get("url")).contains("w_111")); assertTrue(((String) result.get("url")).endsWith(".pdf")); } @@ -220,15 +222,15 @@ public void testTags() throws Exception { cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1", "tag2"})); + assertEquals(tags, asArray(new String[]{"tag1", "tag2"})); tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag1"})); + assertEquals(tags, asArray(new String[]{"tag1"})); cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag2"})); + assertEquals(tags, asArray(new String[]{"tag2"})); cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, ObjectUtils.asArray(new String[]{"tag3"})); + assertEquals(tags, asArray(new String[]{"tag3"})); result = cloudinary.uploader().removeAllTags(new String[]{public_id, public_id2, "noSuchId"}, ObjectUtils.emptyMap()); List publicIds = (List) result.get("public_ids"); assertThat(publicIds, containsInAnyOrder(public_id, public_id2)); // = and not containing "noSuchId" @@ -241,7 +243,7 @@ public void testTags() throws Exception { public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); assertEquals(result.get("format"), "png"); } @@ -251,7 +253,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { boolean errorFound = false; String[] formats = {"jpg"}; try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); } catch (Exception e) { errorFound = true; } @@ -262,7 +264,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("allowed_formats", formats, "format", "jpg", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "format", "jpg", "tags", SDK_TEST_TAG)); assertEquals("jpg", result.get("format")); } @@ -274,7 +276,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120, 30, 109, 150); coordinates.addRect(rect1); coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("face_coordinates", coordinates, "faces", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("face_coordinates", coordinates, "faces", true, "tags", SDK_TEST_TAG)); ArrayList resultFaces = ((ArrayList) result.get("faces")); assertEquals(2, resultFaces.size()); @@ -295,8 +297,8 @@ public void testFaceCoordinates() throws Exception { Coordinates differentCoordinates = new Coordinates(); Rectangle rect3 = new Rectangle(122, 32, 111, 152); differentCoordinates.addRect(rect3); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("faces", true)); + cloudinary.uploader().explicit((String) result.get("public_id"), asMap("face_coordinates", differentCoordinates, "faces", true, "type", "upload")); + Map info = cloudinary.api().resource((String) result.get("public_id"), asMap("faces", true)); resultFaces = (ArrayList) info.get("faces"); assertEquals(1, resultFaces.size()); @@ -313,8 +315,8 @@ public void testFaceCoordinates() throws Exception { public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,300,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("custom_coordinates", coordinates, "tags", SDK_TEST_TAG)); - Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("custom_coordinates", coordinates, "tags", SDK_TEST_TAG)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), asMap("coordinates", true)); int[] expected = new int[]{121, 31, SRC_TEST_IMAGE_W, SRC_TEST_IMAGE_H}; Object[] actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++) { @@ -322,8 +324,8 @@ public void testCustomCoordinates() throws Exception { } coordinates = new Coordinates(new int[]{122, 32, SRC_TEST_IMAGE_W + 100, SRC_TEST_IMAGE_H + 100}); - cloudinary.uploader().explicit((String) uploadResult.get("public_id"), ObjectUtils.asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); - result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); + cloudinary.uploader().explicit((String) uploadResult.get("public_id"), asMap("custom_coordinates", coordinates, "coordinates", true, "type", "upload")); + result = cloudinary.api().resource(uploadResult.get("public_id").toString(), asMap("coordinates", true)); expected = new int[]{122, 32, SRC_TEST_IMAGE_W + 100, SRC_TEST_IMAGE_H + 100}; actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); for (int i = 0; i < expected.length; i++) { @@ -331,23 +333,10 @@ public void testCustomCoordinates() throws Exception { } } - @Test - public void testContext() throws Exception { - //should allow sending context - Map context = ObjectUtils.asMap("caption", "some cäption", "alt", "alternativè"); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("context", context, "tags", SDK_TEST_TAG)); - Map info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", context), info.get("context")); - Map differentContext = ObjectUtils.asMap("caption", "different = caption", "alt2", "alt|alternative alternative"); - cloudinary.uploader().explicit((String) result.get("public_id"), ObjectUtils.asMap("type", "upload", "context", differentContext)); - info = cloudinary.api().resource((String) result.get("public_id"), ObjectUtils.asMap("context", true)); - assertEquals(ObjectUtils.asMap("custom", differentContext), info.get("context")); - } - @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("moderation", "manual", "tags", SDK_TEST_TAG)); assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); } @@ -357,7 +346,7 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { //should support requesting raw conversion try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("raw_convert", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("raw_convert", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -367,7 +356,7 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { //should support requesting categorization try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("categorization", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("categorization", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); } @@ -377,7 +366,7 @@ public void testCategorizationRequest() { public void testDetectionRequest() { //should support requesting detection try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("detection", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("detection", "illegal", "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -387,7 +376,7 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { //should support requesting auto tagging try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("auto_tagging", 0.5f, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("auto_tagging", 0.5f, "tags", SDK_TEST_TAG)); } catch (Exception e) { assertTrue(e.getMessage().matches("^Must use(.*)")); } @@ -413,24 +402,24 @@ public void testUploadLarge() throws Exception { String [] tags = new String [] {"upload_large_tag", SDK_TEST_TAG}; - Map resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); + Map resource = cloudinary.uploader().uploadLarge(temp, asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); assertEquals("raw", resource.get("resource_type")); - resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5243000, "tags", tags)); + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), asMap("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")); - resource = cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); + resource = cloudinary.uploader().uploadLarge(temp, asMap("chunk_size", 5880138, "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")); - resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), ObjectUtils.asMap("chunk_size", 5880138, "tags", tags)); + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), asMap("chunk_size", 5880138, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); assertEquals("image", resource.get("resource_type")); assertEquals(1400, resource.get("width")); @@ -440,15 +429,15 @@ public void testUploadLarge() throws Exception { @Test public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets - Map preset = cloudinary.api().createUploadPreset(ObjectUtils.asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map preset = cloudinary.api().createUploadPreset(asMap("folder", "upload_folder", "unsigned", true)); + Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), asMap("tags", SDK_TEST_TAG)); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } @Test public void testFilenameOption() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("filename", "emanelif", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("filename", "emanelif", "tags", SDK_TEST_TAG)); assertEquals("emanelif", result.get("original_filename")); } @@ -457,7 +446,7 @@ public void testResponsiveBreakpoints() throws Exception { ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false); // A single breakpoint - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", breakpoint, "tags", SDK_TEST_TAG )); java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); @@ -465,7 +454,7 @@ public void testResponsiveBreakpoints() throws Exception { assertEquals(2, breakpoints.size()); // an array of breakpoints - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", new ResponsiveBreakpoint[]{breakpoint}, "tags", SDK_TEST_TAG )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); @@ -475,7 +464,7 @@ public void testResponsiveBreakpoints() throws Exception { // a JSONArray of breakpoints JSONArray array = new JSONArray(); array.put(breakpoint); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("responsive_breakpoints", array, "tags", SDK_TEST_TAG + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", array, "tags", SDK_TEST_TAG )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); @@ -511,7 +500,7 @@ public void testDownloadArchive() throws Exception { public void testUploadInvalidUrl() { try { - cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", ObjectUtils.asMap("return_error", true)); + cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", asMap("return_error", true)); fail("Expected exception was not thrown"); } catch(IOException e) { assertEquals(e.getMessage(), "File not found or unreadable: " + REMOTE_TEST_IMAGE + "\n"); From 52dc34fdeca2155b3e625fd120fdb8abf1c57970 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sat, 26 Nov 2016 11:45:57 +0200 Subject: [PATCH 187/592] fix `face_coordinates` test --- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 11af0023..e3c7d418 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 @@ -272,8 +272,8 @@ public void testAllowedFormatsWithFormat() throws Exception { public void testFaceCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121, 31, 110, 151); - Rectangle rect2 = new Rectangle(120, 30, 109, 150); + Rectangle rect1 = new Rectangle(121, 31, 110, 51); + Rectangle rect2 = new Rectangle(120, 30, 109, 51); coordinates.addRect(rect1); coordinates.addRect(rect2); Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("face_coordinates", coordinates, "faces", true, "tags", SDK_TEST_TAG)); From 985523b044005b071825f36b73160888ec249463 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sat, 19 Nov 2016 18:46:23 +0200 Subject: [PATCH 188/592] Version 1.5.0 (cherry picked from commit 81c85f7) --- CHANGELOG.md | 11 +++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94409de7..0754d11e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +1.5.0 / 2016-11-19 +================== + +New functionality +----------------- + + * Add context API + * Escape `\` and `=` in context + * Add `removeAllTags` API + 1.4.6 / 2016-10-27 ==================================== diff --git a/README.md b/README.md index 7f873514..c12d3330 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.4.6 + 1.5.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.4.6/cloudinary-core-1.4.6.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.4.6/cloudinary-http44-1.4.6.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.5.0/cloudinary-core-1.5.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.5.0/cloudinary-http44-1.5.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index efdabd6b..1e4f499b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.4.6"; + public final static String VERSION = "1.5.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From ee9f619294ad463f66c18d4bb2d714c8b0a4e7ba Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 26 Nov 2016 17:58:57 +0200 Subject: [PATCH 189/592] fix face_coordinates test in android --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 963646b0..d0a7ed90 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -234,8 +234,8 @@ public void testFaceCoordinates() throws Exception { if (cloudinary.config.apiSecret == null) return; Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121, 31, 231, 182); - Rectangle rect2 = new Rectangle(120, 30, 229, 270); + Rectangle rect1 = new Rectangle(121, 31, 110, 51); + Rectangle rect2 = new Rectangle(120, 30, 109, 51); coordinates.addRect(rect1); coordinates.addRect(rect2); JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); From e2d6487a572108969ac45a7c47d6bc00962c60a9 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 26 Nov 2016 18:20:40 +0200 Subject: [PATCH 190/592] [maven-release-plugin] prepare release 1.5.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 676787f4..d87acffb 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 com.cloudinary cloudinary-android-test - 1.4.7-SNAPSHOT + 1.5.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.4.7-SNAPSHOT + 1.5.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index d3c9ec38..75fb2de6 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 85fc38f7..19c7606b 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index c6578da0..d70c7875 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 15f648a5..58466d42 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index ca05f86e..6640f894 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index dc5160e4..549e0385 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index dc0dd732..2c65c8ef 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index bc2c6a44..5c1c0231 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.4.7-SNAPSHOT + 1.5.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.5.0 From cca7bc94701ecf11a571b970337eb3f685532018 Mon Sep 17 00:00:00 2001 From: Tal Lev-Ami Date: Sat, 26 Nov 2016 18:20:50 +0200 Subject: [PATCH 191/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index d87acffb..26611e47 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.5.0 + 1.5.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.5.0 + 1.5.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 75fb2de6..53668c17 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 19c7606b..fe898035 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index d70c7875..9ac877fe 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 58466d42..1118785b 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 6640f894..48bd030a 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 549e0385..fcbc4a42 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 2c65c8ef..53bead47 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 5c1c0231..9a59e0fa 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.5.0 + 1.5.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.5.0 + HEAD From 090dc23bac0dcd1092b35fd95642ca7e4f218427 Mon Sep 17 00:00:00 2001 From: daniel cohen Date: Tue, 13 Dec 2016 10:54:08 +0200 Subject: [PATCH 192/592] search by context support --- .../src/main/java/com/cloudinary/Api.java | 16 ++++++++++++++++ .../com/cloudinary/test/AbstractApiTest.java | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 25fa8d33..fba156db 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -14,6 +14,7 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import org.cloudinary.json.JSONArray; +import com.cloudinary.utils.StringUtils; @SuppressWarnings({"rawtypes", "unchecked"}) public class Api { @@ -78,6 +79,21 @@ public ApiResponse resourcesByTag(String tag, Map options) throws Exception { return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); } + public ApiResponse resourcesByContext(String key, Map options) throws Exception { + return resourcesByContext(key,null,options); + } + + 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"); + params.put("key",key); + if (StringUtils.isNotBlank(value)) { + params.put("value",value); + } + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType,"context"), params , options); + } + public ApiResponse resourcesByIds(Iterable publicIds, 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 4b9e8918..0b34cf70 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 @@ -50,6 +50,11 @@ public static void setUpClass() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options.put("public_id", API_TEST_1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + + options = ObjectUtils.asMap("public_id", "context_1", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "test-key=alt"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options = ObjectUtils.asMap("public_id", "context_2", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "test-key=alternate"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } @AfterClass @@ -387,6 +392,18 @@ public void test17aTransformationDeleteImplicit() throws Exception { api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); } + @Test + public void test20ResourcesContext() throws Exception { + Map result = api.resourcesByContext("test-key", ObjectUtils.emptyMap()); + + List resources = (List) result.get("resources"); + assertEquals(2,resources.size()); + result = api.resourcesByContext("test-key","alt", ObjectUtils.emptyMap()); + + resources = (List) result.get("resources"); + assertEquals(1,resources.size()); + } + /** * @throws Exception * @expectedException \Cloudinary\Api\NotFound From b1cec0f9cf6f58702b1b39b9b5f8c44529afa44a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 8 Jan 2017 12:02:31 +0200 Subject: [PATCH 193/592] Version 1.6.0 --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0754d11e..c60bfc9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ +1.6.0 / 2017-01-08 +================== + + * Add Search by context API + 1.5.0 / 2016-11-19 ================== diff --git a/README.md b/README.md index c12d3330..bb148d6e 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.5.0 + 1.6.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.5.0/cloudinary-core-1.5.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.5.0/cloudinary-http44-1.5.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.6.0/cloudinary-core-1.6.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.6.0/cloudinary-http44-1.6.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1e4f499b..21dbf0c1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.5.0"; + public final static String VERSION = "1.6.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From e5385f0526ef61f3b9726795926cd900c18770bf Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 8 Jan 2017 15:11:39 +0200 Subject: [PATCH 194/592] [maven-release-plugin] prepare release 1.6.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 26611e47..f8420589 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 com.cloudinary cloudinary-android-test - 1.5.1-SNAPSHOT + 1.6.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.5.1-SNAPSHOT + 1.6.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 53668c17..987880a8 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index fe898035..3729fe64 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 9ac877fe..299bf7a4 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 1118785b..ca40b617 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 48bd030a..a539e20b 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index fcbc4a42..bb5d9bdc 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 53bead47..05c9eacd 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 9a59e0fa..783806b9 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.5.1-SNAPSHOT + 1.6.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.6.0 From 83e7a96bd4f61445f43141574a02c1810560d9ad Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 8 Jan 2017 15:29:04 +0200 Subject: [PATCH 195/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index f8420589..645ccb62 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.6.0 + 1.6.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.6.0 + 1.6.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 987880a8..4169c45c 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 3729fe64..43f5ff88 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 299bf7a4..78464ec4 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index ca40b617..72b21a4d 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index a539e20b..3c9c1a4c 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index bb5d9bdc..715ffa01 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 05c9eacd..9dd398d1 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 783806b9..3d3a3992 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.6.0 + 1.6.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.6.0 + HEAD From d45a0ff1389b940bd58280dcb8daec6855130547 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 29 Jan 2017 15:06:37 +0200 Subject: [PATCH 196/592] Add Akamai token generator --- .../main/java/com/cloudinary/AkamaiToken.java | 126 ++++++++++++++++++ .../main/java/com/cloudinary/Cloudinary.java | 1 + .../java/com/cloudinary/AkamaiTokenTest.java | 45 +++++++ 3 files changed, 172 insertions(+) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java b/cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java new file mode 100644 index 00000000..6745b1bb --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java @@ -0,0 +1,126 @@ +package com.cloudinary; + +import com.cloudinary.utils.StringUtils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Token generator for Akamai authentication + */ +public class AkamaiToken { + public String tokenName = Cloudinary.AKAMAI_TOKEN_NAME; + public String key; + public long startTime; + public long endTime; + public String ip; + public String acl; + public long window; + + public AkamaiToken(String key) { + this.key = key; + } + + public AkamaiToken setTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + /** + * Set the start time of the token. Defaults to now. + * + * @param startTime in seconds since epoch + * @return + */ + public AkamaiToken setStartTime(long startTime) { + this.startTime = startTime; + return this; + } + + /** + * Set the end time (expiration) of the token + * + * @param endTime in seconds since epoch + * @return + */ + public AkamaiToken setEndTime(long endTime) { + this.endTime = endTime; + return this; + } + + public AkamaiToken setIp(String ip) { + this.ip = ip; + return this; + } + + public AkamaiToken setAcl(String acl) { + this.acl = acl; + return this; + } + + /** + * The duration of the token in seconds. This value is used to calculate the expiration of the token. + * It is ignored if endTime is provided. + * + * @param window + * @return + */ + public AkamaiToken setWindow(long window) { + this.window = window; + return this; + } + + /** + * Generate the authentication token + * + * @return a signed token + */ + public String generate() { + long expiration = endTime; + if (expiration == 0) { + if (window > 0) { + final long start = startTime > 0 ? startTime : Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L; + expiration = start + window; + } else { + throw new IllegalArgumentException("Must provide either endTime or window"); + } + } + ArrayList tokenParts = new ArrayList(); + if (ip != null) { + tokenParts.add("ip=" + ip); + } + if (startTime > 0) { + tokenParts.add("st=" + startTime); + } + tokenParts.add("exp=" + expiration); + tokenParts.add("acl=" + acl); + String auth = digest(StringUtils.join(tokenParts, "~")); + tokenParts.add("hmac=" + auth); + return tokenName + "=" + StringUtils.join(tokenParts, "~"); + } + + + private String digest(String message) { + byte[] binKey = DatatypeConverter.parseHexBinary(key); + try { + Mac hmac = Mac.getInstance("HmacSHA256"); + SecretKeySpec secret = new SecretKeySpec(binKey, "HmacSHA256"); + hmac.init(secret); + final byte[] bytes = message.getBytes(); + return DatatypeConverter.printHexBinary(hmac.doFinal(bytes)).toLowerCase(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } + return null; + } + + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 21dbf0c1..32dd5ebc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -24,6 +24,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Cloudinary { + public static final String AKAMAI_TOKEN_NAME = "__cld_token__"; private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UploaderStrategy", "com.cloudinary.http42.UploaderStrategy", diff --git a/cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java new file mode 100644 index 00000000..b2b05164 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java @@ -0,0 +1,45 @@ +package com.cloudinary; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Calendar; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.*; + +public class AkamaiTokenTest { + public static final String KEY = "00112233FF99"; + + @Test + public void generateWithStartAndWindow() throws Exception { + AkamaiToken t = new AkamaiToken(KEY); + t.setStartTime(1111111111).setAcl("/image/*").setWindow(300); + assertEquals("should generate an Akamai token with startTime and window", "__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af", t.generate()); + } + + @Test + public void generateWithWindow() throws Exception { + long firstExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; + Thread.sleep(1200); + String token = new AkamaiToken(KEY).setAcl("*").setWindow(300).generate(); + Thread.sleep(1200); + long secondExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; + Matcher m = Pattern.compile("exp=(\\d+)").matcher(token); + assertTrue(m.find()); + final String expString = m.group(1); + final long actual = Long.parseLong(expString); + assertThat(actual, Matchers.greaterThanOrEqualTo(firstExp)); + assertThat(actual, Matchers.lessThanOrEqualTo(secondExp)); + assertEquals(token, new AkamaiToken(KEY).setAcl("*").setEndTime(actual).generate()); + } + + @Test(expected = IllegalArgumentException.class) + public void testMustProvideEndTimeOrWindow(){ + new AkamaiToken(KEY).setAcl("*").generate(); + } + + +} \ No newline at end of file From a3f4d8c0f820e45d7fbc3c2b6031a2e18dc6c4ad Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 30 Jan 2017 15:59:33 +0200 Subject: [PATCH 197/592] Fix "multi" test --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 6 +++--- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 0b34cf70..1c669c7e 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 @@ -243,9 +243,9 @@ public void test07ResourceMetadata() throws Exception { // should allow get resource metadata Map resource = api.resource(API_TEST, ObjectUtils.emptyMap()); assertNotNull(resource); - assertEquals(resource.get("public_id"), API_TEST); - assertEquals(resource.get("bytes"), 3381); - assertEquals(((List) resource.get("derived")).size(), 1); + assertEquals(API_TEST, resource.get("public_id")); + assertEquals(3381, resource.get("bytes")); + assertEquals(1, ((List) resource.get("derived")).size()); } @Test 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 e3c7d418..1e00665c 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 @@ -204,7 +204,7 @@ public void testSprite() throws IOException { public void testMulti() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_1")); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().multi("multi_test_tag", asMap("tags", SDK_TEST_TAG, "transformation", "c_crop,w_0.5" )); assertTrue(((String) result.get("url")).endsWith(".gif")); result = cloudinary.uploader().multi("multi_test_tag", asMap("transformation", "w_100")); assertTrue(((String) result.get("url")).contains("w_100")); From 6052643d34acf43ca285ac04ecde8d7a09ffc2bf Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 30 Jan 2017 16:03:35 +0200 Subject: [PATCH 198/592] Version 1.7.0 --- CHANGELOG.md | 13 +++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c60bfc9c..1eb67bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,17 @@ +1.7.0 / 2017-01-30 +================== + +New functionality +----------------- + + * Add Akamai token generator + +Other changes +------------- + + * Fix "multi" test + 1.6.0 / 2017-01-08 ================== diff --git a/README.md b/README.md index bb148d6e..d3b621c3 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.6.0 + 1.7.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.6.0/cloudinary-core-1.6.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.6.0/cloudinary-http44-1.6.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.7.0/cloudinary-core-1.7.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.7.0/cloudinary-http44-1.7.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 32dd5ebc..1b819917 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -41,7 +41,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.6.0"; + public final static String VERSION = "1.7.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 8ce37e9048b10fdc76b35d3a83c3cdc5115ba4f1 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 30 Jan 2017 17:24:52 +0200 Subject: [PATCH 199/592] [maven-release-plugin] prepare release 1.7.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 645ccb62..8e7f047a 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 com.cloudinary cloudinary-android-test - 1.6.1-SNAPSHOT + 1.7.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.6.1-SNAPSHOT + 1.7.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 4169c45c..51420f07 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 43f5ff88..7a64e351 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 78464ec4..47dbea31 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 72b21a4d..506247fe 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 3c9c1a4c..a2d04e68 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 715ffa01..8eee0355 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 9dd398d1..dbd6eebb 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 3d3a3992..2078d55a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.6.1-SNAPSHOT + 1.7.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.7.0 From cae794f98be312de7db4aa50feff444910a9af5f Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 30 Jan 2017 17:25:01 +0200 Subject: [PATCH 200/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 8e7f047a..e049bc44 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.7.0 + 1.7.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.7.0 + 1.7.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 51420f07..b8172674 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 7a64e351..785c14ad 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 47dbea31..50b026c6 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 506247fe..23c65965 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index a2d04e68..412b1f76 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 8eee0355..00aacdf0 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index dbd6eebb..39917cf4 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 2078d55a..10bc2f6f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.7.0 + 1.7.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.7.0 + HEAD From d8c8c254b88715c0bce896ba370b1485437c62a4 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 31 Jan 2017 17:12:34 +0200 Subject: [PATCH 201/592] Refactor `multi` test --- .../cloudinary/test/AbstractUploaderTest.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) 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 1e00665c..cf9068fb 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 @@ -202,15 +202,23 @@ public void testSprite() throws IOException { @Test public void testMulti() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[] {"multi_test_tag", SDK_TEST_TAG}, "public_id", "multi_test_tag_2")); - Map result = cloudinary.uploader().multi("multi_test_tag", asMap("tags", SDK_TEST_TAG, "transformation", "c_crop,w_0.5" )); + final String MULTI_TEST_TAG = "multi_test_tag" + SUFFIX; + final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, uniqueTag}); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + List ids = new ArrayList(); + Map result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5" )); + ids.add((String) result.get("public_id")); + Map pdfResult = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", new Transformation().width(111), "format", "pdf")); + ids.add((String) pdfResult.get("public_id")); + try { + cloudinary.api().deleteResources(ids, ObjectUtils.emptyMap()); + } catch (Exception ignored) { + } assertTrue(((String) result.get("url")).endsWith(".gif")); - result = cloudinary.uploader().multi("multi_test_tag", asMap("transformation", "w_100")); - assertTrue(((String) result.get("url")).contains("w_100")); - result = cloudinary.uploader().multi("multi_test_tag", asMap("transformation", new Transformation().width(111), "format", "pdf")); - assertTrue(((String) result.get("url")).contains("w_111")); - assertTrue(((String) result.get("url")).endsWith(".pdf")); + assertTrue(((String) result.get("url")).contains("w_0.5")); + assertTrue(((String) pdfResult.get("url")).contains("w_111")); + assertTrue(((String) pdfResult.get("url")).endsWith(".pdf")); } @Test From 0a2192c8906f8a406ec2abf3daaed67ad7accecb Mon Sep 17 00:00:00 2001 From: daniel cohen Date: Mon, 9 Jan 2017 13:41:49 +0200 Subject: [PATCH 202/592] access_mode api stubs --- .../src/main/java/com/cloudinary/Api.java | 109 ++++++++++++++++-- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index fba156db..b49c7804 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -1,7 +1,6 @@ package com.cloudinary; import java.util.*; -import java.util.concurrent.ExecutionException; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; @@ -13,6 +12,7 @@ import com.cloudinary.api.exceptions.RateLimited; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONArray; import com.cloudinary.utils.StringUtils; @@ -70,13 +70,19 @@ public ApiResponse resources(Map options) throws Exception { uri.add(resourceType); if (type != null) uri.add(type); - return callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); + + ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); + addAccessModeToResponse(response,"public"); + return response; } public ApiResponse resourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + addAccessModeToResponse(response,"public"); + return response; } public ApiResponse resourcesByContext(String key, Map options) throws Exception { @@ -100,22 +106,32 @@ public ApiResponse resourcesByIds(Iterable publicIds, Map options) throw String type = ObjectUtils.asString(options.get("type"), "upload"); Map params = ObjectUtils.only(options, "tags", "context", "moderations"); params.put("public_ids", publicIds); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); + addAccessModeToResponse(response,"public"); + return response; } 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"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"), options); + addAccessModeToResponse(response,"public"); + return response; } 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"); - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), + + 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"), options); + + + addAccessModeToResponse(response,"public"); + return response; } public ApiResponse update(String public_id, Map options) throws Exception { @@ -125,8 +141,10 @@ public ApiResponse update(String public_id, Map options) throws Exception { Map params = new HashMap(); Util.processWriteParameters(options, params); params.put("moderation_status", options.get("moderation_status")); - return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), + ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), params, options); + addAccessModeToResponse(response,"public"); + return response; } public ApiResponse deleteResources(Iterable publicIds, Map options) throws Exception { @@ -250,7 +268,9 @@ public ApiResponse restore(Iterable publicIds, Map options) throws Excep String type = ObjectUtils.asString(options.get("type"), "upload"); Map params = new HashMap(); params.put("public_ids", publicIds); - return callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); + + ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); + return response; } public ApiResponse uploadMappings(Map options) throws Exception { @@ -449,6 +469,79 @@ public ApiResponse updateStreamingProfile(String name, String displayName, List< return callApi(HttpMethod.PUT, uri, params, options); } + public ApiResponse updateResourcesAccessModeByIds(String accessMode, List ids, Map options) throws Exception { + if (options == null) options = ObjectUtils.asMap(); + validateAccessMode(accessMode); + options.put("max_results",100); + + ApiResponse response = this.resourcesByIds(ids,options); + addAccessModeToResponse(response,accessMode); + response.put("updated",response.get("resources")); + response.remove("resources"); + response.put("failed" ,Arrays.asList()); + + return response; + } + + public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag,Map options) throws Exception { + if (options == null) options = ObjectUtils.asMap(); + validateAccessMode(accessMode); + options.put("max_results",100); + ApiResponse response = this.resourcesByTag(tag,options); + addAccessModeToResponse(response,accessMode); + response.put("updated",response.get("resources")); + response.remove("resources"); + response.put("failed" ,Arrays.asList()); + return response; + } + + public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String prefix, Map options) throws Exception{ + if (options == null) options = ObjectUtils.asMap(); + validateAccessMode(accessMode); + options.put("prefix",prefix); + options.put("max_results",100); + ApiResponse response = this.resources(options); + addAccessModeToResponse(response,accessMode); + response.put("updated",response.get("resources")); + response.remove("resources"); + response.put("failed" ,Arrays.asList()); + return response; + } + + private void validateAccessMode(String accessMode){ + List modes = Arrays.asList("public","authenticated"); + if (!modes.contains(accessMode)){ + throw new Error("access mode \""+accessMode+"\" not does not match "+ StringUtils.join(modes,"/")); + } + } + + private void addAccessModeToCollection(Object collection, String accessMode){ + List collectionList = (List) collection; + if (collectionList==null) { + throw new Error("no collection found"); + + } + for (Object listItem :collectionList){ + this.addAccessModeToResource(listItem, accessMode); + } + } + + private void addAccessModeToResponse(ApiResponse response, String accessMode){ + if (response.keySet().contains("resources")){ + addAccessModeToCollection(response.get("resources"),accessMode); + }else if (response.keySet().contains("public_id")){ + addAccessModeToResource(response,accessMode); + }else { + throw new Error("unidentified response type (keys: "+StringUtils.join(response.keySet(),",")+")"); + } + } + + private void addAccessModeToResource(Object resource, String accessMode){ + Map resourceMap = (Map) resource; + if (resourceMap==null) { return ;} + resourceMap.put("access_mode",accessMode); + } + /** * @see Api#updateStreamingProfile(String, String, List, Map) */ From 725b875e97c0ca54095e9269ec7c4de2ba06ca2e Mon Sep 17 00:00:00 2001 From: daniel cohen Date: Tue, 10 Jan 2017 15:15:19 +0200 Subject: [PATCH 203/592] added comments removed access mode level validation --- .../src/main/java/com/cloudinary/Api.java | 21 +++++++++++-------- 1 file changed, 12 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 b49c7804..5cde7ea3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -72,6 +72,7 @@ public ApiResponse resources(Map options) throws Exception { uri.add(type); ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -81,6 +82,7 @@ public ApiResponse resourcesByTag(String tag, Map options) throws Exception { 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"), options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -107,6 +109,7 @@ public ApiResponse resourcesByIds(Iterable publicIds, Map options) throw Map params = ObjectUtils.only(options, "tags", "context", "moderations"); params.put("public_ids", publicIds); ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -116,6 +119,7 @@ public ApiResponse resourcesByModeration(String kind, String status, Map options 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"), options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -130,6 +134,7 @@ public ApiResponse resource(String public_id, Map options) throws Exception { "image_metadata", "pages", "phash", "max_results"), options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -143,6 +148,7 @@ public ApiResponse update(String public_id, Map options) throws Exception { params.put("moderation_status", options.get("moderation_status")); ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), params, options); + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,"public"); return response; } @@ -469,12 +475,14 @@ public ApiResponse updateStreamingProfile(String name, String displayName, List< return callApi(HttpMethod.PUT, uri, params, options); } + /* Update access mode method stubs */ public ApiResponse updateResourcesAccessModeByIds(String accessMode, List ids, Map options) throws Exception { if (options == null) options = ObjectUtils.asMap(); - validateAccessMode(accessMode); options.put("max_results",100); ApiResponse response = this.resourcesByIds(ids,options); + + // stubbing default access mode to response , will be removed in final version addAccessModeToResponse(response,accessMode); response.put("updated",response.get("resources")); response.remove("resources"); @@ -485,7 +493,6 @@ public ApiResponse updateResourcesAccessModeByIds(String accessMode, List modes = Arrays.asList("public","authenticated"); - if (!modes.contains(accessMode)){ - throw new Error("access mode \""+accessMode+"\" not does not match "+ StringUtils.join(modes,"/")); - } - } + /* Temporary access mode helper methods */ private void addAccessModeToCollection(Object collection, String accessMode){ List collectionList = (List) collection; @@ -541,6 +543,7 @@ private void addAccessModeToResource(Object resource, String accessMode){ if (resourceMap==null) { return ;} resourceMap.put("access_mode",accessMode); } + /* temporary access_mode stubs */ /** * @see Api#updateStreamingProfile(String, String, List, Map) From c60af98f0908e5778f8b9f9dea4689b25fdc7112 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 30 Jan 2017 11:22:11 +0200 Subject: [PATCH 204/592] unstub access mode support --- .../src/main/java/com/cloudinary/Api.java | 130 +++++++----------- .../src/main/java/com/cloudinary/Util.java | 1 + .../com/cloudinary/test/AbstractApiTest.java | 45 ++++++ 3 files changed, 96 insertions(+), 80 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 5cde7ea3..f0dc73f7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -72,8 +72,6 @@ public ApiResponse resources(Map options) throws Exception { uri.add(type); ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at"), options); - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -82,8 +80,6 @@ public ApiResponse resourcesByTag(String tag, Map options) throws Exception { 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"), options); - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -109,8 +105,6 @@ public ApiResponse resourcesByIds(Iterable publicIds, Map options) throw Map params = ObjectUtils.only(options, "tags", "context", "moderations"); params.put("public_ids", publicIds); ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type), params, options); - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -119,8 +113,6 @@ public ApiResponse resourcesByModeration(String kind, String status, Map options 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"), options); - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -133,9 +125,6 @@ public ApiResponse resource(String public_id, Map options) throws Exception { ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", "image_metadata", "pages", "phash", "max_results"), options); - - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -148,8 +137,6 @@ public ApiResponse update(String public_id, Map options) throws Exception { params.put("moderation_status", options.get("moderation_status")); ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), params, options); - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,"public"); return response; } @@ -475,81 +462,64 @@ public ApiResponse updateStreamingProfile(String name, String displayName, List< return callApi(HttpMethod.PUT, uri, params, options); } - /* Update access mode method stubs */ - public ApiResponse updateResourcesAccessModeByIds(String accessMode, List ids, Map options) throws Exception { - if (options == null) options = ObjectUtils.asMap(); - options.put("max_results",100); - - ApiResponse response = this.resourcesByIds(ids,options); - - // stubbing default access mode to response , will be removed in final version - addAccessModeToResponse(response,accessMode); - response.put("updated",response.get("resources")); - response.remove("resources"); - response.put("failed" ,Arrays.asList()); - - return response; - } - - public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag,Map options) throws Exception { - if (options == null) options = ObjectUtils.asMap(); - options.put("max_results",100); - ApiResponse response = this.resourcesByTag(tag,options); - addAccessModeToResponse(response,accessMode); - response.put("updated",response.get("resources")); - response.remove("resources"); - response.put("failed" ,Arrays.asList()); - return response; - } - - public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String prefix, Map options) throws Exception{ - if (options == null) options = ObjectUtils.asMap(); - options.put("prefix",prefix); - options.put("max_results",100); - ApiResponse response = this.resources(options); - addAccessModeToResponse(response,accessMode); - response.put("updated",response.get("resources")); - response.remove("resources"); - response.put("failed" ,Arrays.asList()); - return response; - } - /* Update access mode method stubs */ - - /* Temporary access mode helper methods */ - - private void addAccessModeToCollection(Object collection, String accessMode){ - List collectionList = (List) collection; - if (collectionList==null) { - throw new Error("no collection found"); - - } - for (Object listItem :collectionList){ - this.addAccessModeToResource(listItem, accessMode); - } + /** + * @see Api#updateStreamingProfile(String, String, List, Map) + */ + public ApiResponse updateStreamingProfile(String name, String displayName, List representations) throws Exception { + return createStreamingProfile(name, displayName, representations); } - private void addAccessModeToResponse(ApiResponse response, String accessMode){ - if (response.keySet().contains("resources")){ - addAccessModeToCollection(response.get("resources"),accessMode); - }else if (response.keySet().contains("public_id")){ - addAccessModeToResource(response,accessMode); - }else { - throw new Error("unidentified response type (keys: "+StringUtils.join(response.keySet(),",")+")"); - } + /** + * Update access mode of one or more resources by prefix + * + * @param accessMode The new access mode, "public" or "authenticated" + * @param prefix The prefix by which to filter applicable resources + * @param options + * @return + * @throws Exception + */ + public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String prefix, Map options) throws Exception { + return updateResourcesAccessMode(accessMode, "prefix", prefix, options); } - private void addAccessModeToResource(Object resource, String accessMode){ - Map resourceMap = (Map) resource; - if (resourceMap==null) { return ;} - resourceMap.put("access_mode",accessMode); + /** + * Update access mode of one or more resources by tag + * + * @param accessMode The new access mode, "public" or "authenticated" + * @param tag The tag by which to filter applicable resources + * @param options + * @return + * @throws Exception + */ + public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, Map options) throws Exception { + return updateResourcesAccessMode(accessMode, "tag", tag, options); } - /* temporary access_mode stubs */ /** - * @see Api#updateStreamingProfile(String, String, List, Map) + * Update access mode of one or more resources by publicIds + * + * @param accessMode The new access mode, "public" or "authenticated" + * @param publicIds A list of public ids of resources to be updated + * @param options + * @return + * @throws Exception */ - public ApiResponse updateStreamingProfile(String name, String displayName, List representations) throws Exception { - return createStreamingProfile(name, displayName, representations); + public ApiResponse updateResourcesAccessModeByIds(String accessMode, Iterable publicIds, Map options) throws Exception { + return updateResourcesAccessMode(accessMode, "public_ids", publicIds, options); + } + + private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, Object value, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); + List uri = new ArrayList(); + uri.add("resources"); + uri.add(resourceType); + uri.add("upload"); + uri.add("update_access_mode"); + Map params = new HashMap(); + params.put("access_mode", accessMode); + params.put(byKey, value); + return callApi(HttpMethod.POST, uri, params, options); } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index ea7e6fb3..cbdcdff8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -30,6 +30,7 @@ public static final Map buildUploadParams(Map options) { params.put("folder", (String) options.get("folder")); params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); + params.put("access_mode", (String) options.get("access_mode")); Object responsive_breakpoints = options.get("responsive_breakpoints"); if (responsive_breakpoints != null) { params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints)); 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 1c669c7e..3176e980 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 @@ -749,4 +749,49 @@ public void testPublishByTag() throws Exception { assertNotNull(resource.get("url")); cloudinary.uploader().destroy(publicId, null); } + + @Test + public void testUpdateResourcesAccessModeByIds() throws Exception { + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "access_mode", "authenticated")); + String publicId = (String) response.get("public_id"); + assertEquals(response.get("access_mode"), "authenticated"); + response = cloudinary.api().updateResourcesAccessModeByIds("public", Arrays.asList(publicId), null); + List updated = (List) response.get("updated"); + assertNotNull(updated); + assertEquals(updated.size(), 1); + Map resource = (Map) updated.get(0); + assertEquals(resource.get("public_id"), publicId); + assertEquals(resource.get("access_mode"), "public"); + cloudinary.uploader().destroy(publicId, null); + } + + @Test + public void testUpdateResourcesAccessModeByPrefix() throws Exception { + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "access_mode", "authenticated")); + String publicId = (String) response.get("public_id"); + assertEquals(response.get("access_mode"), "authenticated"); + response = cloudinary.api().updateResourcesAccessModeByPrefix("public", publicId.substring(0, publicId.length() - 2), null); + List updated = (List) response.get("updated"); + assertNotNull(updated); + assertEquals(updated.size(), 1); + Map resource = (Map) updated.get(0); + assertEquals(resource.get("public_id"), publicId); + assertEquals(resource.get("access_mode"), "public"); + cloudinary.uploader().destroy(publicId, null); + } + + @Test + public void testUpdateResourcesAccessModeByTag() throws Exception { + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", Arrays.asList(uniqueTag, uniqueTag + "2"), "access_mode", "authenticated")); + String publicId = (String) response.get("public_id"); + assertEquals(response.get("access_mode"), "authenticated"); + response = cloudinary.api().updateResourcesAccessModeByTag("public", uniqueTag + "2", null); + List updated = (List) response.get("updated"); + assertNotNull(updated); + assertEquals(updated.size(), 1); + Map resource = (Map) updated.get(0); + assertEquals(resource.get("public_id"), publicId); + assertEquals(resource.get("access_mode"), "public"); + cloudinary.uploader().destroy(publicId, null); + } } From 61c905f2ace3e98164281f97c041e6fe52aff409 Mon Sep 17 00:00:00 2001 From: Itai Benari Date: Mon, 6 Feb 2017 12:44:39 +0200 Subject: [PATCH 205/592] add missing support for next cursor and future proof type --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index f0dc73f7..fef477c9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -514,12 +514,12 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O List uri = new ArrayList(); uri.add("resources"); uri.add(resourceType); - uri.add("upload"); + uri.add(ObjectUtils.asString(options.get("type"), "upload")); uri.add("update_access_mode"); Map params = new HashMap(); params.put("access_mode", accessMode); params.put(byKey, value); - return callApi(HttpMethod.POST, uri, params, options); + return callApi(HttpMethod.POST, uri, params, ObjectUtils.only(options, "next_cursor")); } } From da35b2b8e64507373601ade9c2eeffe767858e9d Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 7 Feb 2017 11:39:45 +0200 Subject: [PATCH 206/592] Add `type`, `next_cursor`, `max_results` and javadoc to access mode API. --- .../src/main/java/com/cloudinary/Api.java | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index fef477c9..edd96238 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -4,12 +4,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; -import com.cloudinary.api.exceptions.AlreadyExists; -import com.cloudinary.api.exceptions.BadRequest; -import com.cloudinary.api.exceptions.GeneralError; -import com.cloudinary.api.exceptions.NotAllowed; -import com.cloudinary.api.exceptions.NotFound; -import com.cloudinary.api.exceptions.RateLimited; +import com.cloudinary.api.exceptions.*; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -473,10 +468,19 @@ public ApiResponse updateStreamingProfile(String name, String displayName, List< * Update access mode of one or more resources by prefix * * @param accessMode The new access mode, "public" or "authenticated" - * @param prefix The prefix by which to filter applicable resources - * @param options - * @return - * @throws Exception + * @param prefix The prefix by which to filter applicable resources + * @param options additional options + *
    + *
  • resource_type - (default "image") - the type of resources to modify
  • + *
  • max_results - optional - the maximum resources to process in a single invocation
  • + *
  • next_cursor - optional - provided by a previous call to the method
  • + *
+ * @return a map of the returned values + *
    + *
  • updated - an array of resources
  • + *
  • next_cursor - optional - provided if more resources can be processed
  • + *
+ * @throws ApiException an API exception */ public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String prefix, Map options) throws Exception { return updateResourcesAccessMode(accessMode, "prefix", prefix, options); @@ -486,10 +490,19 @@ public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String p * Update access mode of one or more resources by tag * * @param accessMode The new access mode, "public" or "authenticated" - * @param tag The tag by which to filter applicable resources - * @param options - * @return - * @throws Exception + * @param tag The tag by which to filter applicable resources + * @param options additional options + *
    + *
  • resource_type - (default "image") - the type of resources to modify
  • + *
  • max_results - optional - the maximum resources to process in a single invocation
  • + *
  • next_cursor - optional - provided by a previous call to the method
  • + *
+ * @return a map of the returned values + *
    + *
  • updated - an array of resources
  • + *
  • next_cursor - optional - provided if more resources can be processed
  • + *
+ * @throws ApiException an API exception */ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, Map options) throws Exception { return updateResourcesAccessMode(accessMode, "tag", tag, options); @@ -499,10 +512,19 @@ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, * Update access mode of one or more resources by publicIds * * @param accessMode The new access mode, "public" or "authenticated" - * @param publicIds A list of public ids of resources to be updated - * @param options - * @return - * @throws Exception + * @param publicIds A list of public ids of resources to be updated + * @param options additional options + *
    + *
  • resource_type - (default "image") - the type of resources to modify
  • + *
  • max_results - optional - the maximum resources to process in a single invocation
  • + *
  • next_cursor - optional - provided by a previous call to the method
  • + *
+ * @return a map of the returned values + *
    + *
  • updated - an array of resources
  • + *
  • next_cursor - optional - provided if more resources can be processed
  • + *
+ * @throws ApiException an API exception */ public ApiResponse updateResourcesAccessModeByIds(String accessMode, Iterable publicIds, Map options) throws Exception { return updateResourcesAccessMode(accessMode, "public_ids", publicIds, options); @@ -511,15 +533,12 @@ public ApiResponse updateResourcesAccessModeByIds(String accessMode, Iterable uri = new ArrayList(); - uri.add("resources"); - uri.add(resourceType); - uri.add(ObjectUtils.asString(options.get("type"), "upload")); - uri.add("update_access_mode"); - Map params = new HashMap(); + String type = ObjectUtils.asString(options.get("type"), "upload"); + List uri = Arrays.asList("resources", resourceType, type, "update_access_mode"); + Map params = ObjectUtils.only(options, "next_cursor", "max_results"); params.put("access_mode", accessMode); params.put(byKey, value); - return callApi(HttpMethod.POST, uri, params, ObjectUtils.only(options, "next_cursor")); + return callApi(HttpMethod.POST, uri, params, options); } } From f11f43fffaf38b4637c3009453cfdd902ff885fb Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Feb 2017 00:39:05 +0200 Subject: [PATCH 207/592] Fix listing direction test. --- .../com/cloudinary/test/AbstractApiTest.java | 62 +++++++++++-------- .../cloudinary/test/AbstractUploaderTest.java | 4 ++ 2 files changed, 40 insertions(+), 26 deletions(-) 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 3176e980..1ea4b207 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 @@ -36,6 +36,7 @@ abstract public class AbstractApiTest extends MockableTest { public static final String API_TEST_UPLOAD_PRESET_2 = API_TEST_UPLOAD_PRESET + "2"; public static final String API_TEST_UPLOAD_PRESET_3 = API_TEST_UPLOAD_PRESET + "3"; public static final String API_TEST_UPLOAD_PRESET_4 = API_TEST_UPLOAD_PRESET + "4"; + public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, uniqueTag}; protected Api api; @BeforeClass @@ -61,7 +62,8 @@ public static void setUpClass() throws IOException { public static void tearDownClass() { Api api = MockableTest.cleanUp(); try { - api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); +// api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); + api.deleteResourcesByTag(uniqueTag, ObjectUtils.emptyMap()); } catch (Exception ignored) { } try { @@ -183,12 +185,20 @@ public void test05ResourcesByPrefix() throws Exception { @Test public void testResourcesListingDirection() throws Exception { // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc")); + Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc", "max_results", 500)); List resources = (List) result.get("resources"); - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1)); + ArrayList resourceIds = new ArrayList(); + for (Map resource : resources) { + resourceIds.add((String) resource.get("public_id")); + } + result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1, "max_results", 500)); List resourcesDesc = (List) result.get("resources"); - Collections.reverse(resources); - assertEquals(resources, resourcesDesc); + ArrayList resourceIdsDesc = new ArrayList(); + for (Map resource : resourcesDesc) { + resourceIdsDesc.add((String) resource.get("public_id")); + } + Collections.reverse(resourceIds); + assertEquals(resourceIds, resourceIdsDesc); } @Ignore @@ -198,7 +208,7 @@ public void testResourcesListingStartAt() throws Exception { Thread.sleep(2000L); java.util.Date startAt = new java.util.Date(); Thread.sleep(2000L); - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", SDK_TEST_TAG)); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); ApiResponse listResources = api.resources(ObjectUtils.asMap("type", "upload", "start_at", startAt, "direction", "asc")); List resources = (List) listResources.get("resources"); assertEquals(response.get("public_id"), resources.get(0).get("public_id")); @@ -252,7 +262,7 @@ public void test07ResourceMetadata() throws Exception { public void test08DeleteDerived() throws Exception { // should allow deleting derived resource cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", API_TEST_3, "tags", SDK_TEST_TAG, "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); + ObjectUtils.asMap("public_id", API_TEST_3, "tags", UPLOAD_TAGS, "eager", Collections.singletonList(new Transformation().width(101).crop("scale")))); Map resource = api.resource(API_TEST_3, ObjectUtils.emptyMap()); assertNotNull(resource); List derived = (List) resource.get("derived"); @@ -269,7 +279,7 @@ public void test08DeleteDerived() throws Exception { public void test09DeleteResources() throws Exception { // should allow deleting resources String public_id = "api_,test3"; - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id, "tags", SDK_TEST_TAG)); + 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); api.deleteResources(Arrays.asList(API_TEST_2, public_id), ObjectUtils.emptyMap()); @@ -279,7 +289,7 @@ public void test09DeleteResources() throws Exception { @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix", "tags", UPLOAD_TAGS)); Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); assertNotNull(resource); api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); @@ -434,7 +444,7 @@ public void test19Ping() throws Exception { public void testDeleteAllResources() throws Exception { // should allow deleting all resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", API_TEST_5, "tags", SDK_TEST_TAG, "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); + ObjectUtils.asMap("public_id", API_TEST_5, "tags", UPLOAD_TAGS, "eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); Map result = api.resource(API_TEST_5, ObjectUtils.emptyMap()); assertEquals(1, ((org.cloudinary.json.JSONArray) result.get("derived")).length()); api.deleteAllResources(ObjectUtils.asMap("keep_original", true)); @@ -446,8 +456,8 @@ public void testDeleteAllResources() throws Exception { @Test public void testManualModeration() throws Exception { // should support setting manual moderation status - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); - Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved", "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", UPLOAD_TAGS)); + Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("moderation_status", "approved", "tags", UPLOAD_TAGS)); assertEquals("approved", ((Map) ((List) apiResult.get("moderation")).get(0)).get("status")); } @@ -455,7 +465,7 @@ public void testManualModeration() throws Exception { public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -467,7 +477,7 @@ public void testOcrUpdate() { public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -479,7 +489,7 @@ public void testRawConvertUpdate() { public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -491,7 +501,7 @@ public void testCategorizationUpdate() { public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -503,7 +513,7 @@ public void testDetectionUpdate() { public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -515,7 +525,7 @@ public void testSimilaritySearchUpdate() { public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[]{121, 31, 110, 151}; @@ -601,9 +611,9 @@ public void testListByModerationUpdate() throws Exception { // "should support listing by moderation kind and value List resources; - Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); - Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); - Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map result1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", UPLOAD_TAGS)); + Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", UPLOAD_TAGS)); + Map result3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("moderation", "manual", "tags", UPLOAD_TAGS)); api.update((String) result1.get("public_id"), ObjectUtils.asMap("moderation_status", "approved")); api.update((String) result2.get("public_id"), ObjectUtils.asMap("moderation_status", "rejected")); Map approved = api.resourcesByModeration("manual", "approved", ObjectUtils.asMap("max_results", 1000)); @@ -632,10 +642,10 @@ public void testListByModerationUpdate() throws Exception { // @Test public void testFolderApi() throws Exception { // should allow deleting all resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item", "tags", SDK_TEST_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item", "tags", SDK_TEST_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item", "tags", SDK_TEST_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/item", "tags", UPLOAD_TAGS)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder2/item", "tags", UPLOAD_TAGS)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder1/item", "tags", UPLOAD_TAGS)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "test_folder1/test_subfolder2/item", "tags", UPLOAD_TAGS)); Map result = api.rootFolders(null); assertEquals("test_folder1", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(0)).get("name")); assertEquals("test_folder2", ((Map) ((org.cloudinary.json.JSONArray) result.get("folders")).get(1)).get("name")); @@ -654,7 +664,7 @@ public void testFolderApi() throws Exception { public void testRestore() throws Exception { // should support restoring resources cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", "api_test_restore", "backup", true, "tags", SDK_TEST_TAG)); + 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); api.deleteResources(Collections.singletonList("api_test_restore"), ObjectUtils.emptyMap()); 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 cf9068fb..2fb82f67 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 @@ -49,6 +49,10 @@ public static void tearDownClass() { api.deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); } catch (Exception ignored) { } + try { + api.deleteResourcesByTag(SDK_TEST_TAG, ObjectUtils.emptyMap()); + } catch (Exception ignored) { + } } @Rule From 648247982786992098c455dd73eb1258a2b596a4 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Feb 2017 01:45:01 +0200 Subject: [PATCH 208/592] Version 1.8.0 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb67bf9..ef520724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ +1.8.0 / 2017-02-08 +================== + +New functionality +----------------- + + * Access mode API + +Other changes +------------- + + * Fix listing direction test. + * Refactor `multi` test + 1.7.0 / 2017-01-30 ================== diff --git a/README.md b/README.md index d3b621c3..1dc81fde 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.7.0 + 1.8.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.7.0/cloudinary-core-1.7.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.7.0/cloudinary-http44-1.7.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.8.0/cloudinary-core-1.8.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.8.0/cloudinary-http44-1.8.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1b819917..0dd4a02d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -41,7 +41,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.7.0"; + public final static String VERSION = "1.8.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 08118248c8258e17704c9e6b11ed53815d8c0931 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Feb 2017 02:06:13 +0200 Subject: [PATCH 209/592] [maven-release-plugin] prepare release 1.8.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index e049bc44..215ba13d 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 com.cloudinary cloudinary-android-test - 1.7.1-SNAPSHOT + 1.8.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.7.1-SNAPSHOT + 1.8.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index b8172674..66141b19 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 785c14ad..78012ce5 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 50b026c6..67da209b 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 23c65965..ce51231e 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 412b1f76..3f2966ba 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 00aacdf0..5ca3dc9b 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 39917cf4..141d341a 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 10bc2f6f..37335a32 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.7.1-SNAPSHOT + 1.8.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.8.0 From 85147731e646a78a09def60bbc0c25b9c9e802d1 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Feb 2017 02:06:22 +0200 Subject: [PATCH 210/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 215ba13d..df7be85f 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.8.0 + 1.8.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.8.0 + 1.8.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 66141b19..3450f2ac 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 78012ce5..2910eef9 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 67da209b..94da6653 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index ce51231e..74ca6e2b 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 3f2966ba..e48ccf33 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 5ca3dc9b..4392bcfb 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 141d341a..efea90e4 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 37335a32..140a70a2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.8.0 + 1.8.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.8.0 + HEAD From 12fc1c1ce0a464264dffd2c2cd6ca8207c1ccaf5 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 6 Feb 2017 08:58:51 +0200 Subject: [PATCH 211/592] Rename AuthToken. Add url parameter. --- .../{AkamaiToken.java => AuthToken.java} | 32 ++++++++++++------- ...kamaiTokenTest.java => AuthTokenTest.java} | 10 +++--- 2 files changed, 26 insertions(+), 16 deletions(-) rename cloudinary-core/src/main/java/com/cloudinary/{AkamaiToken.java => AuthToken.java} (81%) rename cloudinary-core/src/test/java/com/cloudinary/{AkamaiTokenTest.java => AuthTokenTest.java} (82%) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java similarity index 81% rename from cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java rename to cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 6745b1bb..e4291edd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AkamaiToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -12,9 +12,9 @@ import java.util.TimeZone; /** - * Token generator for Akamai authentication + * Authentication Token generator */ -public class AkamaiToken { +public class AuthToken { public String tokenName = Cloudinary.AKAMAI_TOKEN_NAME; public String key; public long startTime; @@ -23,11 +23,11 @@ public class AkamaiToken { public String acl; public long window; - public AkamaiToken(String key) { + public AuthToken(String key) { this.key = key; } - public AkamaiToken setTokenName(String tokenName) { + public AuthToken setTokenName(String tokenName) { this.tokenName = tokenName; return this; } @@ -38,7 +38,7 @@ public AkamaiToken setTokenName(String tokenName) { * @param startTime in seconds since epoch * @return */ - public AkamaiToken setStartTime(long startTime) { + public AuthToken setStartTime(long startTime) { this.startTime = startTime; return this; } @@ -49,17 +49,17 @@ public AkamaiToken setStartTime(long startTime) { * @param endTime in seconds since epoch * @return */ - public AkamaiToken setEndTime(long endTime) { + public AuthToken setEndTime(long endTime) { this.endTime = endTime; return this; } - public AkamaiToken setIp(String ip) { + public AuthToken setIp(String ip) { this.ip = ip; return this; } - public AkamaiToken setAcl(String acl) { + public AuthToken setAcl(String acl) { this.acl = acl; return this; } @@ -71,7 +71,7 @@ public AkamaiToken setAcl(String acl) { * @param window * @return */ - public AkamaiToken setWindow(long window) { + public AuthToken setWindow(long window) { this.window = window; return this; } @@ -82,6 +82,10 @@ public AkamaiToken setWindow(long window) { * @return a signed token */ public String generate() { + return generate(null); + } + + public String generate(String url) { long expiration = endTime; if (expiration == 0) { if (window > 0) { @@ -99,12 +103,18 @@ public String generate() { tokenParts.add("st=" + startTime); } tokenParts.add("exp=" + expiration); - tokenParts.add("acl=" + acl); + if (url != null) { + tokenParts.add("url=" + url); + } else if (acl != null) { + tokenParts.add("acl=" + acl); + } else { + throw new IllegalArgumentException("Must provide either url or acl"); + } String auth = digest(StringUtils.join(tokenParts, "~")); tokenParts.add("hmac=" + auth); return tokenName + "=" + StringUtils.join(tokenParts, "~"); - } + } private String digest(String message) { byte[] binKey = DatatypeConverter.parseHexBinary(key); diff --git a/cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java similarity index 82% rename from cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java rename to cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index b2b05164..a49d314a 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AkamaiTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -10,12 +10,12 @@ import static org.junit.Assert.*; -public class AkamaiTokenTest { +public class AuthTokenTest { public static final String KEY = "00112233FF99"; @Test public void generateWithStartAndWindow() throws Exception { - AkamaiToken t = new AkamaiToken(KEY); + AuthToken t = new AuthToken(KEY); t.setStartTime(1111111111).setAcl("/image/*").setWindow(300); assertEquals("should generate an Akamai token with startTime and window", "__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af", t.generate()); } @@ -24,7 +24,7 @@ public void generateWithStartAndWindow() throws Exception { public void generateWithWindow() throws Exception { long firstExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; Thread.sleep(1200); - String token = new AkamaiToken(KEY).setAcl("*").setWindow(300).generate(); + String token = new AuthToken(KEY).setAcl("*").setWindow(300).generate(); Thread.sleep(1200); long secondExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; Matcher m = Pattern.compile("exp=(\\d+)").matcher(token); @@ -33,12 +33,12 @@ public void generateWithWindow() throws Exception { final long actual = Long.parseLong(expString); assertThat(actual, Matchers.greaterThanOrEqualTo(firstExp)); assertThat(actual, Matchers.lessThanOrEqualTo(secondExp)); - assertEquals(token, new AkamaiToken(KEY).setAcl("*").setEndTime(actual).generate()); + assertEquals(token, new AuthToken(KEY).setAcl("*").setEndTime(actual).generate()); } @Test(expected = IllegalArgumentException.class) public void testMustProvideEndTimeOrWindow(){ - new AkamaiToken(KEY).setAcl("*").generate(); + new AuthToken(KEY).setAcl("*").generate(); } From 94ea0a426fa6797e977738d0cc7abed7cc4b6ad3 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 12 Feb 2017 23:47:53 +0200 Subject: [PATCH 212/592] Support nested objects in CLOUDINARY_URL. e.g. foo[bar]=100. --- .../main/java/com/cloudinary/Cloudinary.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 0dd4a02d..b849a528 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -24,7 +24,6 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Cloudinary { - public static final String AKAMAI_TOKEN_NAME = "__cld_token__"; private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UploaderStrategy", "com.cloudinary.http42.UploaderStrategy", @@ -257,7 +256,13 @@ protected Map parseConfigUrl(String cloudinaryUrl) { for (String param : cloudinaryUri.getQuery().split("&")) { String[] keyValue = param.split("="); try { - params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); + final String value = URLDecoder.decode(keyValue[1], "ASCII"); + final String key = keyValue[0]; + if(isNestedKey(key)) { + putNestedValue(params, key, value); + } else { + params.put(key, value); + } } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unexpected exception", e); } @@ -266,6 +271,25 @@ protected Map parseConfigUrl(String cloudinaryUrl) { return params; } + private void putNestedValue(Map params, String key, String value) { + String[] chain = key.split("[\\[\\]]+"); + Map outer = params; + String innerKey = chain[0]; + for (int i = 0; i < chain.length -1; i++, innerKey = chain[i]) { + Map inner = (Map) outer.get(innerKey); + if (inner == null) { + inner = new HashMap(); + outer.put(innerKey, inner); + } + outer = inner; + } + outer.put(innerKey, value); + } + + private boolean isNestedKey(String key) { + return key.matches("\\w+\\[\\w+\\]"); + } + byte[] getUTF8Bytes(String string) { try { return string.getBytes("UTF-8"); From 330f11bb83a71a7e14a5bc3eeb03184fd2632f8a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 12 Feb 2017 23:50:25 +0200 Subject: [PATCH 213/592] Add support for URL authorization token. Refactor AuthToken. Rename window to duration. Rename end time to expiration. Rename setters. --- .../main/java/com/cloudinary/AuthToken.java | 126 ++++++++++++++---- .../java/com/cloudinary/Configuration.java | 47 ++++++- .../src/main/java/com/cloudinary/Url.java | 16 ++- .../com/cloudinary/utils/ObjectUtils.java | 10 ++ .../java/com/cloudinary/AuthTokenTest.java | 86 +++++++++++- 5 files changed, 253 insertions(+), 32 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index e4291edd..0de7448b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -5,29 +5,41 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Authentication Token generator */ public class AuthToken { - public String tokenName = Cloudinary.AKAMAI_TOKEN_NAME; + public static final AuthToken NULL_AUTH_TOKEN = new AuthToken().setNull(); + public static final String AUTH_TOKEN_NAME = "__cld_token__"; + + public String tokenName = AUTH_TOKEN_NAME; public String key; public long startTime; - public long endTime; + public long expiration; public String ip; public String acl; - public long window; + public long duration; + private boolean isNullToken = false; + + public AuthToken() { + } public AuthToken(String key) { this.key = key; } - public AuthToken setTokenName(String tokenName) { + public AuthToken tokenName(String tokenName) { this.tokenName = tokenName; return this; } @@ -38,7 +50,7 @@ public AuthToken setTokenName(String tokenName) { * @param startTime in seconds since epoch * @return */ - public AuthToken setStartTime(long startTime) { + public AuthToken startTime(long startTime) { this.startTime = startTime; return this; } @@ -46,33 +58,33 @@ public AuthToken setStartTime(long startTime) { /** * Set the end time (expiration) of the token * - * @param endTime in seconds since epoch + * @param expiration in seconds since epoch * @return */ - public AuthToken setEndTime(long endTime) { - this.endTime = endTime; + public AuthToken expiration(long expiration) { + this.expiration = expiration; return this; } - public AuthToken setIp(String ip) { + public AuthToken ip(String ip) { this.ip = ip; return this; } - public AuthToken setAcl(String acl) { + public AuthToken acl(String acl) { this.acl = acl; return this; } /** * The duration of the token in seconds. This value is used to calculate the expiration of the token. - * It is ignored if endTime is provided. + * It is ignored if expiration is provided. * - * @param window + * @param duration in seconds * @return */ - public AuthToken setWindow(long window) { - this.window = window; + public AuthToken duration(long duration) { + this.duration = duration; return this; } @@ -86,13 +98,13 @@ public String generate() { } public String generate(String url) { - long expiration = endTime; + long expiration = this.expiration; if (expiration == 0) { - if (window > 0) { + if (duration > 0) { final long start = startTime > 0 ? startTime : Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L; - expiration = start + window; + expiration = start + duration; } else { - throw new IllegalArgumentException("Must provide either endTime or window"); + throw new IllegalArgumentException("Must provide either expiration or duration"); } } ArrayList tokenParts = new ArrayList(); @@ -103,19 +115,56 @@ public String generate(String url) { tokenParts.add("st=" + startTime); } tokenParts.add("exp=" + expiration); - if (url != null) { - tokenParts.add("url=" + url); - } else if (acl != null) { + if (acl != null) { tokenParts.add("acl=" + acl); - } else { - throw new IllegalArgumentException("Must provide either url or acl"); } - String auth = digest(StringUtils.join(tokenParts, "~")); + ArrayList toSign = new ArrayList(tokenParts); + if (url != null) { + + try { + toSign.add("url=" + escapeUrl(url)); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + String auth = digest(StringUtils.join(toSign, "~")); tokenParts.add("hmac=" + auth); return tokenName + "=" + StringUtils.join(tokenParts, "~"); } + /** + * Escape url using lowercase hex code + * @param url a url string + * @return escaped url + * @throws UnsupportedEncodingException see {@link URLEncoder#encode} + */ + private String escapeUrl(String url) throws UnsupportedEncodingException { + String escaped; + StringBuilder sb= new StringBuilder(URLEncoder.encode(url, "UTF-8")); + String regex= "%.."; + Pattern p = Pattern.compile(regex); // Create the pattern. + Matcher matcher = p.matcher(sb); // Create the matcher. + while (matcher.find()) { + String buf= sb.substring(matcher.start(), matcher.end()).toLowerCase(); + sb.replace(matcher.start(), matcher.end(), buf); + } + escaped = sb.toString(); + return escaped; + } + + + public AuthToken copy() { + final AuthToken authToken = new AuthToken(key); + authToken.tokenName = tokenName; + authToken.startTime = startTime; + authToken.expiration = expiration; + authToken.ip = ip; + authToken.acl = acl; + authToken.duration = duration; + return authToken; + } + private String digest(String message) { byte[] binKey = DatatypeConverter.parseHexBinary(key); try { @@ -132,5 +181,34 @@ private String digest(String message) { return null; } + private AuthToken setNull() { + isNullToken = true; + return this; + } + @Override + public boolean equals(Object o) { + if(o instanceof AuthToken) { + AuthToken other = (AuthToken) o; + return (isNullToken && other.isNullToken) || + key == null ? other.key == null : key.equals(other.key) && + tokenName.equals(other.tokenName) && + startTime == other.startTime && + expiration == other.expiration && + duration == other.duration && + (ip == null ? other.ip == null : ip.equals(other.ip)) && + (acl == null ? other.acl == null : acl.equals(other.acl)); + } else { + return false; + } + } + + @Override + public int hashCode() { + if(isNullToken) { + return 0; + } else { + return Arrays.asList(tokenName, startTime, expiration, duration, ip, acl).hashCode(); + } + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 97dffcb4..4a9d13a2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -39,6 +39,7 @@ public class Configuration { public int timeout; public boolean loadStrategies = true; public boolean clientHints = false; + public AuthToken authToken; public Configuration() { } @@ -92,6 +93,17 @@ 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); + Map tokenMap = (Map) config.get("auth_token"); + if (tokenMap != null) { + this.authToken = new AuthToken(); + this.authToken.tokenName = (String) tokenMap.get("tokenName"); + this.authToken.key = (String) tokenMap.get("key"); + this.authToken.startTime = ObjectUtils.asLong(tokenMap.get("startTime"), 0L); + this.authToken.expiration = ObjectUtils.asLong(tokenMap.get("expiration"),0L); + this.authToken.ip = (String) tokenMap.get("ip"); + this.authToken.acl = (String) tokenMap.get("acl"); + this.authToken.duration = ObjectUtils.asLong(tokenMap.get("duration"), 0L); + } } @SuppressWarnings("rawtypes") @@ -115,6 +127,7 @@ public Map asMap() { map.put("load_strategies", loadStrategies); map.put("timeout", timeout); map.put("client_hints", clientHints); + map.put("auth_token", authToken.copy()); return map; } @@ -137,6 +150,7 @@ public Configuration(Configuration other) { this.useRootPath = other.useRootPath; this.timeout = other.timeout; this.clientHints = other.clientHints; + this.authToken = other.authToken.copy(); } /** @@ -174,6 +188,7 @@ private static Configuration parseConfigUrl(String cloudinaryUrl) { builder.setPrivateCdn(!StringUtils.isEmpty(cloudinaryUri.getPath())); builder.setSecureDistribution(cloudinaryUri.getPath()); if (cloudinaryUri.getQuery() != null) { + AuthToken token = null; for (String param : cloudinaryUri.getQuery().split("&")) { String[] keyValue = param.split("="); String val = null; @@ -197,10 +212,33 @@ private static Configuration parseConfigUrl(String cloudinaryUrl) { builder.setShorten(ObjectUtils.asBoolean(val, false)); } else if (key.equals("load_strategies")) { builder.setLoadStrategies(ObjectUtils.asBoolean(val, true)); - } else { + } else if (key.matches("auth_token\\[\\w+\\]")){ + if (token == null) { + token = new AuthToken(); + } + String subKey = key.substring(key.indexOf('[') + 1, key.indexOf(']') +1); + System.out.println("sub key is " + subKey); + if (subKey.equals("tokenName")) { + token.tokenName = val; + } else if (subKey.equals("key")) { + token.key = val; + } else if (subKey.equals("startTime")) { + token.startTime = ObjectUtils.asLong(val, 0L); + } else if (subKey.equals("expiration")) { + token.expiration = ObjectUtils.asLong(val, 0L); + } else if (subKey.equals("ip")) { + token.ip = val; + } else if (subKey.equals("acl")) { + token.acl = val; + } else if (subKey.equals("duration")) { + token.duration = ObjectUtils.asLong(val, 0L); + } // Log.w("Cloudinary", "ignoring invalid parameter " + val); } } + if (token != null) { + builder.setAuthToken(token); + } } return builder.build(); } @@ -227,6 +265,7 @@ public static class Builder { private boolean loadStrategies = true; private int timeout; private boolean clientHints = false; + private AuthToken authToken; /** * Set the HTTP connection timeout. @@ -353,6 +392,11 @@ public Builder setClientHints(boolean clientHints) { return this; } + public Builder setAuthToken(AuthToken authToken) { + this.authToken = authToken; + return this; + } + /** * Initialize builder from existing {@link Configuration} * @@ -378,6 +422,7 @@ public Builder from(Configuration other) { this.loadStrategies = other.loadStrategies; this.timeout = other.timeout; this.clientHints = other.clientHints; + this.authToken = other.authToken; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 628d4aae..e4dce436 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -28,6 +28,7 @@ public class Url { String version = null; Transformation transformation = null; boolean signUrl; + private AuthToken authToken; String source = null; private String urlSuffix; private Boolean useRootPath; @@ -45,6 +46,7 @@ public class Url { public Url(Cloudinary cloudinary) { this.cloudinary = cloudinary; this.config = new Configuration(cloudinary.config); + this.authToken = config.authToken; } public Url clone() { @@ -210,6 +212,11 @@ public Url signed(boolean signUrl) { return this; } + public Url authToken(AuthToken authToken) { + this.authToken = authToken; + return this; + } + public Url sourceTransformation(Map sourceTransformation) { this.sourceTransformation = sourceTransformation; return this; @@ -346,7 +353,7 @@ public String generate(String source) { version = "v" + version; - if (signUrl) { + if (signUrl && authToken == null) { MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-1"); @@ -367,7 +374,12 @@ public String generate(String source) { String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); String prefix = unsignedDownloadUrlPrefix(source, config.cloudName, config.privateCdn, config.cdnSubdomain, config.secureCdnSubdomain, config.cname, config.secure, config.secureDistribution); - return StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + String s = "/" + StringUtils.join(new String[]{finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { + String token = authToken.generate(s); + s = s + "?" + token; + } + return prefix + s; } private String[] finalizeSource(String source, String format, String urlSuffix) { 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 f8698b4a..bcb1cd97 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -162,4 +162,14 @@ public static Integer asInteger(Object value, Integer defaultValue) { } } + public static Long asLong(Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Long) { + return (Long) value; + } else { + return Long.parseLong(value.toString()); + } + } + } diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index a49d314a..282b8d18 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -1,7 +1,10 @@ package com.cloudinary; import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import java.util.Calendar; import java.util.TimeZone; @@ -12,19 +15,35 @@ public class AuthTokenTest { public static final String KEY = "00112233FF99"; + public static final String ALT_KEY = "CCBB2233FF00"; + private Cloudinary cloudinary; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + final AuthToken authToken = new AuthToken(KEY).duration(300); + authToken.startTime(11111111); // start time is set for test purposes + cloudinary.config.authToken = authToken; + cloudinary.config.cloudName = "test123"; + + } @Test public void generateWithStartAndWindow() throws Exception { AuthToken t = new AuthToken(KEY); - t.setStartTime(1111111111).setAcl("/image/*").setWindow(300); - assertEquals("should generate an Akamai token with startTime and window", "__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af", t.generate()); + t.startTime(1111111111).acl("/image/*").duration(300); + assertEquals("should generate an Akamai token with startTime and duration", "__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af", t.generate()); } @Test public void generateWithWindow() throws Exception { long firstExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; Thread.sleep(1200); - String token = new AuthToken(KEY).setAcl("*").setWindow(300).generate(); + String token = new AuthToken(KEY).acl("*").duration(300).generate(); Thread.sleep(1200); long secondExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; Matcher m = Pattern.compile("exp=(\\d+)").matcher(token); @@ -33,13 +52,70 @@ public void generateWithWindow() throws Exception { final long actual = Long.parseLong(expString); assertThat(actual, Matchers.greaterThanOrEqualTo(firstExp)); assertThat(actual, Matchers.lessThanOrEqualTo(secondExp)); - assertEquals(token, new AuthToken(KEY).setAcl("*").setEndTime(actual).generate()); + assertEquals(token, new AuthToken(KEY).acl("*").expiration(actual).generate()); } @Test(expected = IllegalArgumentException.class) public void testMustProvideEndTimeOrWindow(){ - new AuthToken(KEY).setAcl("*").generate(); + new AuthToken(KEY).acl("*").generate(); + } + + @Test + public void testAuthenticatedUrl() { + cloudinary.config.privateCdn = true; + + 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); + + 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); + + 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); + + 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/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=7d276841d70c4ecbd0708275cd6a82e1f08e47838fbb0bceb2538e06ddfa3029", url); + + message = "should compute expiration as start time + duration"; + AuthToken token = new AuthToken(KEY).startTime(11111111).duration(300); + url = cloudinary.url().signed(true).authToken(token).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); + + } + + @Test + public void testConfiguration() { + cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&auth_token[key]=aabbcc112233&auth_token[duration]=200"); + + assertEquals(cloudinary.config.authToken.key, "aabbcc112233"); + assertEquals(cloudinary.config.authToken.duration, 200); + + } + + @Test + public void testTokenGeneration(){ + AuthToken token = new AuthToken(KEY); + token.duration = 300; + String user = "foobar"; // username taken from elsewhere + token.acl = "/*/t_" + user; + token.startTime(222222222); // we can't rely on the default "now" value in tests + String cookieToken = token.generate(); + assertEquals("__cld_token__=st=222222222~exp=222222522~acl=/*/t_foobar~hmac=eb5e2266c8ec9573f696025f075b92998080347e1c12ac39a26c94d7d712704a", cookieToken); } + @Test + 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("")); + } } \ No newline at end of file From 87fa4db5051a7aa15cc5fb8a590821fade9f57b9 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 15 Feb 2017 16:36:26 +0200 Subject: [PATCH 214/592] Refactor tests for stability --- .../com/cloudinary/test/AbstractApiTest.java | 33 ++++++++++--------- .../com/cloudinary/test/MockableTest.java | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) 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 1ea4b207..aaf98bae 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 @@ -7,6 +7,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; @@ -37,6 +38,9 @@ abstract public class AbstractApiTest extends MockableTest { public static final String API_TEST_UPLOAD_PRESET_3 = API_TEST_UPLOAD_PRESET + "3"; public static final String API_TEST_UPLOAD_PRESET_4 = API_TEST_UPLOAD_PRESET + "4"; public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, uniqueTag}; + public static final String EXPLICIT_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + ",w_100"; + public static final Transformation EXPLICIT_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX).fontFamily("Arial").fontSize(60)); + protected Api api; @BeforeClass @@ -47,7 +51,7 @@ public static void setUpClass() throws IOException { return; } Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "key=value", "eager", - Collections.singletonList(new Transformation().width(100).crop("scale"))); + Collections.singletonList(EXPLICIT_TRANSFORMATION)); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options.put("public_id", API_TEST_1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); @@ -330,7 +334,7 @@ public void test11TagsPrefix() throws Exception { public void test12Transformations() throws Exception { // should allow listing transformations Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", "c_scale,w_100"); + Map transformation = findByAttr((List) result.get("transformations"), "name", EXPLICIT_TRANSFORMATION_NAME); assertNotNull(transformation); assertTrue((Boolean) transformation.get("used")); @@ -339,22 +343,21 @@ public void test12Transformations() throws Exception { @Test public void test13TransformationMetadata() throws Exception { // should allow getting transformation metadata - final Transformation tr = new Transformation().crop("scale").width(100); - preloadResource(ObjectUtils.asMap("eager", Collections.singletonList(tr))); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.asMap("max_results", 500)); + preloadResource(ObjectUtils.asMap("eager", Collections.singletonList(EXPLICIT_TRANSFORMATION))); + Map transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("max_results", 500)); assertNotNull(transformation); - assertEquals(new Transformation((List) transformation.get("info")).generate(), tr.generate()); + assertEquals(new Transformation((List) transformation.get("info")).generate(), EXPLICIT_TRANSFORMATION.generate()); } @Test public void test14TransformationUpdate() throws Exception { // should allow updating transformation allowed_for_strict - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.updateTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation("c_scale,w_100", ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.updateTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), false); } @@ -398,8 +401,8 @@ public void test16bTransformationDelete() throws Exception { @Test public void test17aTransformationDeleteImplicit() throws Exception { // should allow deleting implicit transformation - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); - api.deleteTransformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + api.deleteTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); } @Test @@ -420,7 +423,7 @@ public void test20ResourcesContext() throws Exception { */ @Test(expected = NotFound.class) public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation("c_scale,w_100", ObjectUtils.emptyMap()); + api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); } @Test @@ -556,9 +559,7 @@ public void testGetUploadPreset() throws Exception { // should allow getting a single upload_preset String[] tags = {"a", "b", "c"}; Map context = ObjectUtils.asMap("a", "b", "c", "d"); - Transformation transformation = new Transformation(); - transformation.width(100).crop("scale"); - Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", transformation, "tags", tags, "context", + Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", EXPLICIT_TRANSFORMATION, "tags", tags, "context", context)); String name = result.get("name").toString(); Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 980d2dc5..53203926 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -13,7 +13,7 @@ public class MockableTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; - protected static final int SUFFIX = new Random().nextInt(99999); + protected static final String SUFFIX = String.valueOf(new Random().nextInt(99999)); protected static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; protected static final String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); protected Cloudinary cloudinary; From f05d4a951b28591b058bf209788937da282c27c8 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 15 Feb 2017 16:41:29 +0200 Subject: [PATCH 215/592] Add `AuthToken(Map)` constructor. Add `AuthToken.merge()`. Encode full URL path. --- .../main/java/com/cloudinary/AuthToken.java | 105 ++++++++++++---- .../main/java/com/cloudinary/Cloudinary.java | 55 +-------- .../java/com/cloudinary/Configuration.java | 115 ++++++++---------- .../src/main/java/com/cloudinary/Url.java | 42 ++++++- .../java/com/cloudinary/AuthTokenTest.java | 19 +-- 5 files changed, 183 insertions(+), 153 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 0de7448b..350fe3b0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -1,5 +1,6 @@ package com.cloudinary; +import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import javax.crypto.Mac; @@ -9,10 +10,7 @@ import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.TimeZone; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,6 +18,9 @@ * Authentication Token generator */ public class AuthToken { + /** + * A null AuthToken, which can be passed to a method to override global settings. + */ public static final AuthToken NULL_AUTH_TOKEN = new AuthToken().setNull(); public static final String AUTH_TOKEN_NAME = "__cld_token__"; @@ -39,6 +40,29 @@ public AuthToken(String key) { this.key = key; } + /** + * Create a new AuthToken configuration. + * + * @param options The following keys may be used in the options: key, startTime, expiration, ip, acl, duration. + */ + public AuthToken(Map options) { + if (options != null) { + this.tokenName = ObjectUtils.asString( options.get("tokenName"), this.tokenName); + this.key = (String) options.get("key"); + this.startTime = ObjectUtils.asLong(options.get("startTime"), 0L); + this.expiration = ObjectUtils.asLong(options.get("expiration"),0L); + this.ip = (String) options.get("ip"); + this.acl = (String) options.get("acl"); + this.duration = ObjectUtils.asLong(options.get("duration"), 0L); + } + + } + + /** + * Create a new AuthToken configuration overriding the default token name. + * @param tokenName the name of the token. must be supported by the server. + * @return this + */ public AuthToken tokenName(String tokenName) { this.tokenName = tokenName; return this; @@ -48,7 +72,7 @@ public AuthToken tokenName(String tokenName) { * Set the start time of the token. Defaults to now. * * @param startTime in seconds since epoch - * @return + * @return this */ public AuthToken startTime(long startTime) { this.startTime = startTime; @@ -59,18 +83,28 @@ public AuthToken startTime(long startTime) { * Set the end time (expiration) of the token * * @param expiration in seconds since epoch - * @return + * @return this */ public AuthToken expiration(long expiration) { this.expiration = expiration; return this; } + /** + * Set the ip of the client + * @param ip + * @return this + */ public AuthToken ip(String ip) { this.ip = ip; return this; } + /** + * Define an ACL for a cookie token + * @param acl + * @return this + */ public AuthToken acl(String acl) { this.acl = acl; return this; @@ -81,7 +115,7 @@ public AuthToken acl(String acl) { * It is ignored if expiration is provided. * * @param duration in seconds - * @return + * @return this */ public AuthToken duration(long duration) { this.duration = duration; @@ -97,6 +131,11 @@ public String generate() { return generate(null); } + /** + * Generate a URL token for the given URL. + * @param url the URL to be authorized + * @return a URL token + */ public String generate(String url) { long expiration = this.expiration; if (expiration == 0) { @@ -116,16 +155,11 @@ public String generate(String url) { } tokenParts.add("exp=" + expiration); if (acl != null) { - tokenParts.add("acl=" + acl); + tokenParts.add("acl=" + escapeToLower(acl)); } ArrayList toSign = new ArrayList(tokenParts); if (url != null) { - - try { - toSign.add("url=" + escapeUrl(url)); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + toSign.add("url=" + escapeToLower(url)); } String auth = digest(StringUtils.join(toSign, "~")); tokenParts.add("hmac=" + auth); @@ -137,11 +171,16 @@ public String generate(String url) { * Escape url using lowercase hex code * @param url a url string * @return escaped url - * @throws UnsupportedEncodingException see {@link URLEncoder#encode} */ - private String escapeUrl(String url) throws UnsupportedEncodingException { + private String escapeToLower(String url) { String escaped; - StringBuilder sb= new StringBuilder(URLEncoder.encode(url, "UTF-8")); + String encodedUrl = null; + try { + encodedUrl = URLEncoder.encode(url, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Cannot escape string.", e); + } + StringBuilder sb= new StringBuilder(encodedUrl); String regex= "%.."; Pattern p = Pattern.compile(regex); // Create the pattern. Matcher matcher = p.matcher(sb); // Create the matcher. @@ -154,6 +193,10 @@ private String escapeUrl(String url) throws UnsupportedEncodingException { } + /** + * Create a copy of this AuthToken + * @return a new AuthToken object + */ public AuthToken copy() { final AuthToken authToken = new AuthToken(key); authToken.tokenName = tokenName; @@ -165,6 +208,27 @@ public AuthToken copy() { return authToken; } + /** + * Merge this token with another, creating a new token. Other's members who are not null or 0 will override this object's members. + * @param other the token to merge from + * @return a new token + */ + public AuthToken merge(AuthToken other) { + if(other.equals(NULL_AUTH_TOKEN)) { + // NULL_AUTH_TOKEN can't merge + return other; + } + AuthToken merged = new AuthToken(); + merged.key = other.key != null ? other.key : this.key; + merged.tokenName = other.tokenName != null ? other.tokenName : this.tokenName; + merged.startTime = other.startTime != 0 ? other.startTime : this.startTime; + merged.expiration = other.expiration != 0 ? other.expiration : this.expiration; + merged.ip = other.ip != null ? other.ip : this.ip; + merged.acl = other.acl != null ? other.acl : this.acl; + merged.duration = other.duration != 0 ? other.duration : this.duration; + return merged; + } + private String digest(String message) { byte[] binKey = DatatypeConverter.parseHexBinary(key); try { @@ -174,11 +238,10 @@ private String digest(String message) { final byte[] bytes = message.getBytes(); return DatatypeConverter.printHexBinary(hmac.doFinal(bytes)).toLowerCase(); } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + throw new RuntimeException("Cannot create authorization token.", e); } catch (InvalidKeyException e) { - e.printStackTrace(); + throw new RuntimeException("Cannot create authorization token.", e); } - return null; } private AuthToken setNull() { @@ -191,7 +254,7 @@ public boolean equals(Object o) { if(o instanceof AuthToken) { AuthToken other = (AuthToken) o; return (isNullToken && other.isNullToken) || - key == null ? other.key == null : key.equals(other.key) && + (key == null ? other.key == null : key.equals(other.key)) && tokenName.equals(other.tokenName) && startTime == other.startTime && expiration == other.expiration && diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index b849a528..f896555a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -89,14 +89,14 @@ public Cloudinary(Map config) { } public Cloudinary(String cloudinaryUrl) { - this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + this.config = Configuration.from(cloudinaryUrl); loadStrategies(); } public Cloudinary() { String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); if (cloudinaryUrl != null) { - this.config = new Configuration(parseConfigUrl(cloudinaryUrl)); + this.config = Configuration.from(cloudinaryUrl); } else { this.config = new Configuration(); } @@ -239,57 +239,6 @@ private String buildUrl(String base, Map params) throws Unsuppor return urlBuilder.toString(); } - protected Map parseConfigUrl(String cloudinaryUrl) { - Map params = new HashMap(); - URI cloudinaryUri = URI.create(cloudinaryUrl); - params.put("cloud_name", cloudinaryUri.getHost()); - if (cloudinaryUri.getUserInfo() != null) { - String[] creds = cloudinaryUri.getUserInfo().split(":"); - params.put("api_key", creds[0]); - if (creds.length > 1) { - params.put("api_secret", creds[1]); - } - } - params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); - params.put("secure_distribution", cloudinaryUri.getPath()); - if (cloudinaryUri.getQuery() != null) { - for (String param : cloudinaryUri.getQuery().split("&")) { - String[] keyValue = param.split("="); - try { - final String value = URLDecoder.decode(keyValue[1], "ASCII"); - final String key = keyValue[0]; - if(isNestedKey(key)) { - putNestedValue(params, key, value); - } else { - params.put(key, value); - } - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - } - return params; - } - - private void putNestedValue(Map params, String key, String value) { - String[] chain = key.split("[\\[\\]]+"); - Map outer = params; - String innerKey = chain[0]; - for (int i = 0; i < chain.length -1; i++, innerKey = chain[i]) { - Map inner = (Map) outer.get(innerKey); - if (inner == null) { - inner = new HashMap(); - outer.put(innerKey, inner); - } - outer = inner; - } - outer.put(innerKey, value); - } - - private boolean isNestedKey(String key) { - return key.matches("\\w+\\[\\w+\\]"); - } - byte[] getUTF8Bytes(String string) { try { return string.getBytes("UTF-8"); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 4a9d13a2..f6d21206 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -95,14 +95,7 @@ public void update(Map config) { this.clientHints = ObjectUtils.asBoolean(config.get("client_hints"), false); Map tokenMap = (Map) config.get("auth_token"); if (tokenMap != null) { - this.authToken = new AuthToken(); - this.authToken.tokenName = (String) tokenMap.get("tokenName"); - this.authToken.key = (String) tokenMap.get("key"); - this.authToken.startTime = ObjectUtils.asLong(tokenMap.get("startTime"), 0L); - this.authToken.expiration = ObjectUtils.asLong(tokenMap.get("expiration"),0L); - this.authToken.ip = (String) tokenMap.get("ip"); - this.authToken.acl = (String) tokenMap.get("acl"); - this.authToken.duration = ObjectUtils.asLong(tokenMap.get("duration"), 0L); + this.authToken = new AuthToken(tokenMap); } } @@ -127,7 +120,9 @@ public Map asMap() { map.put("load_strategies", loadStrategies); map.put("timeout", timeout); map.put("client_hints", clientHints); - map.put("auth_token", authToken.copy()); + if (authToken != null) { + map.put("auth_token", authToken.copy()); + } return map; } @@ -150,7 +145,9 @@ public Configuration(Configuration other) { this.useRootPath = other.useRootPath; this.timeout = other.timeout; this.clientHints = other.clientHints; - this.authToken = other.authToken.copy(); + if (other.authToken != null) { + this.authToken = other.authToken.copy(); + } } /** @@ -172,75 +169,65 @@ public static Configuration from(Configuration other) { * @return a new configuration with the arguments supplied by the url */ public static Configuration from(String cloudinaryUrl) { - return from(parseConfigUrl(cloudinaryUrl)); + Configuration config = new Configuration(); + config.update(parseConfigUrl(cloudinaryUrl)); + return config; } - private static Configuration parseConfigUrl(String cloudinaryUrl) { - Builder builder = new Builder(); + static protected Map parseConfigUrl(String cloudinaryUrl) { + Map params = new HashMap(); URI cloudinaryUri = URI.create(cloudinaryUrl); - builder.setCloudName(cloudinaryUri.getHost()); + params.put("cloud_name", cloudinaryUri.getHost()); if (cloudinaryUri.getUserInfo() != null) { String[] creds = cloudinaryUri.getUserInfo().split(":"); - builder.setApiKey(creds[0]); - builder.setApiSecret(creds[1]); + params.put("api_key", creds[0]); + if (creds.length > 1) { + params.put("api_secret", creds[1]); + } } - builder.setPrivateCdn(!StringUtils.isEmpty(cloudinaryUri.getPath())); - builder.setSecureDistribution(cloudinaryUri.getPath()); + params.put("private_cdn", !StringUtils.isEmpty(cloudinaryUri.getPath())); + params.put("secure_distribution", cloudinaryUri.getPath()); + updateMapfromURI(params, cloudinaryUri); + return params; + } + + static private void updateMapfromURI(Map params, URI cloudinaryUri) { if (cloudinaryUri.getQuery() != null) { - AuthToken token = null; for (String param : cloudinaryUri.getQuery().split("&")) { String[] keyValue = param.split("="); - String val = null; try { -// params.put(keyValue[0], URLDecoder.decode(keyValue[1], "ASCII")); - val = URLDecoder.decode(keyValue[1], "ASCII"); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Error decoding cloudinaryUrl", e); - } - - String key = keyValue[0]; - if (key.equals("cname")) { - builder.setCname(val); - } else if (key.equals("upload_prefix")) { - builder.setUploadPrefix(val); - } else if (key.equals("secure")) { - builder.setSecure(ObjectUtils.asBoolean(val, false)); - } else if (key.equals("cdn_subdomain")) { - builder.setCdnSubdomain(ObjectUtils.asBoolean(val, false)); - } else if (key.equals("shorten")) { - builder.setShorten(ObjectUtils.asBoolean(val, false)); - } else if (key.equals("load_strategies")) { - builder.setLoadStrategies(ObjectUtils.asBoolean(val, true)); - } else if (key.matches("auth_token\\[\\w+\\]")){ - if (token == null) { - token = new AuthToken(); + final String value = URLDecoder.decode(keyValue[1], "ASCII"); + final String key = keyValue[0]; + if(isNestedKey(key)) { + putNestedValue(params, key, value); + } else { + params.put(key, value); } - String subKey = key.substring(key.indexOf('[') + 1, key.indexOf(']') +1); - System.out.println("sub key is " + subKey); - if (subKey.equals("tokenName")) { - token.tokenName = val; - } else if (subKey.equals("key")) { - token.key = val; - } else if (subKey.equals("startTime")) { - token.startTime = ObjectUtils.asLong(val, 0L); - } else if (subKey.equals("expiration")) { - token.expiration = ObjectUtils.asLong(val, 0L); - } else if (subKey.equals("ip")) { - token.ip = val; - } else if (subKey.equals("acl")) { - token.acl = val; - } else if (subKey.equals("duration")) { - token.duration = ObjectUtils.asLong(val, 0L); - } -// Log.w("Cloudinary", "ignoring invalid parameter " + val); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); } } - if (token != null) { - builder.setAuthToken(token); + } + } + + static private void putNestedValue(Map params, String key, String value) { + String[] chain = key.split("[\\[\\]]+"); + Map outer = params; + String innerKey = chain[0]; + for (int i = 0; i < chain.length -1; i++, innerKey = chain[i]) { + Map inner = (Map) outer.get(innerKey); + if (inner == null) { + inner = new HashMap(); + outer.put(innerKey, inner); } + outer = inner; } - return builder.build(); + outer.put(innerKey, value); + } + + static private boolean isNestedKey(String key) { + return key.matches("\\w+\\[\\w+\\]"); } /** @@ -422,7 +409,7 @@ public Builder from(Configuration other) { this.loadStrategies = other.loadStrategies; this.timeout = other.timeout; this.clientHints = other.clientHints; - this.authToken = other.authToken; + this.authToken = other.authToken == null ? null : other.authToken.copy(); return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index e4dce436..10c77aff 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -1,6 +1,8 @@ package com.cloudinary; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -212,8 +214,30 @@ public Url signed(boolean signUrl) { return this; } + /** + * Set the authorization token. If authToken has already been set the parameter is merged with the current value unless the parameter value is null or NULL_AUTH_TOKEN.

+ * For example, to generate an authorized URL with a different duration:
+ *
+     *  {@code
+     *   cloudinary.config.authToken = new AuthToken(KEY).duration(500);
+     *   // later...
+     *   cloudinary.url().signed(true).authToken(new AuthToken().duration(300))
+     *                   .type("authenticated").version("1486020273").generate("sample.jpg");
+     *  }
+     *
+ * @param authToken an authorization token object + * @return this + * + * + */ public Url authToken(AuthToken authToken) { - this.authToken = authToken; + if (this.authToken == null) { + this.authToken = authToken; + } else if(authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { + this.authToken = authToken; + } else { + this.authToken = this.authToken.merge(authToken); + } return this; } @@ -353,7 +377,7 @@ public String generate(String source) { version = "v" + version; - if (signUrl && authToken == null) { + if (signUrl && (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN))) { MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-1"); @@ -374,12 +398,18 @@ public String generate(String source) { String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); String prefix = unsignedDownloadUrlPrefix(source, config.cloudName, config.privateCdn, config.cdnSubdomain, config.secureCdnSubdomain, config.cname, config.secure, config.secureDistribution); - String s = "/" + StringUtils.join(new String[]{finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + String url = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { - String token = authToken.generate(s); - s = s + "?" + token; + try { + URL tempUrl = new URL(url); + String path = tempUrl.getPath(); + String token = authToken.generate(path); + url = url + "?" + token; + } catch (MalformedURLException ignored) { + } } - return prefix + s; + return url; } private String[] finalizeSource(String source, String format, String urlSuffix) { diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 282b8d18..27e29684 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import org.junit.rules.TestName; +import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.TimeZone; import java.util.regex.Matcher; @@ -33,14 +34,14 @@ public void setUp() { } @Test - public void generateWithStartAndWindow() throws Exception { + public void generateWithStartAndDuration() throws Exception { AuthToken t = new AuthToken(KEY); t.startTime(1111111111).acl("/image/*").duration(300); - assertEquals("should generate an Akamai token with startTime and duration", "__cld_token__=st=1111111111~exp=1111111411~acl=/image/*~hmac=0854e8b6b6a46471a80b2dc28c69bd352d977a67d031755cc6f3486c121b43af", t.generate()); + assertEquals("should generate an authorization token with startTime and duration", "__cld_token__=st=1111111111~exp=1111111411~acl=%2fimage%2f*~hmac=1751370bcc6cfe9e03f30dd1a9722ba0f2cdca283fa3e6df3342a00a7528cc51", t.generate()); } @Test - public void generateWithWindow() throws Exception { + public void generateWithDuration() throws Exception { long firstExp = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis() / 1000L + 300; Thread.sleep(1200); String token = new AuthToken(KEY).acl("*").duration(300).generate(); @@ -56,7 +57,7 @@ public void generateWithWindow() throws Exception { } @Test(expected = IllegalArgumentException.class) - public void testMustProvideEndTimeOrWindow(){ + public void testMustProvideExpirationOrDuration(){ new AuthToken(KEY).acl("*").generate(); } @@ -78,15 +79,15 @@ public void testAuthenticatedUrl() { 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/v1486020273/sample.jpg", url); + assertEquals(message,"http://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=7d276841d70c4ecbd0708275cd6a82e1f08e47838fbb0bceb2538e06ddfa3029", url); message = "should compute expiration as start time + duration"; - AuthToken token = new AuthToken(KEY).startTime(11111111).duration(300); - url = cloudinary.url().signed(true).authToken(token).resourceType("image").type("authenticated").version("1486020273").generate("sample.jpg"); + 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); } @@ -108,14 +109,14 @@ public void testTokenGeneration(){ token.acl = "/*/t_" + user; token.startTime(222222222); // we can't rely on the default "now" value in tests String cookieToken = token.generate(); - assertEquals("__cld_token__=st=222222222~exp=222222522~acl=/*/t_foobar~hmac=eb5e2266c8ec9573f696025f075b92998080347e1c12ac39a26c94d7d712704a", cookieToken); + assertEquals("__cld_token__=st=222222222~exp=222222522~acl=%2f*%2ft_foobar~hmac=8e39600cc18cec339b21fe2b05fcb64b98de373355f8ce732c35710d8b10259f", cookieToken); } @Test 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("")); } } \ No newline at end of file From acb8599339d7c892e571ce06e53cb2b12d7c2198 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 22 Feb 2017 12:47:35 +0200 Subject: [PATCH 216/592] Add maven items to gitignore --- .gitignore | 14 ++++++++++++++ .../src/main/java/com/cloudinary/AuthToken.java | 16 ++++++++-------- .../test/java/com/cloudinary/AuthTokenTest.java | 9 +++------ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 40676307..daa54e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,17 @@ appengine-web.xml cloudinary-android-test/src/main/AndroidManifest.xml +# Maven + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 350fe3b0..73194e32 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -22,15 +22,15 @@ public class AuthToken { * A null AuthToken, which can be passed to a method to override global settings. */ public static final AuthToken NULL_AUTH_TOKEN = new AuthToken().setNull(); - public static final String AUTH_TOKEN_NAME = "__cld_token__"; + private static final String AUTH_TOKEN_NAME = "__cld_token__"; - public String tokenName = AUTH_TOKEN_NAME; - public String key; - public long startTime; - public long expiration; - public String ip; - public String acl; - public long duration; + private String tokenName = AUTH_TOKEN_NAME; + private String key; + private long startTime; + private long expiration; + private String ip; + private String acl; + private long duration; private boolean isNullToken = false; public AuthToken() { diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 27e29684..b103f750 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -95,18 +95,15 @@ public void testAuthenticatedUrl() { @Test public void testConfiguration() { cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&auth_token[key]=aabbcc112233&auth_token[duration]=200"); - - assertEquals(cloudinary.config.authToken.key, "aabbcc112233"); - assertEquals(cloudinary.config.authToken.duration, 200); - + assertEquals(cloudinary.config.authToken, new AuthToken("aabbcc112233").duration(200)); } @Test public void testTokenGeneration(){ AuthToken token = new AuthToken(KEY); - token.duration = 300; + token.duration(300); String user = "foobar"; // username taken from elsewhere - token.acl = "/*/t_" + user; + token.acl("/*/t_" + user); token.startTime(222222222); // we can't rely on the default "now" value in tests String cookieToken = token.generate(); assertEquals("__cld_token__=st=222222222~exp=222222522~acl=%2f*%2ft_foobar~hmac=8e39600cc18cec339b21fe2b05fcb64b98de373355f8ce732c35710d8b10259f", cookieToken); From 26d0c74fd935ac8eb212810195b12a3209a9f815 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 22 Feb 2017 23:38:10 +0200 Subject: [PATCH 217/592] Version 1.8.1 --- CHANGELOG.md | 9 +++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef520724..69854c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ +1.8.1 / 2017-02-22 +================== + + * Add support for URL authorization token. + * Refactor AuthToken. + * Refactor tests for stability + * Support nested objects in CLOUDINARY_URL. e.g. foo[bar]=100. + * Add maven items to `.gitignore`. + 1.8.0 / 2017-02-08 ================== diff --git a/README.md b/README.md index 1dc81fde..99e9bd1b 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.8.0 + 1.8.1 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.8.0/cloudinary-core-1.8.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.8.0/cloudinary-http44-1.8.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.8.1/cloudinary-core-1.8.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.8.1/cloudinary-http44-1.8.1.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index f896555a..a1b7cd1b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.8.0"; + public final static String VERSION = "1.8.1"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 4747dea4bdd2b0513c5b05cff66a34224b2cfdf1 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 23 Feb 2017 02:12:16 +0200 Subject: [PATCH 218/592] [maven-release-plugin] prepare release 1.8.1 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index df7be85f..7bda6164 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 com.cloudinary cloudinary-android-test - 1.8.1-SNAPSHOT + 1.8.1 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.8.1-SNAPSHOT + 1.8.1
com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 3450f2ac..1b826011 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 2910eef9..f0492efb 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 94da6653..b878598f 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 74ca6e2b..39932049 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index e48ccf33..2d527b36 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 4392bcfb..098e10b9 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index efea90e4..383b4c6f 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 140a70a2..98175277 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.8.1-SNAPSHOT + 1.8.1 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.8.1 From 1aed9be90ada55c5e4bcf7a791c3bb85649c45dd Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 23 Feb 2017 02:12:24 +0200 Subject: [PATCH 219/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 7bda6164..018b0d53 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT com.cloudinary cloudinary-android-test - 1.8.1 + 1.8.2-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.8.1 + 1.8.2-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 1b826011..9a7fa6e3 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index f0492efb..1064cbbc 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index b878598f..05d85c7b 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 39932049..011f79f7 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 2d527b36..dc1800af 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 098e10b9..e754429f 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 383b4c6f..5cc17e69 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 98175277..0406f082 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.8.1 + 1.8.2-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.8.1 + HEAD From 480213d697e261b0bc7e7bc91b9573ef6bfef954 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 22 Feb 2017 16:03:39 +0200 Subject: [PATCH 220/592] Support user defined variables and expressions. * Refactor `Condition` and create the `BaseExpression` and `Expression` classes. * Parse parameters that accept expressions --- .../java/com/cloudinary/Transformation.java | 109 ++++++-- .../transformation/BaseExpression.java | 242 ++++++++++++++++++ .../cloudinary/transformation/Condition.java | 106 ++------ .../cloudinary/transformation/Expression.java | 99 +++++++ .../cloudinary/transformation/TextLayer.java | 17 +- .../com/cloudinary/TransformationTest.java | 49 +++- 6 files changed, 510 insertions(+), 112 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/Expression.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index a9598924..82da2681 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -6,6 +6,7 @@ import com.cloudinary.transformation.AbstractLayer; import com.cloudinary.transformation.Condition; +import com.cloudinary.transformation.Expression; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -365,6 +366,25 @@ public Transformation ifCondition(String condition) { return param("if", condition); } + + /** + * Define a conditional transformation + * @param expression a condition + * @return the transformation for chaining + */ + public Transformation ifCondition(Expression expression) { + return ifCondition(expression.toString()); + } + + /** + * Define a conditional transformation + * @param condition a condition + * @return the transformation for chaining + */ + public Transformation ifCondition(Condition condition) { + return ifCondition(condition.toString()); + } + public Transformation ifElse() { chain(); return param("if", "else"); @@ -530,24 +550,32 @@ public String generate(Map options) { String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); SortedMap params = new TreeMap(); - params.put("a", angle); + + params.put("a", Expression.normalize(angle)); + params.put("ar", Expression.normalize( options.get("aspect_ratio"))); params.put("b", background); params.put("c", crop); params.put("co", color); - params.put("dpr", dpr); + params.put("dpr", Expression.normalize(dpr)); params.put("du", duration); params.put("eo", endOffset); params.put("fl", flags); - params.put("h", height); + params.put("h", Expression.normalize(height)); + params.put("o", Expression.normalize( options.get("opacity"))); + params.put("q", Expression.normalize( options.get("quality"))); + params.put("q", Expression.normalize( options.get("quality"))); + params.put("r", Expression.normalize( options.get("radius"))); params.put("so", startOffset); params.put("t", namedTransformation); params.put("vc", videoCodec); - params.put("w", width); + params.put("w", Expression.normalize(width)); + params.put("x", Expression.normalize( options.get("x"))); + params.put("y", Expression.normalize( options.get("y"))); + params.put("z", Expression.normalize( options.get("zoom"))); String[] simple_params = new String[]{ "ac", "audio_codec", "af", "audio_frequency", - "ar", "aspect_ratio", "bo", "border", "br", "bit_rate", "cs", "color_space", @@ -558,21 +586,39 @@ public String generate(Map options) { "f", "fetch_format", "g", "gravity", "l", "overlay", - "o", "opacity", "p", "prefix", "pg", "page", - "q", "quality", - "r", "radius", "u", "underlay", - "vs", "video_sampling", - "x", "x", - "y", "y", - "z", "zoom"}; + "vs", "video_sampling" + }; for (int i = 0; i < simple_params.length; i += 2) { params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); } List components = new ArrayList(); + + String ifValue = (String) options.get("if"); + if(ifValue != null){ + components.add(0, "if_" + Expression.normalize(ifValue)); + } + + List varParams = new ArrayList(); + for( Object k: options.keySet()) { + String key = (String) k; + if(key.matches("^\\$[a-zA-Z0-9]+$")) { + varParams.add(key + "_" + ObjectUtils.asString(options.get(k))); + } + } + + String variables = processVar((Expression[]) options.get("variables")); + if (variables != null) { + varParams.add(variables); + } + + if (varParams != null && !varParams.isEmpty()) { + components.add(StringUtils.join(varParams, ",")); + } + for (Map.Entry param : params.entrySet()) { if (StringUtils.isNotBlank(param.getValue())) { components.add(param.getKey() + "_" + param.getValue()); @@ -582,14 +628,9 @@ public String generate(Map options) { if (raw_transformation != null) { components.add(raw_transformation); } - - String ifValue = (String) options.get("if"); - if(ifValue != null){ - components.add(0, "if_" + new Condition(ifValue).toString()); - } - if (!components.isEmpty()) { - transformations.add(StringUtils.join(components, ",")); + final String joined = StringUtils.join(components, ","); + transformations.add(Expression.normalize(joined)); } if (isResponsive) { @@ -607,6 +648,17 @@ public String generate(Map options) { return StringUtils.join(transformations, "/"); } + private String processVar(Expression[] variables) { + if(variables == null) { + return null; + } + List s = new ArrayList(variables.length); + for(Expression variable: variables) { + s.add(variable.toString()); + } + return StringUtils.join(s, ","); + } + /** * Check if the value is a float >= 1 * @param value @@ -710,4 +762,23 @@ private static String processVideoCodecParam(Object param) { return outParam.toString(); } + /** + * Add a variable assignment. Each call to this method will add a new variable assignments, but the order of the assignments may change. To enforce a particular order, use {@link #variables(Expression...)} + * @param name the name of the variable + * @param value the value to assign to the variable + * @return this for chaining + */ + public Transformation variable(String name, Object value) { + return param(name, value); + } + + /** + * Add a sequence of variable assignments. The order of the assignments will be honored. + * @param variables variable expressions + * @return this for chaining + */ + public Transformation variables(Expression...variables) { + return param("variables", variables); + } + } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java new file mode 100644 index 00000000..366f13bc --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -0,0 +1,242 @@ +package com.cloudinary.transformation; + +import com.cloudinary.Transformation; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Defines an expression used in transformation parameter values + * @param Children must define themselves as T + */ +public abstract class BaseExpression { + public static final Map OPERATORS = ObjectUtils.asMap( + "=", "eq", + "!=", "ne", + "<", "lt", + ">", "gt", + "<=", "lte", + ">=", "gte", + "&&", "and", + "||", "or", + "*", "mul", + "/", "div", + "+", "add", + "-", "sub" + ); + public static final Map PREDEFINED_VARS = ObjectUtils.asMap( + "width", "w", + "height", "h", + "initialWidth", "iw", + "initialHeight", "ih", + "aspect_ratio", "ar", + "initial_aspect_ratio", "iar", + "aspectRatio", "ar", + "initialAspectRatio", "iar", + "page_count", "pc", + "pageCount", "pc", + "face_count", "fc", + "faceCount", "fc", + "current_page", "cp", + "currentPage", "cp", + "tags", "tags", + "pageX", "px", + "pageY", "py" + + ); + private static final String PATTERN = getpattern(); + + protected List expressions = null; + protected Transformation parent = null; + + protected BaseExpression() { + expressions = new ArrayList(); + } + + /** + * Normalize an expression string, replace "nice names" with their coded values and spaces with "_". + * @param expresion an expression + * @return a parsed expression + */ + public static String normalize(Object expresion) { + + String replacement; + if (expresion == null) { + return null; + } + String conditionStr = String.valueOf(expresion); + conditionStr = conditionStr.replaceAll("[ _]+", "_"); + Pattern replaceRE = Pattern.compile(PATTERN); + Matcher matcher = replaceRE.matcher(conditionStr); + StringBuffer result = new StringBuffer(conditionStr.length()); + while (matcher.find()) { + if (OPERATORS.containsKey(matcher.group())) { + replacement = (String) OPERATORS.get(matcher.group()); + } else if (PREDEFINED_VARS.containsKey(matcher.group())) { + replacement = (String) PREDEFINED_VARS.get(matcher.group()); + } else { + replacement = matcher.group(); + } + matcher.appendReplacement(result, replacement); + } + matcher.appendTail(result); + return result.toString(); + } + + /** + * @return a regex pattern for operators and predefined vars + */ + private static String getpattern() { + String pattern; + final ArrayList operators = new ArrayList(OPERATORS.keySet()); + Collections.sort(operators, Collections.reverseOrder()); + StringBuffer sb = new StringBuffer(); + for(String op: operators) { + sb.append("|").append(Pattern.quote(op)); + } + pattern = "(" + StringUtils.join(PREDEFINED_VARS.keySet(), "|") + sb.toString() + ")"; + return pattern; + } + + public Transformation getParent() { + return parent; + } + + public T setParent(Transformation parent) { + this.parent = parent; + return (T) this; + } + + public String serialize() { + return StringUtils.join(expressions, "_"); + } + + @Override + public String toString() { + return serialize(); + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public T clone() { + T newCondition = newInstance(); + newCondition.expressions.addAll(expressions); + newCondition.parent = parent; + return newCondition; + } + + public T multiple(Object value) { + expressions.add("mul"); + expressions.add(value.toString()); + return (T) this; + } + + abstract protected T newInstance(); + + public T gt(Object value) { + return (T) this.gt().value(value); + } + + public T gt() { + expressions.add("gt"); + return (T) this; + } + + public T and(Object value) { + return (T) and().value(value); + } + + public T and() { + expressions.add("and"); + return (T) this; + } + + public T or(Object value) { + return (T) or().value(value); + } + + public T or() { + expressions.add("or"); + return (T) this; + } + + public T eq(Object value) { + return (T) eq().value(value); + } + + public T eq() { + expressions.add("eq"); + return (T) this; + } + + public T ne(Object value) { + return (T) ne().value(value); + } + + public T ne() { + expressions.add("ne"); + return (T) this; + } + + public T lt(Object value) { + return (T) lt().value(value); + } + + public T lt() { + expressions.add("lt"); + return (T) this; + } + + public T lte(Object value) { + return (T) lte().value(value); + } + + public T lte() { + expressions.add("lte"); + return (T) this; + } + + public T gte(Object value) { + return (T) gte().value(value); + } + + public T gte() { + expressions.add("gte"); + return (T) this; + } + + public T div(Object value) { + return (T) div().value(value); + } + + public T div() { + expressions.add("div"); + return (T) this; + } + + public T add(Object value) { + return (T) add().value(value); + } + + public T add() { + expressions.add("add"); + return (T) this; + } + + public T sub(Object value) { + return (T) sub().value(value); + } + + public T sub() { + expressions.add("sub"); + return (T) this; + } + + public T value(Object value) { + expressions.add(String.valueOf(value)); + return (T) this; + } +} 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 bb28eab5..c53762a6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -1,110 +1,40 @@ package com.cloudinary.transformation; import com.cloudinary.Transformation; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Represents a condition for {@link Transformation#ifCondition()} */ -public class Condition { - public static final Map OPERATORS = ObjectUtils.asMap( - "=", "eq", - "!=", "ne", - "<", "lt", - ">", "gt", - "<=", "lte", - ">=", "gte", - "&&", "and", - "||", "or"); - - public static final Map PARAMETERS = ObjectUtils.asMap( - "width", "w", - "height", "h", - "aspect_ratio", "ar", - "aspectRatio", "ar", - "page_count", "pc", - "pageCount", "pc", - "face_count", "fc", - "faceCount", "fc" - ); - - protected List predicateList = null; - private Transformation parent = null; +public class Condition extends BaseExpression { public Condition() { - predicateList = new ArrayList(); + super(); } /** * Create a Condition Object. The conditionStr string will be translated to a serialized condition. - * + *
+ * For example, new Condition("fc > 3") * @param conditionStr condition in string format */ public Condition(String conditionStr) { this(); if (conditionStr != null) { - predicateList.add(literal(conditionStr)); - } - } - - private String literal(String conditionStr) { - - String replacement; - conditionStr = conditionStr.replaceAll("[ _]+", "_"); - Pattern replaceRE = Pattern.compile("(" + StringUtils.join(PARAMETERS.keySet(), "|") + "|[=<>&|!]+)"); - Matcher matcher = replaceRE.matcher(conditionStr); - StringBuffer result = new StringBuffer(conditionStr.length()); - while (matcher.find()) { - if (OPERATORS.containsKey(matcher.group())) { - replacement = (String) OPERATORS.get(matcher.group()); - } else if (PARAMETERS.containsKey(matcher.group())) { - replacement = (String) PARAMETERS.get(matcher.group()); - } else { - replacement = matcher.group(); - } - matcher.appendReplacement(result, replacement); + expressions.add(normalize(conditionStr)); } - matcher.appendTail(result); - return result.toString(); } - public Transformation getParent() { return parent;} - - public Condition setParent(Transformation parent) { - this.parent = parent; - return this; - } - - public String serialize() { return StringUtils.join(predicateList, "_");} - @Override - public String toString() { - return serialize(); + protected Condition newInstance() { + return new Condition(); } - protected Condition predicate(String name, String operator, String value) { + protected Condition predicate(String name, String operator, Object value) { if (OPERATORS.containsKey(operator)) { operator = (String) OPERATORS.get(operator); } - predicateList.add(String.format("%s_%s_%s", name, operator, value)); - return this; - } - - public Condition and() { - predicateList.add("and"); - return this; - } - - public Condition or() { - predicateList.add("or"); + expressions.add(String.format("%s_%s_%s", name, operator, value)); return this; } @@ -118,41 +48,39 @@ public Transformation then() { } public Condition width(String operator, Object value) { - predicateList.add("w_" + operator + "_" + value); - return this; + return predicate("w", operator, value); } public Condition height(String operator, Object value) { - predicateList.add("h_" + operator + "_" + value); - return this; + return predicate("h", operator, value); } public Condition aspectRatio(String operator, Object value) { - predicateList.add("ar_" + operator + "_" + value); - return this; + return predicate("ar", 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) { - predicateList.add("fc_" + operator + "_" + value); - return this; + 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) { - predicateList.add("pc_" + operator + "_" + value); - return this; + return predicate("pc", operator, value); } + } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/Expression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/Expression.java new file mode 100644 index 00000000..d8e2558f --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Expression.java @@ -0,0 +1,99 @@ +package com.cloudinary.transformation; + +/** + * Represents a transformation parameter expression. + */ +public class Expression extends BaseExpression { + + private boolean predefined = false; + + public Expression(){ + super(); + } + + public Expression(String name){ + super(); + expressions.add(name); + } + + public static Expression variable(String name, Object value){ + Expression var = new Expression(name); + var.expressions.add(value.toString()); + return var; + } + + public static Expression faceCount() { + return new Expression("fc"); + } + + @Override + protected Expression newInstance() { + return new Expression(); + } + /* + * @returns a new expression with the predefined variable "width" + */ + public static Expression width() { + return new Expression("width"); + } + /* + * @returns a new expression with the predefined variable "height" + */ + public static Expression height() { + return new Expression("height"); + } + /* + * @returns a new expression with the predefined variable "initialWidth" + */ + public static Expression initialWidth() { + return new Expression("initialWidth"); + } + /* + * @returns a new expression with the predefined variable "initialHeight" + */ + public static Expression initialHeight() { + return new Expression("initialHeight"); + } + /* + * @returns a new expression with the predefined variable "aspectRatio" + */ + public static Expression aspectRatio() { + return new Expression("aspectRatio"); + } + /* + * @returns a new expression with the predefined variable "initialAspectRatio" + */ + public static Expression initialAspectRatio() { + return new Expression("initialAspectRatio"); + } + /* + * @returns a new expression with the predefined variable "pageCount" + */ + public static Expression pageCount() { + return new Expression("pageCount"); + } + /* + * @returns a new expression with the predefined variable "currentPage" + */ + public static Expression currentPage() { + return new Expression("currentPage"); + } + /* + * @returns a new expression with the predefined variable "tags" + */ + public static Expression tags() { + return new Expression("tags"); + } + /* + * @returns a new expression with the predefined variable "pageX" + */ + public static Expression pageX() { + return new Expression("pageX"); + } + /* + * @returns a new expression with the predefined variable "pageY" + */ + public static Expression pageY() { + return new Expression("pageY"); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index fb4037aa..6337c266 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -1,6 +1,8 @@ package com.cloudinary.transformation; import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.cloudinary.SmartUrlEncoder; import com.cloudinary.utils.StringUtils; @@ -81,7 +83,20 @@ public TextLayer lineSpacing(Integer lineSpacing) { } public TextLayer text(String text) { - this.text = SmartUrlEncoder.encode(text).replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); + String part; + StringBuffer result = new StringBuffer(); + // Don't encode interpolation expressions e.g. $(variable) + Matcher m = Pattern.compile("\\$\\([a-zA-Z]\\w+\\)").matcher(text); + int start = 0; + while (m.find()) { + part = text.substring(start, m.start()); + part = SmartUrlEncoder.encode(part); + result.append(part); // append encoded pre-match + result.append(m.group()); // append match + start = m.end(); + } + result.append(SmartUrlEncoder.encode(text.substring(start))); + this.text = result.toString().replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); return getThis(); } diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 506891c5..8a2212eb 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -1,15 +1,17 @@ package com.cloudinary; +import com.cloudinary.transformation.Condition; +import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; import org.cloudinary.json.JSONArray; import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; +import static com.cloudinary.transformation.Expression.faceCount; +import static com.cloudinary.transformation.Expression.variable; import static org.junit.Assert.*; /** @@ -140,5 +142,46 @@ public void endIf2() throws Exception { assertEquals("force the if_else clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_else/c_crop,w_100/if_end", transformation.toString()); } + + @Test + public void testArrayShouldDefineASetOfVariables() { + // using methods + Transformation t = new Transformation(); + t.ifCondition("face_count > 2") + .variables(variable("$z", 5), variable("$foo", "$z * 2")) + .crop("scale") + .width("$foo * 200"); + assertEquals("if_fc_gt_2,$z_5,$foo_$z_mul_2,c_scale,w_$foo_mul_200", t.generate()); + } + + @Test + public void testVariable(){ + // using strings + Transformation t = new Transformation(); + t.variable("$foo", 10) + .chain() + .ifCondition(faceCount().gt(2)) + .crop("scale") + .width(new Condition("$foo * 200 / faceCount")) + .endIf(); + assertEquals("$foo_10/if_fc_gt_2/c_scale,w_$foo_mul_200_div_fc/if_end", t.generate()); + } + + @Test + public void testShouldSupportTextValues() { + Transformation t = new Transformation(); + t.effect("$efname", 100) + .variable("$efname", "!blur!"); + assertEquals("$efname_!blur!,e_$efname:100", t.generate()); + } + @Test + public void testSupportStringInterpolation() { + Transformation t = new Transformation() + .crop("scale") + .overlay(new TextLayer().text( + "$(start)Hello $(name)$(ext), $(no ) $( no)$(end)" + ).fontFamily("Arial").fontSize(18)); + assertEquals("c_scale,l_text:Arial_18:$(start)Hello%20$(name)$(ext)%E2%80%9A%20%24%28no%20%29%20%24%28%20no%29$(end)", t.generate()); + } } \ No newline at end of file From 5b0cc2a284466b7c06d54bf476bb6828a9eab34f Mon Sep 17 00:00:00 2001 From: Richard Gieg Date: Wed, 8 Mar 2017 03:48:28 -0800 Subject: [PATCH 221/592] Pass moderation param in explicit call (#59) * Pass moderation param in explicit call * Add test for moderation param in explicit call --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 1 + .../src/main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 15a8d614..ae09ca6c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -192,6 +192,7 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("headers", Util.buildCustomHeaders(options.get("headers"))); params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); + params.put("moderation", (String) options.get("moderation")); if (options.get("face_coordinates") != null) { params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); } 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 2fb82f67..4e63e962 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 @@ -155,7 +155,7 @@ public void testUniqueFilename() throws Exception { @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload")); + Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload", "moderation", "manual")); String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg").version(result.get("version")).generate("sample"); String eagerUrl = (String) ((Map) ((List) result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; From ab6ab3afc0c434964afc642e44a90c6c714bab18 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Mar 2017 13:50:14 +0200 Subject: [PATCH 222/592] Add `expired_at` to private download. (#60) --- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 1 + .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index a1b7cd1b..c17feb0a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -172,6 +172,7 @@ public String privateDownload(String publicId, String format, MapsingletonMap("expires_at", inTwentyMinutes)); URI uri = new URI(url); Map parameters = getUrlParameters(uri); assertEquals("imgÿ=&é", parameters.get("public_id")); assertEquals("jpg", parameters.get("format")); assertEquals("a", parameters.get("api_key")); + assertEquals(String.valueOf(inTwentyMinutes), parameters.get("expires_at")); assertEquals("/v1_1/test123/image/download", uri.getPath()); } From c8d370b64225796a40a38b4909d4a1eab62aebd8 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Mar 2017 13:55:41 +0200 Subject: [PATCH 223/592] Fix encoding error in api update resource (#61) --- .../src/main/java/com/cloudinary/http43/ApiStrategy.java | 3 ++- .../src/main/java/com/cloudinary/http44/ApiStrategy.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index f083c354..63979ccb 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -9,6 +9,7 @@ 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; @@ -159,7 +160,7 @@ private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map Date: Wed, 8 Mar 2017 17:27:51 +0530 Subject: [PATCH 224/592] Fix `OutOfMemoryError` when uploading large files in android. Fixes #55 (#57) Fixed OOM crash while uploading files on devices with limited memory --- .../src/main/java/com/cloudinary/android/MultipartUtility.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index a652084d..441e8c88 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -48,6 +48,7 @@ public MultipartUtility(String requestURL, String charset, String boundary, Map< URL url = new URL(requestURL); httpConn = (HttpURLConnection) url.openConnection(); httpConn.setDoOutput(true); // indicates POST method + httpConn.setChunkedStreamingMode(0); httpConn.setDoInput(true); if (headers != null) { for (Map.Entry header : headers.entrySet()) { From 469c740c1d4c1603d9eacb775a623c75098be5c3 Mon Sep 17 00:00:00 2001 From: Nadav Ofir Date: Wed, 8 Mar 2017 13:59:45 +0200 Subject: [PATCH 225/592] Add async parameter to upload params + test (#63) --- .../src/main/java/com/cloudinary/test/UploaderTest.java | 8 ++++++++ cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../java/com/cloudinary/test/AbstractUploaderTest.java | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index d0a7ed90..c5ec10ea 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -163,6 +163,14 @@ public void testEager() throws Exception { ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } + public void testUploadAsync() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), + ObjectUtils.asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true))); + assertEquals(result.getString("status"), "pending"); + } + public void testHeaders() throws Exception { if (cloudinary.config.apiSecret == null) return; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index cbdcdff8..1ec1d71f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -8,7 +8,7 @@ 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"}; + "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { 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 4e63e962..c573b367 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 @@ -167,6 +167,12 @@ public void testEager() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", SDK_TEST_TAG)); } + @Test + public void testUploadAsync() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true)); + assertEquals((String)result.get("status"), "pending"); + } + @Test public void testHeaders() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", new String[]{"Link: 1"}, "tags", SDK_TEST_TAG)); From 282d522910ac077f24085fa6587212e876054f1d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Mar 2017 14:00:34 +0200 Subject: [PATCH 226/592] Add gravity-auto test (#64) --- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 457e244d..147e4251 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -297,6 +297,13 @@ private Object parametersForTestQuality() { } + @Test + public void testAutoGravity(){ + Transformation transformation = new Transformation().crop("crop").gravity("auto").width(0.5f); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,g_auto,w_0.5/test", result); + } + @Test public void testTransformationSimple() { // should support named transformation From 46af43127f7a8bc0e720e6cbc86b3915e6b67bb4 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Mar 2017 14:07:35 +0200 Subject: [PATCH 227/592] Add artistic filter test (#65) --- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 147e4251..852cf8c9 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -406,6 +406,13 @@ public void testEffectWithParam() { assertEquals(DEFAULT_UPLOAD_PATH + "e_sepia:10/test", result); } + @Test + public void testArtisticFilter(){ + Transformation transformation = new Transformation().effect("art", "incognito"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "e_art:incognito/test", result); + } + @Test public void testDensity() { // should support density From 9fb40b6b877e3dffbb2a135f1211f7c6fc8f0cc1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Mar 2017 14:12:37 +0200 Subject: [PATCH 228/592] Fix double encoding for commas and slashes in text layers (#66) Encode comma to %252C and slash to %252F --- .../com/cloudinary/transformation/TextLayer.java | 2 +- .../java/com/cloudinary/test/CloudinaryTest.java | 12 ++++++++---- .../com/cloudinary/transformation/LayerTest.java | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index 6337c266..73416b98 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -96,7 +96,7 @@ public TextLayer text(String text) { start = m.end(); } result.append(SmartUrlEncoder.encode(text.substring(start))); - this.text = result.toString().replace("%2C", "%E2%80%9A").replace("/", "%E2%81%84"); + this.text = result.toString().replace("%2C", "%252C").replace("/", "%252F"); return getThis(); } 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 852cf8c9..19f6d47f 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -964,11 +964,13 @@ public void testOverlayOptions() { "logo.png", new Layer().resourceType("video").publicId("cat"), "video:cat", + new TextLayer().text("Hello/World").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%252FWorld", new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), - "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt"}; @@ -995,11 +997,13 @@ public void testBackwardCampatibleOverlayOptions() { "logo.png", new LayerBuilder().resourceType("video").publicId("cat"), "video:cat", + new TextLayerBuilder().text("Hello/World").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%252FWorld", new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new TextLayerBuilder().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4"), - "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayerBuilder().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayerBuilder().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt"}; 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 76660c26..ad5e6e17 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -72,11 +72,13 @@ public void testLayerOptions() { "logo.png", new Layer().resourceType("video").publicId("cat"), "video:cat", + new TextLayer().text("Hello/World").fontFamily("Arial").fontSize(18), + "text:Arial_18:Hello%252FWorld", new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18), - "text:Arial_18:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4"), - "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%E2%80%9A%20Nice%20to%20meet%20you%3F", + "text:Arial_18_bold_italic_letter_spacing_4:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt"}; From 89e06f136c8ed8fc36674fa04e5bd2160faaeabd Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Mar 2017 14:57:30 +0200 Subject: [PATCH 229/592] Fix string interpolation test. --- .../src/test/java/com/cloudinary/TransformationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 8a2212eb..fd2a4e0f 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -182,6 +182,6 @@ public void testSupportStringInterpolation() { .overlay(new TextLayer().text( "$(start)Hello $(name)$(ext), $(no ) $( no)$(end)" ).fontFamily("Arial").fontSize(18)); - assertEquals("c_scale,l_text:Arial_18:$(start)Hello%20$(name)$(ext)%E2%80%9A%20%24%28no%20%29%20%24%28%20no%29$(end)", t.generate()); + assertEquals("c_scale,l_text:Arial_18:$(start)Hello%20$(name)$(ext)%252C%20%24%28no%20%29%20%24%28%20no%29$(end)", t.generate()); } } \ No newline at end of file From 3939b4770fd8c1c95f781c8be954f5e6b6f6f422 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Mar 2017 15:02:20 +0200 Subject: [PATCH 230/592] Version 1.9.0 --- CHANGELOG.md | 20 +++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69854c6f..1d210c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,24 @@ +1.9.0 / 2017-03-08 +================== + +New functionality +----------------- + + * Support **User defined variables** and **expressions**. + * Add `async` parameter to upload params(#63) + * Add `expired_at` parameter to private download. (#60) + * Add `moderation` parameter in explicit call (#59) + +Other changes +------------- + + * Fix double encoding for commas and slashes in text layers (#66) + * Add artistic filter test (#65) + * Add gravity-auto test (#64) + * Fix `OutOfMemoryError` when uploading large files in android. Fixes #55 (#57) + * Fix encoding error in api update resource (#61) + 1.8.1 / 2017-02-22 ================== diff --git a/README.md b/README.md index 99e9bd1b..43f467e3 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.8.1 + 1.9.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.8.1/cloudinary-core-1.8.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.8.1/cloudinary-http44-1.8.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.9.0/cloudinary-core-1.9.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.9.0/cloudinary-http44-1.9.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c17feb0a..c9de811c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.8.1"; + public final static String VERSION = "1.9.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 8da6d865c43f33acb976d9d90f3d363d1c4346b1 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Mar 2017 15:38:26 +0200 Subject: [PATCH 231/592] [maven-release-plugin] prepare release 1.9.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 018b0d53..5f8cacab 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 com.cloudinary cloudinary-android-test - 1.8.2-SNAPSHOT + 1.9.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.8.2-SNAPSHOT + 1.9.0
com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 9a7fa6e3..e89d01ad 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 1064cbbc..fdddb087 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 05d85c7b..2e05b3b0 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 011f79f7..12660352 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index dc1800af..5220f121 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index e754429f..86c4b776 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 5cc17e69..4804039a 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 0406f082..5f408673 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.8.2-SNAPSHOT + 1.9.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.9.0 From 5fca93526b47717cf7bd9f8169a6c7ee27498ebe Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 8 Mar 2017 15:38:34 +0200 Subject: [PATCH 232/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 5f8cacab..cc338ca0 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.9.0 + 1.9.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.9.0 + 1.9.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index e89d01ad..a5301cfc 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index fdddb087..2ecf9fb4 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 2e05b3b0..8871da11 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 12660352..d9d08642 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 5220f121..8f4453d8 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 86c4b776..1ef91b07 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 4804039a..799fbce4 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 5f408673..fc58ce7e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.9.0 + 1.9.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.9.0 + HEAD From 70726c372c3bb62f51115032ac4912a5985ed66b Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Fri, 10 Mar 2017 20:03:42 +0200 Subject: [PATCH 233/592] Remove duplicate quality parameter line. --- cloudinary-core/src/main/java/com/cloudinary/Transformation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 82da2681..a4aed8eb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -563,7 +563,6 @@ public String generate(Map options) { params.put("h", Expression.normalize(height)); params.put("o", Expression.normalize( options.get("opacity"))); params.put("q", Expression.normalize( options.get("quality"))); - params.put("q", Expression.normalize( options.get("quality"))); params.put("r", Expression.normalize( options.get("radius"))); params.put("so", startOffset); params.put("t", namedTransformation); From 2187cee81ef0010fba0d886111878d1e3a4e59ed Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 13 Mar 2017 14:31:14 +0200 Subject: [PATCH 234/592] Add `skip_transformation_name` parameter to generate archive. (#67) --- .../src/main/java/com/cloudinary/ArchiveParams.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java index e0bb5b78..64f03376 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -20,6 +20,7 @@ public class ArchiveParams { private boolean useOriginalFilename = false; private boolean async = false; private boolean keepDerived = false; + private boolean skipTransformationName = false; private String notificationUrl = null; private String[] targetTags = null; private String[] tags = null; @@ -110,6 +111,15 @@ public ArchiveParams async(boolean async) { return this; } + public boolean isSkipTransformationName() { + return skipTransformationName; + } + + public ArchiveParams skipTransformationName(boolean skipTransformationName) { + this.skipTransformationName = skipTransformationName; + return this; + } + public boolean isKeepDerived() { return keepDerived; } @@ -185,6 +195,7 @@ public Map toMap() { params.put("use_original_filename", useOriginalFilename); params.put("async", async); params.put("keep_derived", keepDerived); + params.put("skip_transformation_name", skipTransformationName); if (notificationUrl != null) params.put("notification_url", notificationUrl); if (targetTags != null) From 85940550aee35bc92747ad4894b8237ead1d0dc2 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 13 Mar 2017 14:31:49 +0200 Subject: [PATCH 235/592] Add expires at to generate archive (#68) --- .../src/main/java/com/cloudinary/ArchiveParams.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java index 64f03376..3ba2216d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -27,6 +27,7 @@ public class ArchiveParams { private String[] publicIds = null; private String[] prefixes = null; private Transformation[] transformations = null; + private Long expiresAt = null; public String resourceType() { return resourceType; @@ -183,6 +184,15 @@ public ArchiveParams transformations(Transformation[] transformations) { return this; } + public ArchiveParams expiresAt(Long expiresAt) { + this.expiresAt = expiresAt; + return this; + } + + public Long expiresAt(){ + return expiresAt; + } + public Map toMap() { Map params = new HashMap(); params.put("resource_type", resourceType); @@ -209,6 +219,9 @@ public Map toMap() { if (transformations != null) { params.put("transformations", Arrays.asList(transformations)); } + if (expiresAt != null){ + params.put("expires_at", expiresAt); + } return params; } } From a7fa6e7fa1407b61a55c84eb06ff25817275a625 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 13 Mar 2017 07:58:11 +0200 Subject: [PATCH 236/592] Normalize effect parameter --- .../src/main/java/com/cloudinary/Transformation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index a4aed8eb..4c3f60a4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -558,6 +558,7 @@ public String generate(Map options) { params.put("co", color); params.put("dpr", Expression.normalize(dpr)); params.put("du", duration); + params.put("e", Expression.normalize( options.get("effect"))); params.put("eo", endOffset); params.put("fl", flags); params.put("h", Expression.normalize(height)); @@ -581,7 +582,6 @@ public String generate(Map options) { "d", "default_image", "dl", "delay", "dn", "density", - "e", "effect", "f", "fetch_format", "g", "gravity", "l", "overlay", From a3ab6ee1040101241a45a1258d602d777f44777d Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 13 Mar 2017 08:09:51 +0200 Subject: [PATCH 237/592] Avoid normalizing negative numbers. --- .../com/cloudinary/transformation/BaseExpression.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index 366f13bc..e522522f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -87,17 +87,19 @@ public static String normalize(Object expresion) { } /** - * @return a regex pattern for operators and predefined vars + * @return a regex pattern for operators and predefined vars as /((operators)(?=[ _])|variables)/ */ private static String getpattern() { String pattern; final ArrayList operators = new ArrayList(OPERATORS.keySet()); Collections.sort(operators, Collections.reverseOrder()); - StringBuffer sb = new StringBuffer(); + StringBuffer sb = new StringBuffer("(("); for(String op: operators) { - sb.append("|").append(Pattern.quote(op)); + sb.append(Pattern.quote(op)).append("|"); } - pattern = "(" + StringUtils.join(PREDEFINED_VARS.keySet(), "|") + sb.toString() + ")"; + sb.deleteCharAt(sb.length() - 1); + sb.append(")(?=[ _])|").append(StringUtils.join(PREDEFINED_VARS.keySet(), "|")).append(")"); + pattern = sb.toString(); return pattern; } From 48b7ec8513520197bb8add561961c0b17d78c397 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 14 Mar 2017 12:16:42 +0200 Subject: [PATCH 238/592] Fix variables. * Fix variable regex. * Make Expression.serialize return normalized expression * Fix variable sorting. * Add tests for variable order. --- .../java/com/cloudinary/Transformation.java | 15 ++++++------- .../transformation/BaseExpression.java | 2 +- .../com/cloudinary/TransformationTest.java | 21 ++++++++++++++++--- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 4c3f60a4..34a21f12 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -12,6 +12,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Transformation { + public static final String VAR_NAME_RE = "^\\$[a-zA-Z][a-zA-Z0-9]+$"; protected Map transformation; protected List transformations; protected String htmlWidth; @@ -601,21 +602,21 @@ public String generate(Map options) { components.add(0, "if_" + Expression.normalize(ifValue)); } - List varParams = new ArrayList(); + SortedSet varParams = new TreeSet(); for( Object k: options.keySet()) { String key = (String) k; - if(key.matches("^\\$[a-zA-Z0-9]+$")) { + if(key.matches(VAR_NAME_RE)) { varParams.add(key + "_" + ObjectUtils.asString(options.get(k))); } } - String variables = processVar((Expression[]) options.get("variables")); - if (variables != null) { - varParams.add(variables); + if (!varParams.isEmpty()) { + components.add(StringUtils.join(varParams, ",")); } - if (varParams != null && !varParams.isEmpty()) { - components.add(StringUtils.join(varParams, ",")); + String variables = processVar((Expression[]) options.get("variables")); + if (variables != null) { + components.add(variables); } for (Map.Entry param : params.entrySet()) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index e522522f..cf0c98d7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -113,7 +113,7 @@ public T setParent(Transformation parent) { } public String serialize() { - return StringUtils.join(expressions, "_"); + return normalize(StringUtils.join(expressions, "_")); } @Override diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index fd2a4e0f..84741494 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -142,7 +142,7 @@ public void endIf2() throws Exception { assertEquals("force the if_else clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_else/c_crop,w_100/if_end", transformation.toString()); } - + @Test public void testArrayShouldDefineASetOfVariables() { // using methods @@ -153,7 +153,22 @@ public void testArrayShouldDefineASetOfVariables() { .width("$foo * 200"); assertEquals("if_fc_gt_2,$z_5,$foo_$z_mul_2,c_scale,w_$foo_mul_200", t.generate()); } - + + @Test + public void testShouldSortDefinedVariable(){ + Transformation t = new Transformation().variable("$second", 1).variable("$first", 2); + assertEquals("$first_2,$second_1", t.generate()); + } + + @Test + public void testShouldPlaceDefinedVariablesBeforeOrdered(){ + Transformation t = new Transformation() + .variables(variable("$z", 5), variable("$foo", "$z * 2")) + .variable("$second", 1) + .variable("$first", 2); + assertEquals("$first_2,$second_1,$z_5,$foo_$z_mul_2", t.generate()); + } + @Test public void testVariable(){ // using strings @@ -166,7 +181,7 @@ public void testVariable(){ .endIf(); assertEquals("$foo_10/if_fc_gt_2/c_scale,w_$foo_mul_200_div_fc/if_end", t.generate()); } - + @Test public void testShouldSupportTextValues() { Transformation t = new Transformation(); From b284f6c74e11700196ea40fe13188cc74351ea36 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 14 Mar 2017 17:34:05 +0200 Subject: [PATCH 239/592] Version 1.9.1 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d210c7b..13f0ee52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ +1.9.1 / 2017-03-14 +================== + + * Add expires at to generate archive (#68) + * Add `skip_transformation_name` parameter to generate archive. (#67) + * Fix variables. + * Fix variable regex. + * Make Expression.serialize return normalized expression + * Fix variable sorting. + * Add tests for variable order. + * Avoid normalizing negative numbers. + * Normalize effect parameter + * Remove duplicate quality parameter line. + 1.9.0 / 2017-03-08 ================== diff --git a/README.md b/README.md index 43f467e3..462e3f25 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.9.0 + 1.9.1 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.9.0/cloudinary-core-1.9.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.9.0/cloudinary-http44-1.9.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.9.1/cloudinary-core-1.9.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.9.1/cloudinary-http44-1.9.1.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c9de811c..bc4a1f43 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.9.0"; + public final static String VERSION = "1.9.1"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 28176160d7bf370f0be12c4ecba00a607880e4b7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 14 Mar 2017 17:57:21 +0200 Subject: [PATCH 240/592] [maven-release-plugin] prepare release 1.9.1 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index cc338ca0..28f668a2 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 com.cloudinary cloudinary-android-test - 1.9.1-SNAPSHOT + 1.9.1 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.9.1-SNAPSHOT + 1.9.1 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index a5301cfc..2ff33ab7 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 2ecf9fb4..fd5c63c9 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 8871da11..e375c37b 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index d9d08642..b8ef6a15 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 8f4453d8..b8ad9fc0 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 1ef91b07..9f6424e5 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 799fbce4..8a5873a9 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 cloudinary-test-common diff --git a/pom.xml b/pom.xml index fc58ce7e..0b94b0ce 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.9.1-SNAPSHOT + 1.9.1 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.9.1 From 87ab24dae200af2da025e502d986df7e484c1ca1 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 14 Mar 2017 17:57:29 +0200 Subject: [PATCH 241/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 28f668a2..542b4f5b 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT com.cloudinary cloudinary-android-test - 1.9.1 + 1.9.2-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.9.1 + 1.9.2-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 2ff33ab7..c1e48783 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index fd5c63c9..23c5524c 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index e375c37b..5753d3cb 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index b8ef6a15..23735258 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index b8ad9fc0..0b6ed722 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index 9f6424e5..dfc424ed 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 8a5873a9..a5bf8998 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 0b94b0ce..4603751a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.9.1 + 1.9.2-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.9.1 + HEAD From 90af188b009a8357daf3c6de8d14b19847e19e74 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 16 Mar 2017 09:15:00 +0200 Subject: [PATCH 242/592] Add `videoTag(String source)` overload to `Url`. --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 4 ++++ .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 1 + 2 files changed, 5 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 10c77aff..b8867ba9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -575,6 +575,10 @@ public String videoTag() { return videoTag("", new HashMap()); } + public String videoTag(String source) { + return videoTag(source, new HashMap()); + } + public String videoTag(Map attributes) { return videoTag("", attributes); } 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 19f6d47f..545d9004 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -774,6 +774,7 @@ public void testVideoTag() { expectedTag = String.format(expectedTag, expectedUrl, expectedUrl, expectedUrl, expectedUrl); assertEquals(expectedTag, cloudinary.url().videoTag("movie", emptyMap())); assertEquals(expectedTag, cloudinary.url().publicId("movie").videoTag()); + assertEquals(expectedTag, cloudinary.url().videoTag("movie")); } @Test From 2e96f0537fac1ae06f34c37852270558c9b6b9d3 Mon Sep 17 00:00:00 2001 From: Matthew Zavislak Date: Mon, 13 Jun 2016 16:41:38 -0700 Subject: [PATCH 243/592] Close streams in UploaderStrategy - Eliminate logs like: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. java.lang.Throwable: Explicit termination method 'end' not called at dalvik.system.CloseGuard.open(CloseGuard.java:180) at java.util.zip.Inflater.(Inflater.java:82) at com.android.okhttp.okio.GzipSource.(GzipSource.java:62) at com.android.okhttp.internal.http.HttpEngine.unzip(HttpEngine.java:645) at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:827) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:443) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:388) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:501) at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105) at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java) at com.cloudinary.android.UploaderStrategy.callApi(UploaderStrategy.java:81) at com.cloudinary.Uploader.callApi(Uploader.java:22) at com.cloudinary.Uploader.upload(Uploader.java:55) --- .../cloudinary/android/UploaderStrategy.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index ac3662c6..fe76eb44 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.util.Collection; import java.util.Map; @@ -75,22 +76,38 @@ public Map callApi(String action, Map params, Map options, Objec } else if (file instanceof byte[]) { multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); } + HttpURLConnection connection = multipart.execute(); int code; + + OutputStream outputStream = null; + try { code = connection.getResponseCode(); - } catch (IOException e) { + outputStream = connection.getOutputStream(); + } catch (Exception e) { if (e.getMessage().equals("No authentication challenges found")) { // Android trying to be clever... code = 401; } else { throw e; } + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (Exception e) {} + } } + InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); String responseData = readFully(responseStream); connection.disconnect(); + try { + responseStream.close(); + } catch (Exception e) {} + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } From 69595d22c30f2d7a4de6f45e655021781c108227 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 21 Mar 2017 16:39:31 +0200 Subject: [PATCH 244/592] Fix `MultipartUtility` - Verify inner stream is closed if an exception is thrown somewhere along the way. --- .../cloudinary/android/MultipartUtility.java | 5 ++ .../cloudinary/android/UploaderStrategy.java | 78 +++++++++---------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 441e8c88..0feba615 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -136,4 +136,9 @@ public HttpURLConnection execute() throws IOException { return httpConn; } + public void close(){ + if (writer != null){ + writer.close(); + } + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index fe76eb44..1c3486c6 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -5,7 +5,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.net.HttpURLConnection; import java.util.Collection; import java.util.Map; @@ -48,66 +47,63 @@ public Map callApi(String action, Map params, Map options, Objec } } String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); - } - } else { - if (StringUtils.isNotBlank(param.getValue())) { - multipart.addFormField(param.getKey(), param.getValue().toString()); + MultipartUtility multipart = null; + HttpURLConnection connection; + + try { + multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); + + // Remove blank parameters + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof Collection) { + for (Object value : (Collection) param.getValue()) { + multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); + } + } else { + if (StringUtils.isNotBlank(param.getValue())) { + multipart.addFormField(param.getKey(), param.getValue().toString()); + } } } - } - if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - multipart.addFilePart("file", (File) file, filename); - } else if (file instanceof String) { - multipart.addFormField("file", (String) file); - } else if (file instanceof InputStream) { - multipart.addFilePart("file", (InputStream) file, filename); - } else if (file instanceof byte[]) { - multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); + if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + file = new File((String) file); + } + String filename = (String) options.get("filename"); + if (file instanceof File) { + multipart.addFilePart("file", (File) file, filename); + } else if (file instanceof String) { + multipart.addFormField("file", (String) file); + } else if (file instanceof InputStream) { + multipart.addFilePart("file", (InputStream) file, filename); + } else if (file instanceof byte[]) { + multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); + } + + connection = multipart.execute(); + } finally { + if (multipart != null){ + // Closing more than once has no effect so we can call it safely without having to check state + multipart.close(); + } } - HttpURLConnection connection = multipart.execute(); int code; - - OutputStream outputStream = null; - try { code = connection.getResponseCode(); - outputStream = connection.getOutputStream(); - } catch (Exception e) { + } catch (IOException e) { if (e.getMessage().equals("No authentication challenges found")) { // Android trying to be clever... code = 401; } else { throw e; } - } finally { - if (outputStream != null) { - try { - outputStream.close(); - } catch (Exception e) {} - } } - InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); String responseData = readFully(responseStream); connection.disconnect(); - try { - responseStream.close(); - } catch (Exception e) {} - if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } From 80f10bfc74e85d6ef742c25f9987ac5b2b799096 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 22 Mar 2017 17:04:25 +0200 Subject: [PATCH 245/592] Add javaDoc for `MultipartUtility.close()` --- .../main/java/com/cloudinary/android/MultipartUtility.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 0feba615..a8efd6b0 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -136,6 +136,10 @@ public HttpURLConnection execute() throws IOException { return httpConn; } + /*** + * Closes the internal connection's output stream. + * Closing a previously closed stream has no effect. + */ public void close(){ if (writer != null){ writer.close(); From 975aac414187dc6dc23c8b2c68b75a82ff6bddcb Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 16 Mar 2017 11:50:54 +0200 Subject: [PATCH 246/592] Add support for `allowMissing` parameter in archive creation. --- .../src/main/java/com/cloudinary/ArchiveParams.java | 11 +++++++++++ .../src/main/java/com/cloudinary/Util.java | 1 + 2 files changed, 12 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java index 3ba2216d..3b998a88 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -21,6 +21,7 @@ public class ArchiveParams { private boolean async = false; private boolean keepDerived = false; private boolean skipTransformationName = false; + private boolean allowMissing = false; private String notificationUrl = null; private String[] targetTags = null; private String[] tags = null; @@ -121,6 +122,15 @@ public ArchiveParams skipTransformationName(boolean skipTransformationName) { return this; } + public boolean isAllowMissing(){ + return allowMissing; + } + + public ArchiveParams allowMissing(boolean allowMissing){ + this.allowMissing = allowMissing; + return this; + } + public boolean isKeepDerived() { return keepDerived; } @@ -206,6 +216,7 @@ public Map toMap() { params.put("async", async); params.put("keep_derived", keepDerived); params.put("skip_transformation_name", skipTransformationName); + params.put("allow_missing", allowMissing); if (notificationUrl != null) params.put("notification_url", notificationUrl); if (targetTags != null) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 1ec1d71f..e3c76a74 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -173,6 +173,7 @@ public static final Map buildArchiveParams(Map options, String t putEager("transformations", options, params); putObject("timestamp", options, params, Util.timestamp()); putBoolean("skip_transformation_name", options, params); + putBoolean("allow_missing", options, params); putObject("expires_at", options, params); } return params; From 1d1e571ba79621d5b958fabe7ef0023f0462960f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 16 Mar 2017 14:31:50 +0200 Subject: [PATCH 247/592] Add support for notification_url param in `Api.update` --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index edd96238..49493ab0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -130,6 +130,7 @@ public ApiResponse update(String public_id, Map options) throws Exception { Map params = new HashMap(); Util.processWriteParameters(options, params); params.put("moderation_status", options.get("moderation_status")); + params.put("notification_url", options.get("notification_url")); ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, public_id), params, options); return response; From 7501af7eb67f752598beddff5bc820f62c63b385 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 22 Mar 2017 14:40:40 +0200 Subject: [PATCH 248/592] Add test for `generate_archive` of raw resources. --- .../java/com/cloudinary/test/AbstractUploaderTest.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 c573b367..b2bf00cf 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 @@ -36,6 +36,7 @@ public static void setUpClass() throws IOException { } cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, "resource_type", "raw")); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, "transformation", new Transformation().crop("scale").width(10))); @@ -499,6 +500,12 @@ public void testCreateArchive() throws Exception { assertEquals(4, result.get("file_count")); } + @Test + public void testCreateArchiveRaw() throws Exception { + Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).resourceType("raw")); + assertEquals(1, result.get("file_count")); + } + @Test public void testDownloadArchive() throws Exception { String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); From 3dfdca554d738ad9cee63922989b29d73feb6b3b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 12 Mar 2017 12:10:23 +0200 Subject: [PATCH 249/592] Add upload progress callback for Android --- .../cloudinary/android/MultipartUtility.java | 36 ++++++---- .../cloudinary/android/UploaderStrategy.java | 55 +++++++++++---- .../main/java/com/cloudinary/Uploader.java | 68 ++++++++++++++++--- .../strategies/AbstractUploaderStrategy.java | 6 +- .../strategies/ProgressCallback.java | 5 ++ .../cloudinary/http42/UploaderStrategy.java | 30 ++++---- .../cloudinary/http43/UploaderStrategy.java | 7 +- .../cloudinary/http44/UploaderStrategy.java | 9 ++- 8 files changed, 162 insertions(+), 54 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 441e8c88..74fb3a97 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -1,19 +1,12 @@ package com.cloudinary.android; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; +import com.cloudinary.Cloudinary; + +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLConnection; import java.util.Map; -import com.cloudinary.Cloudinary; - /** * This utility class provides an abstraction layer for sending multipart HTTP * POST requests to a web server. @@ -25,14 +18,13 @@ public class MultipartUtility { private final String boundary; private static final String LINE_FEED = "\r\n"; private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; + private final MultipartCallback multipartCallback; private HttpURLConnection httpConn; private String charset; private OutputStream outputStream; private PrintWriter writer; - public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; - /** * This constructor initializes a new HTTP POST request with content type is * set to multipart/form-data @@ -42,8 +34,13 @@ public class MultipartUtility { * @throws IOException */ public MultipartUtility(String requestURL, String charset, String boundary, Map headers) throws IOException { + this(requestURL, charset, boundary, headers, null); + } + + public MultipartUtility(String requestURL, String charset, String boundary, Map headers, MultipartCallback multipartCallback) throws IOException { this.charset = charset; this.boundary = boundary; + this.multipartCallback = multipartCallback; URL url = new URL(requestURL); httpConn = (HttpURLConnection) url.openConnection(); @@ -62,7 +59,7 @@ public MultipartUtility(String requestURL, String charset, String boundary, Map< } public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { - this(requestURL, charset, boundary, null); + this(requestURL, charset, boundary, null, null); } /** @@ -107,9 +104,11 @@ public void addFilePart(String fieldName, InputStream inputStream, String fileNa writer.flush(); byte[] buffer = new byte[4096]; - int bytesRead = -1; + int bytesRead; + long totalRead = 0; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); + notifyCallback(totalRead += bytesRead); } outputStream.flush(); inputStream.close(); @@ -118,6 +117,12 @@ public void addFilePart(String fieldName, InputStream inputStream, String fileNa writer.flush(); } + private void notifyCallback(long bytes) { + if (multipartCallback != null) { + multipartCallback.totalBytesLoaded(bytes); + } + } + public void addFilePart(String fieldName, InputStream inputStream) throws IOException { addFilePart(fieldName, inputStream, "file"); } @@ -136,4 +141,7 @@ public HttpURLConnection execute() throws IOException { return httpConn; } + interface MultipartCallback { + void totalBytesLoaded(long bytes); + } } diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index ac3662c6..fb54b5bf 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -1,26 +1,24 @@ package com.cloudinary.android; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; + +import java.io.*; import java.net.HttpURLConnection; import java.util.Collection; import java.util.Map; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; +import static com.cloudinary.android.MultipartUtility.*; public class UploaderStrategy extends AbstractUploaderStrategy { @SuppressWarnings("rawtypes") @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { + public Map callApi(String action, Map params, Map options, Object file, final ProgressCallback progressCallback) throws IOException { // initialize options if passed as null if (options == null) { options = ObjectUtils.emptyMap(); @@ -46,8 +44,22 @@ public Map callApi(String action, Map params, Map options, Objec params.put("api_key", apiKey); } } + String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); - MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); + MultipartCallback multipartCallback; + if (progressCallback == null) { + multipartCallback = null; + } else { + final long totalBytes = determineLength(file); + multipartCallback = new MultipartCallback() { + @Override + public void totalBytesLoaded(long bytes) { + progressCallback.onProgress(bytes, totalBytes); + } + }; + } + + MultipartUtility multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers"), multipartCallback); // Remove blank parameters for (Map.Entry param : params.entrySet()) { @@ -112,6 +124,23 @@ public Map callApi(String action, Map params, Map options, Objec } } + private long determineLength(Object file) { + long actualLength = -1; + + if (file != null) { + if (file instanceof File) { + actualLength = ((File) file).length(); + } else if (file instanceof byte[]) { + actualLength = ((byte[]) file).length; + } else if (!(file instanceof InputStream)) { + File f = new File(file.toString()); + actualLength = f.length(); + } + } + + return actualLength; + } + protected static String readFully(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index ae09ca6c..b2e8064f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; +import com.cloudinary.strategies.ProgressCallback; import org.cloudinary.json.JSONObject; import com.cloudinary.strategies.AbstractUploaderStrategy; @@ -30,7 +31,11 @@ private Command() { } public Map callApi(String action, Map params, Map options, Object file) throws IOException { - return strategy.callApi(action, params, options, file); + return strategy.callApi(action, params, options, file, null); + } + + public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { + return strategy.callApi(action, params, options, file, progressCallback); } private Cloudinary cloudinary; @@ -51,39 +56,64 @@ public Map buildUploadParams(Map options) { } public Map unsignedUpload(Object file, String uploadPreset, Map options) throws IOException { + return unsignedUpload(file, uploadPreset, options, null); + } + + public Map unsignedUpload(Object file, String uploadPreset, Map options, ProgressCallback progressCallback) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); HashMap nextOptions = new HashMap(options); nextOptions.put("unsigned", true); nextOptions.put("upload_preset", uploadPreset); - return upload(file, nextOptions); + return upload(file, nextOptions, progressCallback); } public Map upload(Object file, Map options) throws IOException { + return upload(file, options, null); + } + + public Map upload(Object file, Map options, final ProgressCallback progressCallback) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); Map params = buildUploadParams(options); - return callApi("upload", params, options, file); + + return callApi("upload", params, options, file, progressCallback); } public Map uploadLargeRaw(Object file, Map options) throws IOException { - return uploadLargeRaw(file, options, 20000000); + return uploadLargeRaw(file, options, 20000000, null); + } + + public Map uploadLargeRaw(Object file, Map options, ProgressCallback progressCallback) throws IOException { + return uploadLargeRaw(file, options, 20000000, progressCallback); } public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { + return uploadLargeRaw(file, options, bufferSize, null); + } + + public Map uploadLargeRaw(Object file, Map options, int bufferSize, ProgressCallback callback) throws IOException { Map sentOptions = new HashMap(); sentOptions.putAll(options); sentOptions.put("resource_type", "raw"); - return uploadLarge(file, sentOptions, bufferSize); + return uploadLarge(file, sentOptions, bufferSize, callback); } public Map uploadLarge(Object file, Map options) throws IOException { + return uploadLarge(file, options, null); + } + + public Map uploadLarge(Object file, Map options, ProgressCallback progressCallback) throws IOException { int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), 20000000); - return uploadLarge(file, options, bufferSize); + return uploadLarge(file, options, bufferSize, progressCallback); } @SuppressWarnings("resource") public Map uploadLarge(Object file, Map options, int bufferSize) throws IOException { + return uploadLarge(file, options, bufferSize, null); + } + + public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallback progressCallback) throws IOException { InputStream input; long length = -1; if (file instanceof InputStream) { @@ -100,14 +130,14 @@ public Map uploadLarge(Object file, Map options, int bufferSize) throws IOExcept input = new FileInputStream(f); } try { - Map result = uploadLargeParts(input, options, bufferSize, length); + Map result = uploadLargeParts(input, options, bufferSize, length, progressCallback); return result; } finally { input.close(); } } - private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length) throws IOException { + private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length, final ProgressCallback progressCallback) throws IOException { Map params = buildUploadParams(options); Map sentOptions = new HashMap(); @@ -123,6 +153,8 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon int partNumber = 0; long totalBytes = 0; Map response = null; + final long knownLengthBeforeUpload = length; + long totalBytesUploaded = 0; while (true) { bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize); boolean atEnd = bytesRead == -1; @@ -147,9 +179,27 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon extraHeaders.put("Content-Range", range); Map sentParams = new HashMap(); sentParams.putAll(params); - response = callApi("upload", sentParams, sentOptions, buffer); + + // wrap the callback with another callback to account for multiple parts + final long bytesUploadedSoFar = totalBytesUploaded; + final ProgressCallback singlePartProgressCallback; + if (progressCallback == null) { + singlePartProgressCallback = null; + } else { + singlePartProgressCallback = new ProgressCallback() { + + @Override + public void onProgress(long bytesUploaded, long totalBytes) { + progressCallback.onProgress(bytesUploadedSoFar + bytesUploaded, knownLengthBeforeUpload); + } + }; + } + + response = callApi("upload", sentParams, sentOptions, buffer, singlePartProgressCallback); + if (atEnd) break; buffer[0] = nibbleBuffer[0]; + totalBytesUploaded += currentBufferSize; currentBufferSize = 1; partNumber++; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index fee97c9a..39a932bf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -18,5 +18,9 @@ public Cloudinary cloudinary() { } @SuppressWarnings("rawtypes") - public abstract Map callApi(String action, Map params, Map options, Object file) throws IOException; + public Map callApi(String action, Map params, Map options, Object file) throws IOException{ + return callApi(action, params, options, file, null); + } + + public abstract Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java new file mode 100644 index 00000000..1b93535c --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java @@ -0,0 +1,5 @@ +package com.cloudinary.strategies; + +public interface ProgressCallback { + void onProgress(long bytesUploaded, long totalBytes); +} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index ac41d990..1efb43af 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -1,12 +1,11 @@ package com.cloudinary.http42; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.Collection; -import java.util.Map; - +import com.cloudinary.Cloudinary; +import com.cloudinary.Util; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.strategies.ProgressCallback; +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; @@ -22,17 +21,22 @@ import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; -import com.cloudinary.Cloudinary; -import com.cloudinary.Util; -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +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) throws IOException { + 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(); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 5e7ff3e9..72e9786c 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.Map; +import com.cloudinary.strategies.ProgressCallback; import org.apache.http.HttpHost; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; @@ -54,7 +55,11 @@ public void init(Uploader uploader) { @SuppressWarnings({"rawtypes", "unchecked"}) @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { + 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(); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index d4fc5b6c..8a8b9892 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -6,11 +6,10 @@ import java.util.Collection; import java.util.Map; +import com.cloudinary.strategies.ProgressCallback; import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; @@ -56,7 +55,11 @@ public void init(Uploader uploader) { @SuppressWarnings({"rawtypes", "unchecked"}) @Override - public Map callApi(String action, Map params, Map options, Object file) throws IOException { + 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(); From 9aa76e0f97c9d310fc68f5a2a26b188881fdc4ae Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 16 Mar 2017 12:52:10 +0200 Subject: [PATCH 250/592] Add `progressCallback` test cases. --- .../com/cloudinary/test/UploaderTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index c5ec10ea..363baf98 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -6,6 +6,7 @@ import com.cloudinary.Coordinates; import com.cloudinary.Transformation; import com.cloudinary.android.Utils; +import com.cloudinary.strategies.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; @@ -22,6 +23,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public class UploaderTest extends InstrumentationTestCase { @@ -46,6 +49,31 @@ protected InputStream getAssetStream(String filename) throws IOException { return getInstrumentation().getContext().getAssets().open(filename); } + private long getAssetFileSize(String filename) { + try { + return getInstrumentation().getContext().getAssets().openFd(filename).getLength(); + } catch (IOException e) { + return -1; + } + } + + private File getLargeFile() throws IOException { + File temp = File.createTempFile("cldupload.test.", ""); + FileOutputStream out = new FileOutputStream(temp); + int[] header = new int[]{0x42, 0x4D, 0x4A, 0xB9, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB8, 0x59, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xB8, 0x1E, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF5, 0x28, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + byte[] byteHeader = new byte[138]; + for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; + byte[] piece = new byte[10]; + Arrays.fill(piece, (byte) 0xff); + out.write(byteHeader); + for (int i = 1; i <= 588000; i++) { + out.write(piece); + } + out.close(); + assertEquals(5880138, temp.length()); + return temp; + } + public void testUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -61,6 +89,37 @@ public void testUpload() throws Exception { assertEquals(result.get("signature"), expected_signature); } + public void testUploadProgressCallback() throws Exception { + if (cloudinary.config.apiSecret == null) + return; + + final CountDownLatch signal = new CountDownLatch(1); + final long totalLength = getAssetFileSize(TEST_IMAGE); + + ProgressCallback progressCallback = new ProgressCallback() { + @Override + public void onProgress(long bytesUploaded, long totalBytes) { + if (bytesUploaded == totalLength) { + signal.countDown(); + } + } + }; + + JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true), progressCallback)); + + signal.await(5, TimeUnit.SECONDS); + assertEquals(signal.getCount(), 0); + assertEquals(result.getLong("width"), 241L); + assertEquals(result.getLong("height"), 51L); + assertNotNull(result.get("colors")); + assertNotNull(result.get("predominant")); + Map to_sign = new HashMap(); + to_sign.put("public_id", result.getString("public_id")); + to_sign.put("version", ObjectUtils.asString(result.get("version"))); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + assertEquals(result.get("signature"), expected_signature); + } + public void testUnsignedUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -380,4 +439,40 @@ public void testUploadLarge() throws Exception { assertEquals(1400L, resource.getLong("width")); assertEquals(1400L, resource.getLong("height")); } + + public void testUploadLargeProgressCallback() throws Exception { + // support uploading large files + if (cloudinary.config.apiSecret == null) + return; + + + File temp = getLargeFile(); + final CountDownLatch signal = new CountDownLatch(1); + final long totalLength = temp.length(); + + ProgressCallback progressCallback = new ProgressCallback() { + @Override + public void onProgress(long bytesUploaded, long totalBytes) { + if (bytesUploaded == totalLength) { + signal.countDown(); + } + } + }; + JSONObject resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000), progressCallback)); + + signal.await(120, TimeUnit.SECONDS); + assertEquals(signal.getCount(), 0); + + assertEquals("raw", resource.getString("resource_type")); + + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5243000))); + assertEquals("image", resource.getString("resource_type")); + assertEquals(1400L, resource.getLong("width")); + assertEquals(1400L, resource.getLong("height")); + + resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138))); + assertEquals("image", resource.getString("resource_type")); + assertEquals(1400L, resource.getLong("width")); + assertEquals(1400L, resource.getLong("height")); + } } From 2ebf29a1a1662c3cfde9ad40a3e29e72654301d2 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 22 Mar 2017 16:59:18 +0200 Subject: [PATCH 251/592] Add javaDoc for `MultipartCallback` --- .../src/main/java/com/cloudinary/android/MultipartUtility.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java index 74fb3a97..5808da23 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java @@ -141,6 +141,9 @@ public HttpURLConnection execute() throws IOException { return httpConn; } + /*** + * For internal use only - callback to monitor multipart upload progress + */ interface MultipartCallback { void totalBytesLoaded(long bytes); } From 517b9dc16fe6241517acd196fbfdc6b17f2ce606 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 30 Mar 2017 21:10:23 +0300 Subject: [PATCH 252/592] Move `ProgressCallback` to the `com.cloudinary` package. --- .../main/java/com/cloudinary/test/UploaderTest.java | 4 +--- .../com/cloudinary/android/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/ProgressCallback.java | 13 +++++++++++++ .../src/main/java/com/cloudinary/Uploader.java | 9 +++++---- .../strategies/AbstractUploaderStrategy.java | 1 + .../com/cloudinary/strategies/ProgressCallback.java | 5 ----- .../com/cloudinary/http42/UploaderStrategy.java | 2 +- .../com/cloudinary/http43/UploaderStrategy.java | 2 +- .../com/cloudinary/http44/UploaderStrategy.java | 2 +- 9 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/ProgressCallback.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 363baf98..2d07aef1 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -6,7 +6,7 @@ import com.cloudinary.Coordinates; import com.cloudinary.Transformation; import com.cloudinary.android.Utils; -import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; @@ -17,8 +17,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URLDecoder; -import java.net.URLEncoder; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index fb54b5bf..088d728b 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -1,7 +1,7 @@ package com.cloudinary.android; import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONException; diff --git a/cloudinary-core/src/main/java/com/cloudinary/ProgressCallback.java b/cloudinary-core/src/main/java/com/cloudinary/ProgressCallback.java new file mode 100644 index 00000000..7ef81b01 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/ProgressCallback.java @@ -0,0 +1,13 @@ +package com.cloudinary; + +/** + * Defines a callback for network operations. + */ +public interface ProgressCallback { + /** + * Invoked during network operation. + * @param bytesUploaded the number of bytes uploaded so far + * @param totalBytes the total number of byte to upload - if known + */ + void onProgress(long bytesUploaded, long totalBytes); +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index b2e8064f..6d3f3d58 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; -import com.cloudinary.strategies.ProgressCallback; import org.cloudinary.json.JSONObject; import com.cloudinary.strategies.AbstractUploaderStrategy; @@ -20,6 +19,8 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Uploader { + public static final int BUFFER_SIZE = 20000000; + private final class Command { final static String add = "add"; final static String remove = "remove"; @@ -81,11 +82,11 @@ public Map upload(Object file, Map options, final ProgressCallback progressCallb } public Map uploadLargeRaw(Object file, Map options) throws IOException { - return uploadLargeRaw(file, options, 20000000, null); + return uploadLargeRaw(file, options, BUFFER_SIZE, null); } public Map uploadLargeRaw(Object file, Map options, ProgressCallback progressCallback) throws IOException { - return uploadLargeRaw(file, options, 20000000, progressCallback); + return uploadLargeRaw(file, options, BUFFER_SIZE, progressCallback); } public Map uploadLargeRaw(Object file, Map options, int bufferSize) throws IOException { @@ -104,7 +105,7 @@ public Map uploadLarge(Object file, Map options) throws IOException { } public Map uploadLarge(Object file, Map options, ProgressCallback progressCallback) throws IOException { - int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), 20000000); + int bufferSize = ObjectUtils.asInteger(options.get("chunk_size"), BUFFER_SIZE); return uploadLarge(file, options, bufferSize, progressCallback); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index 39a932bf..db652e96 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -4,6 +4,7 @@ import java.util.Map; import com.cloudinary.Cloudinary; +import com.cloudinary.ProgressCallback; import com.cloudinary.Uploader; public abstract class AbstractUploaderStrategy { diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java deleted file mode 100644 index 1b93535c..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/ProgressCallback.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cloudinary.strategies; - -public interface ProgressCallback { - void onProgress(long bytesUploaded, long totalBytes); -} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 1efb43af..c3d311c7 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -3,7 +3,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Util; import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.apache.http.HttpHost; diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 72e9786c..8cd3f3da 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.Map; -import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.ProgressCallback; import org.apache.http.HttpHost; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 8a8b9892..63df9469 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.Map; -import com.cloudinary.strategies.ProgressCallback; +import com.cloudinary.ProgressCallback; import org.apache.http.HttpHost; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; From 3bf53e42f6341db2b5f1a77913554642b9358919 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 15 Mar 2017 11:47:54 +0200 Subject: [PATCH 253/592] Fix ParseException when accessing `Response.rateLimits` with default locale set to non-english. --- .../main/java/com/cloudinary/http42/api/Response.java | 4 ++-- .../main/java/com/cloudinary/http43/api/Response.java | 4 ++-- .../main/java/com/cloudinary/http44/api/Response.java | 4 ++-- .../main/java/com/cloudinary/test/AbstractApiTest.java | 9 +++++++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java index 07c0b29c..dfe44ed6 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java @@ -4,6 +4,7 @@ import java.text.ParseException; 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; @@ -33,8 +34,7 @@ public HttpResponse getRawHttpResponse() { private static final Pattern RATE_LIMIT_REGEX = Pattern .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); public Map rateLimits() throws ParseException { Header[] headers = this.response.getAllHeaders(); 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 index 14582f50..fd4bfa58 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java @@ -3,6 +3,7 @@ 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; @@ -32,8 +33,7 @@ public HttpResponse getRawHttpResponse() { private static final Pattern RATE_LIMIT_REGEX = Pattern .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); public Map rateLimits() throws java.text.ParseException { Header[] headers = this.response.getAllHeaders(); 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 index 51805353..0beb97c5 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java @@ -3,6 +3,7 @@ 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; @@ -32,8 +33,7 @@ public HttpResponse getRawHttpResponse() { private static final Pattern RATE_LIMIT_REGEX = Pattern .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat( - RFC1123_PATTERN); + private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); public Map rateLimits() throws java.text.ParseException { Header[] headers = this.response.getAllHeaders(); 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 aaf98bae..4e8b5441 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 @@ -5,6 +5,7 @@ import com.cloudinary.Coordinates; import com.cloudinary.Transformation; import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.RateLimit; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.transformation.TextLayer; @@ -13,6 +14,7 @@ import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.equalTo; import org.junit.*; +import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import java.io.IOException; @@ -433,6 +435,13 @@ public void test18Usage() throws Exception { assertNotNull(result.get("last_updated")); } + @Test + public void testRateLimitWithNonEnglishLocale() throws Exception { + Locale.setDefault(new Locale("de", "DE")); + ApiResponse result = cloudinary.api().usage(new HashMap()); + Assert.assertNotNull(result.apiRateLimit().getReset()); + } + @Test public void test19Ping() throws Exception { // should support ping API call From bd4979955e275ce8f1c6e883c30bc6ebe836556d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 15 Mar 2017 14:54:25 +0200 Subject: [PATCH 254/592] Add method `deleteDerivedResourcesByTransformations` to Admin Api --- .../src/main/java/com/cloudinary/Api.java | 13 +++++++++++- .../com/cloudinary/test/AbstractApiTest.java | 20 +++++++++++++++++++ 2 files changed, 32 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 49493ab0..6f97de4a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -140,8 +140,19 @@ public ApiResponse deleteResources(Iterable publicIds, Map options) thro 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 = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor"); + Map params = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor", "transformations"); + params.put("public_ids", publicIds); + return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); + } + + public ApiResponse deleteDerivedResourcesByTransformations(Iterable publicIds, List transformations, 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 = ObjectUtils.only(options, "invalidate", "next_cursor"); + params.put("keep_original", true); params.put("public_ids", publicIds); + params.put("transformations", Util.buildEager(transformations)); return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), 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 4e8b5441..49297198 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 @@ -281,6 +281,26 @@ public void test08DeleteDerived() throws Exception { assertEquals(derived.size(), 0); } + @Test() + public void testDeleteDerivedByTransformation() throws Exception { + // should allow deleting resources + String public_id = "api_test_123"; + List transformations = new ArrayList(); + transformations.add(new Transformation().angle(90)); + transformations.add(new Transformation().width(120)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id, "tags", UPLOAD_TAGS, "eager", transformations)); + Map resource = api.resource(public_id, ObjectUtils.emptyMap()); + assertNotNull(resource); + List derived = ((List) resource.get("derived")); + assertTrue(derived.size() == 2); + api.deleteDerivedResourcesByTransformations(ObjectUtils.asArray(public_id), ObjectUtils.asArray(transformations), ObjectUtils.emptyMap()); + + resource = api.resource(public_id, ObjectUtils.emptyMap()); + assertNotNull(resource); + derived = ((List) resource.get("derived")); + assertTrue(derived.size() == 0); + } + @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources From a0cecc483290d6cf51edc7a67fbd37d34215444b Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 2 Apr 2017 03:01:19 +0300 Subject: [PATCH 255/592] Add `ocr` to explicit API --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 6d3f3d58..85c95fb4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -244,6 +244,7 @@ public Map explicit(String publicId, Map options) throws IOException { params.put("headers", Util.buildCustomHeaders(options.get("headers"))); params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); params.put("moderation", (String) options.get("moderation")); + params.put("ocr", (String) options.get("ocr")); if (options.get("face_coordinates") != null) { params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); } From 7a6de9d765411213cce56d2dd1451400c6db33de Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 2 Apr 2017 08:00:59 +0300 Subject: [PATCH 256/592] Add `ocr` gravity value tests --- .../com/cloudinary/test/CloudinaryTest.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) 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 545d9004..55774331 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -288,7 +288,7 @@ public void testQuality( Object quality, String result) { assertEquals(result, transformation.generate()); } @SuppressWarnings("unused") - private Object parametersForTestQuality() { + private Object[][] parametersForTestQuality() { return new Object[][]{ {0.4, "q_0.4"}, {"0.4", "q_0.4"}, @@ -298,10 +298,24 @@ private Object parametersForTestQuality() { } @Test - public void testAutoGravity(){ - Transformation transformation = new Transformation().crop("crop").gravity("auto").width(0.5f); + @TestCaseName("{method}: {0}") + @Parameters + public void testAutoGravity(String value, String serialized){ + Transformation transformation = new Transformation().crop("crop").gravity(value).width(0.5f); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,g_auto,w_0.5/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,"+ serialized + ",w_0.5/test", result); + } + @SuppressWarnings("unused") + private String[][] parametersForTestAutoGravity() { + return new String[][]{ + {"west", "g_west"}, + {"auto", "g_auto"}, + {"auto:good", "g_auto:good"}, + {"auto:ocr_text", "g_auto:ocr_text"}, + {"ocr_text", "g_ocr_text"}, + {"ocr_text:adv_ocr", "g_ocr_text:adv_ocr"} + }; + } @Test From f36b434bf3359fae9c450358a87f414f3f7fdfc7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 2 Apr 2017 09:31:58 +0300 Subject: [PATCH 257/592] Version 1.10.0 --- CHANGELOG.md | 25 +++++++++++++++++++ README.md | 4 +-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0ee52..6a868b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,29 @@ +1.10.0 / 2017-04-02 +=================== + +New functionality +----------------- + + * Add upload progress callback for Android + * Add support for notification_url param in `Api.update` + * Add support for `allowMissing` parameter in archive creation. + * Add `videoTag(String source)` overload to `Url`. + * Add `deleteDerivedResourcesByTransformations` to Admin Api + * Add `ocr` to explicit API + +Other changes +------------- + + * Add `ocr` gravity value tests + * Fix ParseException when accessing `Response.rateLimits` with default locale set to non-english. + * Add javaDoc for `MultipartUtility.close()` + * Merge pull request #46 Close streams in UploaderStrategy + * Add javaDoc for `MultipartCallback` + * Add `progressCallback` test cases. + * Add test for `generate_archive` of raw resources. + * Fix `MultipartUtility` - Verify inner stream is closed if an exception is thrown somewhere along the way. + 1.9.1 / 2017-03-14 ================== diff --git a/README.md b/README.md index 462e3f25..39369652 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.9.1 + 1.10.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.9.1/cloudinary-core-1.9.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.9.1/cloudinary-http44-1.9.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.10.0/cloudinary-core-1.10.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.10.0/cloudinary-http44-1.10.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index bc4a1f43..324a9614 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.9.1"; + public final static String VERSION = "1.10.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 2f13019393f9c10296c9d50041247b8f57b8edb5 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 25 Apr 2017 16:58:15 +0300 Subject: [PATCH 258/592] Add `fps` transformation parameter. --- .../main/java/com/cloudinary/Cloudinary.java | 2 +- .../java/com/cloudinary/Transformation.java | 28 +++++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 15 ++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 324a9614..d8aef295 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.10.0"; + public final static String VERSION = "1.11.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 34a21f12..17c7359c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -413,6 +413,33 @@ public Transformation endIf() { return chain(); } + /** + * fps (frames per second) parameter for video + * @param value Either a single value int or float or a range in the format <start>[-<end>].
+ * For example, 23-29.7 + * @return the transformation for chaining + */ + public Transformation fps(String value) { + return param("fps", value); + } + + /** + * fps (frames per second) parameter for video + * @param value the desired fps + * @return the transformation for chaining + */ + public Transformation fps(double value) { + return param("fps", new Float(value)); + } + + /** + * fps (frames per second) parameter for video + * @param value the desired fps + * @return the transformation for chaining + */ + public Transformation fps(int value) { + return param("fps", new Integer(value)); + } public boolean isResponsive() { return this.isResponsive; @@ -584,6 +611,7 @@ public String generate(Map options) { "dl", "delay", "dn", "density", "f", "fetch_format", + "fps", "fps", "g", "gravity", "l", "overlay", "p", "prefix", 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 55774331..934dcffb 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1060,6 +1060,19 @@ public void testResponsiveBreakpointsToJson() { assertArrayEquals(expectedArr, actualArr); } + @Test + public void testFps() { + Transformation t = new Transformation().fps(12); + assertEquals("fps_12", t.generate()); + t = new Transformation().fps(12.5); + assertEquals("fps_12.5", t.generate()); + t = new Transformation().fps("12"); + assertEquals("fps_12", t.generate()); + t = new Transformation().fps("12-25.6"); + assertEquals("fps_12-25.6", t.generate()); + + } + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { @@ -1073,4 +1086,6 @@ public static Map getUrlParameters(URI uri) throws UnsupportedEn } return params; } + + } From 6a0815a53884efa722c3b13b8f7ffaa8db062cf6 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 25 Apr 2017 17:05:19 +0300 Subject: [PATCH 259/592] Version 1.11.0 --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a868b93..5fb3a9dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +1.11.0 / 2017-04-25 +=================== + +New functionality +----------------- + + * Add `fps` transformation parameter. + 1.10.0 / 2017-04-02 =================== diff --git a/README.md b/README.md index 39369652..25b3ff82 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.10.0 + 1.11.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.10.0/cloudinary-core-1.10.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.10.0/cloudinary-http44-1.10.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.11.0/cloudinary-core-1.11.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.11.0/cloudinary-http44-1.11.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away From 9251eab1f0e0dbff3cc86d67859bf6bd18a807ff Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 26 Apr 2017 10:45:36 +0300 Subject: [PATCH 260/592] Fix multipart callback and improve test (#77) --- .../main/java/com/cloudinary/test/UploaderTest.java | 11 +++++------ .../java/com/cloudinary/android/UploaderStrategy.java | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java index 2d07aef1..271e0d2b 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java @@ -91,22 +91,21 @@ public void testUploadProgressCallback() throws Exception { if (cloudinary.config.apiSecret == null) return; - final CountDownLatch signal = new CountDownLatch(1); final long totalLength = getAssetFileSize(TEST_IMAGE); + final long[] totalUploaded = new long[]{0}; ProgressCallback progressCallback = new ProgressCallback() { @Override public void onProgress(long bytesUploaded, long totalBytes) { - if (bytesUploaded == totalLength) { - signal.countDown(); - } + totalUploaded[0] += bytesUploaded; } }; JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true), progressCallback)); - signal.await(5, TimeUnit.SECONDS); - assertEquals(signal.getCount(), 0); + assertTrue("ProgressCallback was never called", totalUploaded[0] > 0); + assertEquals("ProgressCallback calls do not sum up to actual file length", totalLength, totalUploaded[0]); + assertEquals(result.getLong("width"), 241L); assertEquals(result.getLong("height"), 51L); assertNotNull(result.get("colors")); diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index bba83348..9c33ebdb 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -63,7 +63,7 @@ public void totalBytesLoaded(long bytes) { HttpURLConnection connection; try { - multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers")); + multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers"), multipartCallback); // Remove blank parameters for (Map.Entry param : params.entrySet()) { From 63568d582f6021743cb3471d7174b8aba1c09921 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 26 Apr 2017 11:48:31 +0300 Subject: [PATCH 261/592] [maven-release-plugin] prepare release 1.11.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 542b4f5b..70c664d5 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 com.cloudinary cloudinary-android-test - 1.9.2-SNAPSHOT + 1.11.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.9.2-SNAPSHOT + 1.11.0
com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index c1e48783..94e40151 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 23c5524c..0bc7237d 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 5753d3cb..d80ec118 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 23735258..b4cefc5e 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 0b6ed722..d5dbf28f 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index dfc424ed..f9ebe719 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index a5bf8998..5f2bc404 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index 4603751a..b053b608 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.9.2-SNAPSHOT + 1.11.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.11.0 From 6bbae58e8645c6b10aba91dd0f6de6b5bb189f13 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 26 Apr 2017 11:48:38 +0300 Subject: [PATCH 262/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 70c664d5..2e3d1f31 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.11.0 + 1.11.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.11.0 + 1.11.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index 94e40151..f7ae7099 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 0bc7237d..65c1c56b 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index d80ec118..8c4b13c3 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index b4cefc5e..d9320698 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index d5dbf28f..0c102aea 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index f9ebe719..b52dccdc 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 5f2bc404..cb5619d0 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index b053b608..a9ac309f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.11.0 + 1.11.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.11.0 + HEAD From c8135b553459500939d8dc21fea61d645d61c4cb Mon Sep 17 00:00:00 2001 From: daniel cohen Date: Thu, 23 Mar 2017 14:06:06 +0200 Subject: [PATCH 263/592] Add Search API --- .../src/main/java/com/cloudinary/Api.java | 1 - .../main/java/com/cloudinary/Cloudinary.java | 4 + .../src/main/java/com/cloudinary/Search.java | 71 ++++++++++++ .../main/java/com/cloudinary/Uploader.java | 17 ++- .../com/cloudinary/utils/ObjectUtils.java | 10 ++ .../com/cloudinary/http42/ApiStrategy.java | 33 +++--- .../java/com/cloudinary/test/SearchTest.java | 4 + .../com/cloudinary/http43/ApiStrategy.java | 27 +++-- .../java/com/cloudinary/test/SearchTest.java | 4 + .../com/cloudinary/http44/ApiStrategy.java | 36 +++++-- .../java/com/cloudinary/test/SearchTest.java | 4 + .../com/cloudinary/test/AbstractApiTest.java | 6 +- .../cloudinary/test/AbstractSearchTest.java | 101 ++++++++++++++++++ 13 files changed, 271 insertions(+), 47 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/Search.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/SearchTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/SearchTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/SearchTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 6f97de4a..687c2541 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -9,7 +9,6 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONArray; -import com.cloudinary.utils.StringUtils; @SuppressWarnings({"rawtypes", "unchecked"}) public class Api { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index d8aef295..a56b55a5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -56,6 +56,10 @@ public Api api() { return new Api(this, apiStrategy); } + public Search search() { + return new Search(this); + } + public static void registerUploaderStrategy(String className) { if (!UPLOAD_STRATEGIES.contains(className)) { UPLOAD_STRATEGIES.add(className); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java new file mode 100644 index 00000000..94cd222a --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -0,0 +1,71 @@ +package com.cloudinary; + +import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class Search { + + private Cloudinary cloudinary; + private ArrayList> sortByParam; + private ArrayList aggregateParam; + private ArrayList withFieldParam; + private HashMap params; + + Search(Cloudinary cloudinary) { + this.cloudinary = cloudinary; + this.params = new HashMap(); + this.sortByParam = new ArrayList>(); + this.aggregateParam = new ArrayList(); + this.withFieldParam = new ArrayList(); + } + + public Search expression(String value) { + this.params.put("expression", value); + return this; + } + + public Search maxResults(Integer value) { + this.params.put("max_results", value); + return this; + } + + public Search nextCursor(String value) { + this.params.put("next_cursor", value); + return this; + } + + public Search aggregate(String field) { + aggregateParam.add(field); + return this; + } + + public Search withField(String field) { + withFieldParam.add(field); + return this; + } + + public Search sortBy(String field, String dir) { + HashMap sortBucket = new HashMap(); + sortBucket.put(field, dir); + sortByParam.add(sortBucket); + return this; + } + + public HashMap toQuery() { + HashMap queryParams = new HashMap(this.params); + queryParams.put("with_field", withFieldParam); + queryParams.put("sort_by", sortByParam); + queryParams.put("aggregate", aggregateParam); + return queryParams; + } + + public ApiResponse execute() throws Exception { + Map options = ObjectUtils.asMap("content_type", "json"); + return this.cloudinary.api().callApi(Api.HttpMethod.POST, Arrays.asList("resources", "search"), this.toQuery(), options); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 85c95fb4..f49ca8e9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -1,21 +1,16 @@ package com.cloudinary; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; +import org.cloudinary.json.JSONObject; + +import java.io.*; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.cloudinary.json.JSONObject; - -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; - @SuppressWarnings({"rawtypes", "unchecked"}) public class Uploader { 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 bcb1cd97..c1922b8c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -131,6 +131,16 @@ public static Map toMap(JSONObject object) throws JSONException return map; } + public static JSONObject toJSON(Map map) throws JSONException { + JSONObject json = new JSONObject(); + for (Map.Entry entry : map.entrySet()) { + String field = entry.getKey(); + Object value = entry.getValue(); + json.put(field, value); + } + return json; + } + private static Object fromJson(Object json) throws JSONException { if (json == JSONObject.NULL) { return null; 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 07bd18ac..98c3a93b 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -8,13 +8,11 @@ import com.cloudinary.strategies.AbstractApiStrategy; import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpUriRequest; +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; @@ -44,23 +42,27 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (String single : (Iterable) param.getValue()) { - apiUrlBuilder.addParameter(param.getKey() + "[]", single); + 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())); } - } else { - apiUrlBuilder.addParameter(param.getKey(), ObjectUtils.asString(param.getValue())); } } + ClientConnectionManager connectionManager = (ClientConnectionManager) this.api.cloudinary.config.properties.get("connectionManager"); DefaultHttpClient client = new DefaultHttpClient(connectionManager); @@ -88,6 +90,11 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map uri, Map params, Map options) throws URISyntaxException, UnsupportedEncodingException { URI apiUri; - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - List parameters; HttpRequestBase request; - parameters = prepareParams(params); - if(method == HttpMethod.GET) { - apiUrlBuilder.setParameters(parameters); + + 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 { @@ -152,7 +157,7 @@ private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map uri, Map uri, Map params, Map options) throws URISyntaxException, UnsupportedEncodingException { URI apiUri; - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - List parameters; HttpRequestBase request; - parameters = ApiUtils.prepareParams(params); - if(method == HttpMethod.GET) { - apiUrlBuilder.setParameters(parameters); + + 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 - parameters.add(new BasicNameValuePair("_method", "delete")); + paramsCopy.put("_method", "delete"); //continue with POST case POST: request = new HttpPost(apiUri); @@ -156,11 +165,16 @@ private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map resources = (List) result.get("resources"); assertEquals(2,resources.size()); result = api.resourcesByContext("test-key","alt", ObjectUtils.emptyMap()); - + resources = (List) result.get("resources"); assertEquals(1,resources.size()); } @@ -834,4 +834,4 @@ public void testUpdateResourcesAccessModeByTag() throws Exception { assertEquals(resource.get("access_mode"), "public"); cloudinary.uploader().destroy(publicId, null); } -} +} \ No newline at end of file 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 new file mode 100644 index 00000000..02683815 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java @@ -0,0 +1,101 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONObject; +import org.junit.*; +import org.junit.rules.TestName; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.junit.Assume.*; + +@SuppressWarnings({"rawtypes", "unchecked", "JavaDoc"}) +abstract public class AbstractSearchTest extends MockableTest { + @Rule + public TestName currentTest = new TestName(); + private static final String SEARCH_TAG = "search_test"; + private static final String API_TEST = "api_test_" + SUFFIX; + private static final String API_TEST_1 = API_TEST + "_1"; + private static final String API_TEST_2 = API_TEST + "_2"; + + @BeforeClass + public static void setUpClass() throws Exception { + Cloudinary cloudinary = new Cloudinary(); + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=in_review"); + cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options = ObjectUtils.asMap("public_id", API_TEST_1, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=new"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options = ObjectUtils.asMap("public_id", API_TEST_2, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=validated"); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + try { + Thread.sleep(3000); //wait for search indexing + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + Api api = MockableTest.cleanUp(); + Cloudinary cloudinary = new Cloudinary(); + cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); + } + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + } + + @Test + public void shouldFindResourcesByTag() throws Exception { + Map result = cloudinary.search().expression(String.format("tags:%s", SEARCH_TAG)).execute(); + List resources = (List) result.get("resources"); + assertEquals(3, resources.size()); + } + + @Test + public void shouldFindResourceByPublicId() throws Exception { + Map result = cloudinary.search().expression(String.format("public_id:%s", API_TEST_1)).execute(); + List resources = (List) result.get("resources"); + assertEquals(1, resources.size()); + } + + @Test + public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() throws Exception { + List resources; + Map result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc").execute(); + resources = (List) result.get("resources"); + + assertEquals(1, resources.size()); + assertEquals(3, result.get("total_count")); + assertEquals(API_TEST, resources.get(0).get("public_id")); + + + result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc") + .nextCursor(ObjectUtils.asString(result.get("next_cursor"))).execute(); + resources = (List) result.get("resources"); + + assertEquals(1, resources.size()); + assertEquals(3, result.get("total_count")); + assertEquals(API_TEST_1, resources.get(0).get("public_id")); + + result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc") + .nextCursor(ObjectUtils.asString(result.get("next_cursor"))).execute(); + resources = (List) result.get("resources"); + + assertEquals(1, resources.size()); + assertEquals(3, result.get("total_count")); + assertEquals(API_TEST_2, resources.get(0).get("public_id")); + assertNull(result.get("next_cursor")); + + + } +} From a541b9a586dfab45a75cb73bcd78d8c0f30006be Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 May 2017 12:18:52 +0300 Subject: [PATCH 264/592] Version 1.12.0 --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb3a9dc..553e6d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +1.12.0 / 2017-05-01 +=================== + +New functionality +----------------- + + * Add Search API + 1.11.0 / 2017-04-25 =================== diff --git a/README.md b/README.md index 25b3ff82..24a7596b 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.11.0 + 1.12.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.11.0/cloudinary-core-1.11.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.11.0/cloudinary-http44-1.11.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.12.0/cloudinary-core-1.12.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.12.0/cloudinary-http44-1.12.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index a56b55a5..dc266ef2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.11.0"; + public final static String VERSION = "1.12.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; From 2cf0672c8c442555cba1241cccc080c401773374 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 May 2017 13:36:09 +0300 Subject: [PATCH 265/592] [maven-release-plugin] prepare release 1.12.0 --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 2e3d1f31..156cfe8e 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 com.cloudinary cloudinary-android-test - 1.11.1-SNAPSHOT + 1.12.0 apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.11.1-SNAPSHOT + 1.12.0 com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index f7ae7099..d52df62c 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index 65c1c56b..fa2a2125 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index 8c4b13c3..f98f7c17 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index d9320698..4a5cd0e6 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 0c102aea..59f27f58 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index b52dccdc..adf89aaa 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index cb5619d0..0ec264b8 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 cloudinary-test-common diff --git a/pom.xml b/pom.xml index a9ac309f..00173c61 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.11.1-SNAPSHOT + 1.12.0 pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - HEAD + 1.12.0 From e10f4551cbaf9ecb72ad5cbc682117d6ad4c9bb0 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 1 May 2017 13:36:18 +0300 Subject: [PATCH 266/592] [maven-release-plugin] prepare for next development iteration --- cloudinary-android-test/pom.xml | 6 +++--- cloudinary-android/pom.xml | 2 +- cloudinary-core/pom.xml | 2 +- cloudinary-http42/pom.xml | 2 +- cloudinary-http43/pom.xml | 2 +- cloudinary-http44/pom.xml | 2 +- cloudinary-taglib/pom.xml | 2 +- cloudinary-test-common/pom.xml | 2 +- pom.xml | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 156cfe8e..887c36ba 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -5,12 +5,12 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT com.cloudinary cloudinary-android-test - 1.12.0 + 1.12.1-SNAPSHOT apk Cloudinary Android Test Project @@ -19,7 +19,7 @@ com.cloudinary cloudinary-android jar - 1.12.0 + 1.12.1-SNAPSHOT com.google.android diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml index d52df62c..9b37e2f0 100644 --- a/cloudinary-android/pom.xml +++ b/cloudinary-android/pom.xml @@ -10,7 +10,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-android diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml index fa2a2125..17327b97 100644 --- a/cloudinary-core/pom.xml +++ b/cloudinary-core/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-core diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml index f98f7c17..ddac49e5 100644 --- a/cloudinary-http42/pom.xml +++ b/cloudinary-http42/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-http42 diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml index 4a5cd0e6..4a1db79a 100644 --- a/cloudinary-http43/pom.xml +++ b/cloudinary-http43/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-http43 diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml index 59f27f58..7309b471 100644 --- a/cloudinary-http44/pom.xml +++ b/cloudinary-http44/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-http44 diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml index adf89aaa..37e3ce93 100644 --- a/cloudinary-taglib/pom.xml +++ b/cloudinary-taglib/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-taglib diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml index 0ec264b8..dc408bcb 100644 --- a/cloudinary-test-common/pom.xml +++ b/cloudinary-test-common/pom.xml @@ -4,7 +4,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT cloudinary-test-common diff --git a/pom.xml b/pom.xml index 00173c61..486caf99 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.cloudinary cloudinary-parent - 1.12.0 + 1.12.1-SNAPSHOT pom Cloudinary Java Client Library Parent Project @@ -52,7 +52,7 @@ scm:git:git://github.com/cloudinary/cloudinary_java.git scm:git:git@github.com:cloudinary/cloudinary_java.git http://github.com/cloudinary/cloudinary_java - 1.12.0 + HEAD From 5c64a5dd013caeffd4af75bdd0b80a5fe089fed6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Fri, 5 May 2017 17:03:53 +0300 Subject: [PATCH 267/592] Add support for fetch overlay/underlay (#69) --- .../cloudinary/transformation/FetchLayer.java | 25 +++++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 6 ++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java new file mode 100644 index 00000000..9c588a79 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java @@ -0,0 +1,25 @@ +package com.cloudinary.transformation; + +import com.cloudinary.utils.Base64Coder; + +public class FetchLayer extends AbstractLayer { + + public FetchLayer() { + this.type = "fetch"; + } + + public FetchLayer url(String remoteUrl) { + this.publicId = Base64Coder.encodeString(remoteUrl); + return this; + } + + @Override + public FetchLayer type(String type) { + throw new UnsupportedOperationException("Cannot modify type for fetch layers"); + } + + @Override + FetchLayer getThis() { + return this; + } +} 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 934dcffb..9f979d67 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -988,7 +988,11 @@ public void testOverlayOptions() { "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), - "subtitles:Arial_40:sample_sub_he.srt"}; + "subtitles:Arial_40:sample_sub_he.srt", + new FetchLayer().url("https://test").resourceType("image"), + "fetch:aHR0cHM6Ly90ZXN0", + new FetchLayer().url("https://test"), + "fetch:aHR0cHM6Ly90ZXN0"}; for (int i = 0; i < tests.length; i += 2) { Object layer = tests[i]; From 04914ae6424e4cdd8ae37efbdef356fab4d37af1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Fri, 5 May 2017 17:05:56 +0300 Subject: [PATCH 268/592] Add `type` parameter to `Api.publishResource()` (#73) --- .../src/main/java/com/cloudinary/Api.java | 2 +- .../com/cloudinary/test/AbstractApiTest.java | 29 +++++++++++++++++++ 2 files changed, 30 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 687c2541..1c3b9b2a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -330,7 +330,7 @@ private ApiResponse publishResource(String byKey, Object value, Map options) thr uri.add("publish_resources"); Map params = new HashMap(); params.put(byKey, value); - params.putAll(ObjectUtils.only(options, "invalidate", "overwrite")); + params.putAll(ObjectUtils.only(options, "invalidate", "overwrite", "type")); return callApi(HttpMethod.POST, uri, 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 01868610..a7219a89 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 @@ -762,6 +762,35 @@ public void testPublishByIds() throws Exception { cloudinary.uploader().destroy(publicId, null); } + @Test + public void testPublishWithType() throws Exception { + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "type", "authenticated")); + String publicId = (String) response.get("public_id"); + + // publish with wrong type - verify publish fails + response = cloudinary.api().publishByIds(Arrays.asList(publicId), ObjectUtils.asMap("type", "private")); + List published = (List) response.get("published"); + List failed = (List) response.get("failed"); + assertNotNull(published); + assertNotNull(failed); + assertEquals(published.size(), 0); + assertEquals(failed.size(), 1); + + // publish with correct type - verify publish succeeds + response = cloudinary.api().publishByIds(Arrays.asList(publicId), ObjectUtils.asMap("type", "authenticated")); + published = (List) response.get("published"); + failed = (List) response.get("failed"); + assertNotNull(published); + assertNotNull(failed); + assertEquals(published.size(), 1); + assertEquals(failed.size(), 0); + + Map resource = (Map) published.get(0); + assertEquals(resource.get("public_id"), publicId); + assertNotNull(resource.get("url")); + cloudinary.uploader().destroy(publicId, null); + } + @Test public void testPublishByPrefix() throws Exception { Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "type", "authenticated")); From a5d95950152be44658ce780825ff64c79bb47257 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Fri, 5 May 2017 17:10:56 +0300 Subject: [PATCH 269/592] Add url_suffix support for private images. (#76) --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 6 +++++- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index b8867ba9..96d3583e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -449,15 +449,19 @@ public String finalizeResourceType(String resourceType, String type, String urlS if (type == null) { type = "upload"; } + if (!StringUtils.isBlank(urlSuffix)) { if (resourceType.equals("image") && type.equals("upload")) { resourceType = "images"; type = null; + } else if (resourceType.equals("image") && type.equals("private")) { + resourceType = "private_images"; + type = null; } else if (resourceType.equals("raw") && type.equals("upload")) { resourceType = "files"; type = null; } else { - throw new IllegalArgumentException("URL Suffix only supported for image/upload and raw/upload"); + throw new IllegalArgumentException("URL Suffix only supported for image/upload, image/private and raw/upload"); } } if (useRootPath) { 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 9f979d67..8690e1a0 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -228,6 +228,11 @@ public void testSupportUrlSuffixForRawUploads() { assertEquals("http://test123-res.cloudinary.com/files/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); + } @Test(expected = IllegalArgumentException.class) public void testDisllowUseRootPathInSharedDistribution() { cloudinary.url().useRootPath(true).generate("test"); From 43b582184ef2bc71b70f9977a8486c138d95165e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Fri, 5 May 2017 17:11:43 +0300 Subject: [PATCH 270/592] Add support for `format` in Responsive breakpoints transformation. (#78) --- .../src/main/java/com/cloudinary/ResponsiveBreakpoint.java | 4 +++- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java index 670cbf51..35b063ae 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java @@ -2,6 +2,8 @@ import org.cloudinary.json.JSONObject; +import java.util.Collections; + public class ResponsiveBreakpoint extends JSONObject { public ResponsiveBreakpoint() { put("create_derived", true); @@ -21,7 +23,7 @@ public Transformation transformation() { } public ResponsiveBreakpoint transformation(Transformation transformation) { - put("transformation", transformation); + put("transformation", Util.buildEager(Collections.singletonList(transformation))); return this; } 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 b2bf00cf..64768983 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 @@ -462,7 +462,7 @@ public void testFilenameOption() throws Exception { @Test public void testResponsiveBreakpoints() throws Exception { - ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false); + ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false).transformation(new EagerTransformation().format("gif").effect("sepia")); // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", @@ -471,6 +471,7 @@ public void testResponsiveBreakpoints() throws Exception { java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); assertEquals(2, breakpoints.size()); + assertTrue(((Map)breakpoints.get(0)).get("url").toString().endsWith("gif")); // an array of breakpoints result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", From 51fcd438a67bd6cd9ce61196765b85a04f4ebd50 Mon Sep 17 00:00:00 2001 From: Elias Ojala Date: Sat, 6 May 2017 18:28:06 +0300 Subject: [PATCH 271/592] Improved formatting of markdown --- README.md | 86 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 24a7596b..f222b8b2 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,14 @@ For Java, Cloudinary provides a library for simplifying the integration even fur The cloudinary_java library is available in [Maven Central](https://repo1.maven.org/maven2/com/cloudinary/). To use it, add the following dependency to your pom.xml : - - com.cloudinary - cloudinary-http44 - 1.12.0 - +```xml + + com.cloudinary + cloudinary-http44 + 1.12.0 + +``` + Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.12.0/cloudinary-core-1.12.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.12.0/cloudinary-http44-1.12.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. @@ -80,16 +83,19 @@ Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done eith by when initializing the Cloudinary object, or by using the CLOUDINARY_URL environment variable / system property. The entry point of the library is the Cloudinary object. - - Cloudinary cloudinary = new Cloudinary(); +```java +Cloudinary cloudinary = new Cloudinary(); +``` Here's an example of setting the configuration parameters programatically: - Map config = new HashMap(); - config.put("cloud_name", "n07t21i7"); - config.put("api_key", "123456789012345"); - config.put("api_secret", "abcdeghijklmnopqrstuvwxyz12"); - Cloudinary cloudinary = new Cloudinary(config); +```java +Map config = new HashMap(); +config.put("cloud_name", "n07t21i7"); +config.put("api_key", "123456789012345"); +config.put("api_secret", "abcdeghijklmnopqrstuvwxyz12"); +Cloudinary cloudinary = new Cloudinary(config); +``` Another example of setting the configuration parameters by providing the CLOUDINARY_URL value to the constructor: @@ -101,21 +107,29 @@ Any image uploaded to Cloudinary can be transformed and embedded using powerful The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: - cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg"); +```java +cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg"); +``` Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: - cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg"); +```java +cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg"); +``` You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. Embedding a Facebook profile to match your graphic design is very simple: - cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg"); - +```java +cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg"); +``` + Same goes for Twitter: - cloudinary.url().type("twitter_name").generate("billclinton.jpg"); +```java +cloudinary.url().type("twitter_name").generate("billclinton.jpg"); +``` ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_manipulation) for more information about displaying and transforming images in Java**. @@ -124,22 +138,28 @@ Same goes for Twitter: Assuming you have your Cloudinary configuration parameters defined (`cloud_name`, `api_key`, `api_secret`), uploading to Cloudinary is very simple. The following example uploads a local JPG to the cloud: - - cloudinary.uploader().upload("my_picture.jpg", ObjectUtils.emptyMap()); + +```java +cloudinary.uploader().upload("my_picture.jpg", ObjectUtils.emptyMap()); +``` The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: - cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg"); - - # http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg +```java +cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg"); + +# http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg +``` You can also specify your own public ID: - - cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")); - cloudinary.url().generate("sample_remote.jpg"); +```java +cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")); + +cloudinary.url().generate("sample_remote.jpg"); - # http://res.cloudinary.com/demo/image/upload/sample_remote.jpg +# http://res.cloudinary.com/demo/image/upload/sample_remote.jpg +``` ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. @@ -149,9 +169,11 @@ Returns an html image tag pointing to Cloudinary. Usage: - cloudinary.url().format("png").transformation(new Transformation().width(100).height(100).crop("fill")).imageTag("sample"); +```java +cloudinary.url().format("png").transformation(new Transformation().width(100).height(100).crop("fill")).imageTag("sample"); - # +# +``` ### imageUploadTag @@ -159,9 +181,11 @@ Returns an html input field for direct image upload, to be used in conjunction w Usage: - Map options = ObjectUtils.asMap("resource_type", "auto"); - Map htmlOptions = ObjectUtils.asMap("alt", "sample"); - String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); +```java +Map options = ObjectUtils.asMap("resource_type", "auto"); +Map htmlOptions = ObjectUtils.asMap("alt", "sample"); +String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); +``` ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. From c2fbf47b1e4d6c6e756a514871d6f8243a113524 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 27 Apr 2017 14:14:36 +0300 Subject: [PATCH 272/592] Fix test to handle non-deterministic set order. --- cloudinary-core/src/test/java/com/cloudinary/UtilTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java index 0f6b08fb..bdae440a 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -15,7 +15,8 @@ public class UtilTest { public void encodeContext() throws Exception { Map context = ObjectUtils.asMap("caption", "different = caption", "alt2", "alt|alternative"); String result = Util.encodeContext(context); - assertEquals("caption=different \\= caption|alt2=alt\\|alternative", result); + assertTrue("caption=different \\= caption|alt2=alt\\|alternative".equals(result) || + "alt2=alt\\|alternative|caption=different \\= caption".equals(result)); } } \ No newline at end of file From 54bcb33087c718adc73040cc1c12a1ecc06c8f5a Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 27 Apr 2017 14:17:04 +0300 Subject: [PATCH 273/592] Fix android test configuration - copy and filter `AndroidManifest.xml`. --- cloudinary-android-test/pom.xml | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml index 887c36ba..525b4f7f 100644 --- a/cloudinary-android-test/pom.xml +++ b/cloudinary-android-test/pom.xml @@ -53,6 +53,46 @@ true + + maven-antrun-plugin + 1.8 + + + copy + validate + + + + + + + run + + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + validate + + replace + + + + + ${basedir}/src/main/AndroidManifest.xml + + + %CLOUDINARY_URL% + ${env.CLOUDINARY_URL} + + + + From bda630ad6383d349e2af72b99e8c2d4332b825a4 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 27 Apr 2017 14:17:34 +0300 Subject: [PATCH 274/592] Integrate travis. --- .travis.yml | 11 +++++++++++ pom.xml | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..be01f315 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: android + +jdk: + - oraclejdk8 + - oraclejdk7 + - openjdk7 + +# Temporarily disabled, test fail because Hamcrest needs java 1.7 (probably) +# - openjdk6 + +script: mvn test -B -Dtest=\!*#*Timeout* -DfailIfNoTests=false diff --git a/pom.xml b/pom.xml index 486caf99..16048500 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + maven-compiler-plugin 3.0 From 9be7f5d125fbd12d1fa58aa9dc64db160098eae3 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 May 2017 12:59:41 +0300 Subject: [PATCH 275/592] Use travis Job id in upload suffix to enable parallel testing. --- .../src/main/java/com/cloudinary/test/MockableTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 53203926..151b774e 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -3,6 +3,7 @@ import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.IOException; @@ -13,7 +14,7 @@ public class MockableTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; - protected static final String SUFFIX = String.valueOf(new Random().nextInt(99999)); + protected static String SUFFIX = StringUtils.isNotBlank(System.getenv("TRAVIS_JOB_ID")) ? System.getenv("TRAVIS_JOB_ID") : String.valueOf(new Random().nextInt(99999)); protected static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; protected static final String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); protected Cloudinary cloudinary; From 0a74d81511869aa8795cccbfcbe5cbe8f0bab7c0 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 May 2017 14:42:51 +0300 Subject: [PATCH 276/592] Make ids and tags unique (based on Travis Job-id) to allow for parallel test runs. --- .../com/cloudinary/test/AbstractApiTest.java | 50 +++++++++---------- .../cloudinary/test/AbstractUploaderTest.java | 6 +-- 2 files changed, 27 insertions(+), 29 deletions(-) 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 a7219a89..12f915b8 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 @@ -5,21 +5,17 @@ import com.cloudinary.Coordinates; import com.cloudinary.Transformation; import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.RateLimit; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.equalTo; import org.junit.*; -import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import java.io.IOException; import java.util.*; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.AllOf.allOf; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.*; @@ -42,6 +38,7 @@ abstract public class AbstractApiTest extends MockableTest { public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, uniqueTag}; public static final String EXPLICIT_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + ",w_100"; public static final Transformation EXPLICIT_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX).fontFamily("Arial").fontSize(60)); + public static final String TEST_KEY = "test-key" + SUFFIX; protected Api api; @@ -58,15 +55,17 @@ public static void setUpClass() throws IOException { options.put("public_id", API_TEST_1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", "context_1", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "test-key=alt"); + String context1 = TEST_KEY + "=alt"; + String context2 = TEST_KEY + "=alternate"; + options = ObjectUtils.asMap("public_id", "context_1", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", "context_2", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "test-key=alternate"); + options = ObjectUtils.asMap("public_id", "context_2", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } @AfterClass public static void tearDownClass() { - Api api = MockableTest.cleanUp(); + Api api = MockableTest.cleanUp(); try { // api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); api.deleteResourcesByTag(uniqueTag, ObjectUtils.emptyMap()); @@ -102,6 +101,7 @@ public static void tearDownClass() { } } + @Rule public TestName currentTest = new TestName(); @@ -143,8 +143,8 @@ public void test02Resources() throws Exception { Map result = api.resources(ObjectUtils.asMap("max_results", 500, "next_cursor", next_cursor)); resources.addAll((List) result.get("resources")); next_cursor = (String) result.get("next_cursor"); - } while (next_cursor != null ); - assertThat(resources, hasItem(allOf(hasEntry("public_id", (String) resource.get("public_id")),hasEntry("type", "upload")))); + } while (next_cursor != null); + assertThat(resources, hasItem(allOf(hasEntry("public_id", (String) resource.get("public_id")), hasEntry("type", "upload")))); } @Test @@ -183,9 +183,9 @@ public void test05ResourcesByPrefix() throws Exception { assertThat(resources, hasItem(hasEntry("public_id", (Object) API_TEST))); assertThat(resources, hasItem(hasEntry("public_id", (Object) API_TEST_1))); // resources = (List>) result.get("resources"); - assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST),hasEntry("type", "upload")))); + assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST), hasEntry("type", "upload")))); assertThat(resources, hasItem(hasEntry("context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))))); - assertThat(resources, hasItem(hasEntry(equalTo("tags"), hasItem( SDK_TEST_TAG)))); + assertThat(resources, hasItem(hasEntry(equalTo("tags"), hasItem(SDK_TEST_TAG)))); } @Test @@ -338,15 +338,15 @@ public void test10Tags() throws Exception { // should allow listing tags Map result = api.tags(ObjectUtils.asMap("max_results", 500)); List tags = (List) result.get("tags"); - assertThat( tags, hasItem(SDK_TEST_TAG)); + assertThat(tags, hasItem(SDK_TEST_TAG)); } @Test public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", SDK_TEST_TAG.substring(0,SDK_TEST_TAG.length()-1))); + Map result = api.tags(ObjectUtils.asMap("prefix", SDK_TEST_TAG.substring(0, SDK_TEST_TAG.length() - 1))); List tags = (List) result.get("tags"); - assertThat( tags, hasItem(SDK_TEST_TAG)); + assertThat(tags, hasItem(SDK_TEST_TAG)); result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); tags = (List) result.get("tags"); assertEquals(0, tags.size()); @@ -429,14 +429,14 @@ public void test17aTransformationDeleteImplicit() throws Exception { @Test public void test20ResourcesContext() throws Exception { - Map result = api.resourcesByContext("test-key", ObjectUtils.emptyMap()); + Map result = api.resourcesByContext(TEST_KEY, ObjectUtils.emptyMap()); List resources = (List) result.get("resources"); - assertEquals(2,resources.size()); - result = api.resourcesByContext("test-key","alt", ObjectUtils.emptyMap()); + assertEquals(2, resources.size()); + result = api.resourcesByContext(TEST_KEY, "alt", ObjectUtils.emptyMap()); resources = (List) result.get("resources"); - assertEquals(1,resources.size()); + assertEquals(1, resources.size()); } /** @@ -497,7 +497,7 @@ public void testManualModeration() throws Exception { public void testOcrUpdate() { // should support requesting ocr info try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -509,7 +509,7 @@ public void testOcrUpdate() { public void testRawConvertUpdate() { // should support requesting raw conversion try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("raw_convert", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -521,7 +521,7 @@ public void testRawConvertUpdate() { public void testCategorizationUpdate() { // should support requesting categorization try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("categorization", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -533,7 +533,7 @@ public void testCategorizationUpdate() { public void testDetectionUpdate() { // should support requesting detection try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("detection", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -545,7 +545,7 @@ public void testDetectionUpdate() { public void testSimilaritySearchUpdate() { // should support requesting similarity search try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); } catch (Exception e) { assertTrue(e instanceof BadRequest); @@ -557,7 +557,7 @@ public void testSimilaritySearchUpdate() { public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates Coordinates coordinates = new Coordinates("121,31,110,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap( "tags", UPLOAD_TAGS)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("custom_coordinates", coordinates)); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("coordinates", true)); int[] expected = new int[]{121, 31, 110, 151}; 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 64768983..2b6fbb1a 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 @@ -4,8 +4,6 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; import org.junit.*; import org.junit.rules.TestName; @@ -201,8 +199,8 @@ public void testImageUploadTag() { @Test public void testSprite() throws IOException { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", SDK_TEST_TAG)); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); From 96c130f1a71e646edb8dc8a3b2b06097a5130267 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 May 2017 15:02:04 +0300 Subject: [PATCH 277/592] Add logs to locate error. --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 1 + 1 file changed, 1 insertion(+) 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 12f915b8..304e62fc 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 @@ -61,6 +61,7 @@ public static void setUpClass() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options = ObjectUtils.asMap("public_id", "context_2", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + System.out.println("Suffix: " + SUFFIX); } @AfterClass From 919900c49a16a9cead55cec525360b0032d2bbec Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 4 May 2017 14:13:57 +0300 Subject: [PATCH 278/592] Fix cross-test pollution issues. Add travis_job_id to public id of uploaded resources. Delete newly created streaming profiles from account after test. --- .../com/cloudinary/test/AbstractApiTest.java | 6 +-- .../AbstractStreamingProfilesApiTest.java | 44 ++++++++++++------- 2 files changed, 31 insertions(+), 19 deletions(-) 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 304e62fc..4278e3f1 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 @@ -57,11 +57,11 @@ public static void setUpClass() throws IOException { String context1 = TEST_KEY + "=alt"; String context2 = TEST_KEY + "=alternate"; - options = ObjectUtils.asMap("public_id", "context_1", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context1); + options = ObjectUtils.asMap("public_id", "context_1" + SUFFIX, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", "context_2", "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); + options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - System.out.println("Suffix: " + SUFFIX); + System.out.println("Generated tag suffix: " + SUFFIX); } @AfterClass diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java index 590100a3..db4c1440 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java @@ -8,14 +8,14 @@ import com.cloudinary.utils.ObjectUtils; import org.hamcrest.Matcher; import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; +import org.junit.*; import org.junit.rules.TestName; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; @@ -26,6 +26,9 @@ abstract public class AbstractStreamingProfilesApiTest extends MockableTest { private static final String PROFILE_NAME = "api_test_streaming_profile" + SUFFIX; protected Api api; private static final List PREDEFINED_PROFILES = Arrays.asList("4k", "full_hd", "hd", "sd", "full_hd_wifi", "full_hd_lean", "hd_lean"); + public static final String UPDATE_PROFILE_NAME = PROFILE_NAME + "_update"; + public static final String DELETE_PROFILE_NAME = PROFILE_NAME + "_delete"; + public static final String CREATE_PROFILE_NAME = PROFILE_NAME + "_create"; @BeforeClass public static void setUpClass() throws IOException { @@ -48,14 +51,13 @@ public void setUp() { @Test public void testCreate() throws Exception { - final String name = PROFILE_NAME + "_create"; - ApiResponse result = api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + ApiResponse result = api.createStreamingProfile(CREATE_PROFILE_NAME, null, Collections.singletonList(ObjectUtils.asMap( "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") )), ObjectUtils.emptyMap()); assertTrue(result.containsKey("data")); Map profile = (Map) result.get("data"); - assertThat(profile, (Matcher) hasEntry("name", (Object) name)); + assertThat(profile, (Matcher) hasEntry("name", (Object) CREATE_PROFILE_NAME)); } @Test @@ -82,31 +84,29 @@ public void testList() throws Exception { @Test public void testDelete() throws Exception { ApiResponse result; - final String name = PROFILE_NAME + "_delete"; try { - api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + api.createStreamingProfile(DELETE_PROFILE_NAME, null, Collections.singletonList(ObjectUtils.asMap( "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") )), ObjectUtils.emptyMap()); } catch (AlreadyExists ignored) { } - result = api.deleteStreamingProfile(name); + result = api.deleteStreamingProfile(DELETE_PROFILE_NAME); assertTrue(result.containsKey("data")); Map profile = (Map) result.get("data"); - assertThat(profile, (Matcher) hasEntry("name", (Object) (name))); + assertThat(profile, (Matcher) hasEntry("name", (Object) DELETE_PROFILE_NAME)); } @Test public void testUpdate() throws Exception { - final String name = PROFILE_NAME + "_update"; try { - api.createStreamingProfile(name, null, Collections.singletonList(ObjectUtils.asMap( + api.createStreamingProfile(UPDATE_PROFILE_NAME, null, Collections.singletonList(ObjectUtils.asMap( "transformation", new Transformation().crop("limit").width(1200).height(1200).bitRate("5m") )), ObjectUtils.emptyMap()); } catch (AlreadyExists ignored) { } - Map result = api.updateStreamingProfile(name, null, Collections.singletonList( + Map result = api.updateStreamingProfile(UPDATE_PROFILE_NAME, null, Collections.singletonList( ObjectUtils.asMap("transformation", new Transformation().crop("limit").width(800).height(800).bitRate("5m") )), ObjectUtils.emptyMap()); @@ -114,7 +114,7 @@ public void testUpdate() throws Exception { assertTrue(result.containsKey("data")); assertThat(result, (Matcher) hasEntry("message", (Object) "updated")); Map profile = (Map) result.get("data"); - assertThat(profile, (Matcher) hasEntry("name", (Object) (name))); + assertThat(profile, (Matcher) hasEntry("name", (Object) UPDATE_PROFILE_NAME)); assertThat(profile, Matchers.hasEntry(equalTo("representations"), (Matcher) hasItem(hasKey("transformation")))); final Map representation = (Map) ((List) profile.get("representations")).get(0); Map transformation = (Map) ((List)representation.get("transformation")).get(0); @@ -125,4 +125,16 @@ public void testUpdate() throws Exception { (Matcher) hasEntry("bit_rate", "5m") )); } + + @AfterClass + public static void tearDownClass() { + Api api = new Cloudinary().api(); + try { + api.deleteStreamingProfile(CREATE_PROFILE_NAME); + api.deleteStreamingProfile(DELETE_PROFILE_NAME); + api.deleteStreamingProfile(UPDATE_PROFILE_NAME); + } catch (Exception e) { + e.printStackTrace(); + } + } } From 9aa1efe852c1199f4e7ce4c482ccae2679923829 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 4 May 2017 15:12:41 +0300 Subject: [PATCH 279/592] Fix `deleteStreamProfile` no-options overload. --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- .../cloudinary/test/AbstractStreamingProfilesApiTest.java | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 1c3b9b2a..80ce6e4e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -434,7 +434,7 @@ public ApiResponse deleteStreamingProfile(String name, Map options) throws Excep * @see Api#deleteStreamingProfile(String, Map) */ public ApiResponse deleteStreamingProfile(String name) throws Exception { - return getStreamingProfile(name, null); + return deleteStreamingProfile(name, null); } /** diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java index db4c1440..0eec01ba 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java @@ -18,8 +18,7 @@ import java.util.Map; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; abstract public class AbstractStreamingProfilesApiTest extends MockableTest { @@ -92,10 +91,7 @@ public void testDelete() throws Exception { } result = api.deleteStreamingProfile(DELETE_PROFILE_NAME); - assertTrue(result.containsKey("data")); - Map profile = (Map) result.get("data"); - assertThat(profile, (Matcher) hasEntry("name", (Object) DELETE_PROFILE_NAME)); - + assertEquals("deleted", result.get("message")); } @Test From 74b6c09c11dd62fd370f4bb44d254274add4ac26 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 4 May 2017 15:13:12 +0300 Subject: [PATCH 280/592] Fix cross-test pollution issues (add suffix to public id). --- .../com/cloudinary/test/AbstractApiTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 4278e3f1..e4490679 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 @@ -61,7 +61,6 @@ public static void setUpClass() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - System.out.println("Generated tag suffix: " + SUFFIX); } @AfterClass @@ -285,7 +284,7 @@ public void test08DeleteDerived() throws Exception { @Test() public void testDeleteDerivedByTransformation() throws Exception { // should allow deleting resources - String public_id = "api_test_123"; + String public_id = "api_test_123" + SUFFIX; List transformations = new ArrayList(); transformations.add(new Transformation().angle(90)); transformations.add(new Transformation().width(120)); @@ -305,7 +304,7 @@ public void testDeleteDerivedByTransformation() throws Exception { @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources - String public_id = "api_,test3"; + String public_id = "api_,test3" + 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); @@ -712,35 +711,36 @@ public void testRestore() throws Exception { @Test public void testUploadMapping() throws Exception { + String aptTestUploadMapping = "api_test_upload_mapping" + SUFFIX; try { - api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + api.deleteUploadMapping(aptTestUploadMapping, ObjectUtils.emptyMap()); } catch (Exception ignored) { } - api.createUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://cloudinary.com")); - Map result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + api.createUploadMapping(aptTestUploadMapping, ObjectUtils.asMap("template", "http://cloudinary.com")); + Map result = api.uploadMapping(aptTestUploadMapping, ObjectUtils.emptyMap()); assertEquals(result.get("template"), "http://cloudinary.com"); - api.updateUploadMapping("api_test_upload_mapping", ObjectUtils.asMap("template", "http://res.cloudinary.com")); - result = api.uploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + api.updateUploadMapping(aptTestUploadMapping, ObjectUtils.asMap("template", "http://res.cloudinary.com")); + result = api.uploadMapping(aptTestUploadMapping, ObjectUtils.emptyMap()); assertEquals(result.get("template"), "http://res.cloudinary.com"); result = api.uploadMappings(ObjectUtils.emptyMap()); ListIterator mappings = ((ArrayList) result.get("mappings")).listIterator(); boolean found = false; while (mappings.hasNext()) { Map mapping = (Map) mappings.next(); - if (mapping.get("folder").equals("api_test_upload_mapping") + if (mapping.get("folder").equals(aptTestUploadMapping) && mapping.get("template").equals("http://res.cloudinary.com")) { found = true; break; } } assertTrue(found); - api.deleteUploadMapping("api_test_upload_mapping", ObjectUtils.emptyMap()); + api.deleteUploadMapping(aptTestUploadMapping, ObjectUtils.emptyMap()); result = api.uploadMappings(ObjectUtils.emptyMap()); found = false; while (mappings.hasNext()) { Map mapping = (Map) mappings.next(); - if (mapping.get("folder").equals("api_test_upload_mapping") + if (mapping.get("folder").equals(aptTestUploadMapping) && mapping.get("template").equals("http://res.cloudinary.com")) { found = true; break; From 7d394c1617acfcab7d5147ee0f7de8e1d4731017 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 4 May 2017 15:27:35 +0300 Subject: [PATCH 281/592] Cleanup logs and improve exception handling for StreamingProfile tests. --- .../AbstractStreamingProfilesApiTest.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java index 0eec01ba..6a917208 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStreamingProfilesApiTest.java @@ -5,6 +5,7 @@ import com.cloudinary.Transformation; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.AlreadyExists; +import com.cloudinary.api.exceptions.NotFound; import com.cloudinary.utils.ObjectUtils; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -113,7 +114,7 @@ public void testUpdate() throws Exception { assertThat(profile, (Matcher) hasEntry("name", (Object) UPDATE_PROFILE_NAME)); assertThat(profile, Matchers.hasEntry(equalTo("representations"), (Matcher) hasItem(hasKey("transformation")))); final Map representation = (Map) ((List) profile.get("representations")).get(0); - Map transformation = (Map) ((List)representation.get("transformation")).get(0); + Map transformation = (Map) ((List) representation.get("transformation")).get(0); assertThat(transformation, allOf( (Matcher) hasEntry("width", 800), (Matcher) hasEntry("height", 800), @@ -123,14 +124,22 @@ public void testUpdate() throws Exception { } @AfterClass - public static void tearDownClass() { + public static void tearDownClass() throws Exception { Api api = new Cloudinary().api(); try { api.deleteStreamingProfile(CREATE_PROFILE_NAME); - api.deleteStreamingProfile(DELETE_PROFILE_NAME); + } catch (NotFound ignored) { + } + + try { api.deleteStreamingProfile(UPDATE_PROFILE_NAME); - } catch (Exception e) { - e.printStackTrace(); + } catch (NotFound ignored) { + } + + try { + // this should already be gone but in case that deletion-test failed we still need to cleanup the account. + api.deleteStreamingProfile(DELETE_PROFILE_NAME); + } catch (NotFound ignored) { } } } From 03913e9af1fd46616e4dde92e0acb0c0de586991 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 11 May 2017 16:43:14 +0300 Subject: [PATCH 282/592] Rename `deleteDerivedResourcesByTransformations` to 'deleteDerivedByTransformation' --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 80ce6e4e..03ca062d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -144,7 +144,7 @@ public ApiResponse deleteResources(Iterable publicIds, Map options) thro return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } - public ApiResponse deleteDerivedResourcesByTransformations(Iterable publicIds, List transformations, Map options) throws Exception { + 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"); String type = ObjectUtils.asString(options.get("type"), "upload"); 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 e4490679..44d9d0ac 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 @@ -293,7 +293,7 @@ public void testDeleteDerivedByTransformation() throws Exception { assertNotNull(resource); List derived = ((List) resource.get("derived")); assertTrue(derived.size() == 2); - api.deleteDerivedResourcesByTransformations(ObjectUtils.asArray(public_id), ObjectUtils.asArray(transformations), ObjectUtils.emptyMap()); + api.deleteDerivedByTransformation(ObjectUtils.asArray(public_id), ObjectUtils.asArray(transformations), ObjectUtils.emptyMap()); resource = api.resource(public_id, ObjectUtils.emptyMap()); assertNotNull(resource); From cea1493bfdb0eff0854ce0f2fa8d66280699b90a Mon Sep 17 00:00:00 2001 From: Matthew Zavislak Date: Thu, 11 May 2017 13:49:59 -0700 Subject: [PATCH 283/592] Close responsestream --- .../main/java/com/cloudinary/android/UploaderStrategy.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 9c33ebdb..103d7cd4 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -115,6 +115,10 @@ public void totalBytesLoaded(long bytes) { String responseData = readFully(responseStream); connection.disconnect(); + try { + responseStream.close(); + } catch (Exception e) {} + if (code != 200 && code != 400 && code != 404 && code != 500) { throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); } From 5fe2d92b7bc795a913cec283d70cb78758ba868d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 11 Jun 2017 12:09:06 +0300 Subject: [PATCH 284/592] Add `deleteByToken` to `Uploader`. --- .../cloudinary/android/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/Uploader.java | 5 ++++- .../strategies/AbstractUploaderStrategy.java | 22 +++++++++++++++++-- .../cloudinary/http42/UploaderStrategy.java | 2 +- .../cloudinary/http43/UploaderStrategy.java | 2 +- .../cloudinary/http44/UploaderStrategy.java | 2 +- .../cloudinary/test/AbstractUploaderTest.java | 10 +++++++++ 7 files changed, 38 insertions(+), 7 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 9c33ebdb..ac9d4e85 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -45,7 +45,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - String apiUrl = this.cloudinary().cloudinaryApiUrl(action, options); + String apiUrl = buildUploadUrl(action, options); MultipartCallback multipartCallback; if (progressCallback == null) { multipartCallback = null; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index f49ca8e9..78684b73 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -109,7 +109,7 @@ public Map uploadLarge(Object file, Map options, int bufferSize) throws IOExcept return uploadLarge(file, options, bufferSize, null); } - public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallback progressCallback) throws IOException { + public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallback progressCallback) throws IOException { InputStream input; long length = -1; if (file instanceof InputStream) { @@ -510,4 +510,7 @@ public String imageUploadTag(String field, Map options, Map html return builder.toString(); } + public Map deleteByToken(String token) throws Exception { + return callApi("delete_by_token", ObjectUtils.asMap("token", token), ObjectUtils.emptyMap(), null); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index db652e96..e12947a4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -6,6 +6,8 @@ import com.cloudinary.Cloudinary; import com.cloudinary.ProgressCallback; import com.cloudinary.Uploader; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; public abstract class AbstractUploaderStrategy { protected Uploader uploader; @@ -19,9 +21,25 @@ public Cloudinary cloudinary() { } @SuppressWarnings("rawtypes") - public Map callApi(String action, Map params, Map options, Object file) throws IOException{ + public Map callApi(String action, Map params, Map options, Object file) throws IOException { return callApi(action, params, options, file, null); } public abstract Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException; -} + + protected String buildUploadUrl(String action, Map options) { + String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), + ObjectUtils.asString(uploader.cloudinary().config.uploadPrefix, "https://api.cloudinary.com")); + String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(uploader.cloudinary().config.cloudName)); + if (cloud_name == null) + throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); + + if (action.equals("delete_by_token")) { + // the only method (so far) that doesn't need resource_type + return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, action}, "/"); + } else { + String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); + return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, resource_type, action}, "/"); + } + } +} \ No newline at end of file diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index c3d311c7..dfed6846 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -50,7 +50,7 @@ public Map callApi(String action, Map params, Map options, Objec Util.clearEmpty(params); } - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + String apiUrl = buildUploadUrl(action, options); ClientConnectionManager connectionManager = (ClientConnectionManager) this.uploader.cloudinary().config.properties.get("connectionManager"); HttpClient client = new DefaultHttpClient(connectionManager); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 8cd3f3da..cd8732d6 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -73,7 +73,7 @@ public Map callApi(String action, Map params, Map options, Objec Util.clearEmpty(params); } - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + String apiUrl = buildUploadUrl(action, options); HttpPost postMethod = new HttpPost(apiUrl); ApiUtils.setTimeouts(postMethod, options); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 63df9469..617e5923 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -73,7 +73,7 @@ public Map callApi(String action, Map params, Map options, Objec Util.clearEmpty(params); } - String apiUrl = uploader.cloudinary().cloudinaryApiUrl(action, options); + String apiUrl = buildUploadUrl(action, options); HttpPost postMethod = new HttpPost(apiUrl); ApiUtils.setTimeouts(postMethod, options); 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 2b6fbb1a..8cb34461 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 @@ -78,6 +78,16 @@ public void testUtf8Upload() throws IOException { assertEquals(result.get("signature"), expected_signature); } + @Test + public void testDeleteByToken() throws Exception { + Map options = ObjectUtils.asMap("return_delete_token", true, "tags", new String[]{SDK_TEST_TAG, uniqueTag}); + Map res = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + String token = (String) res.get("delete_token"); + res = cloudinary.uploader().deleteByToken(token); + assertNotNull(res); + assertEquals("ok", res.get("result")); + } + @Test public void testUpload() throws IOException { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", SDK_TEST_TAG)); From 61c985ed8b61205e19abe1d7e0f4133305558889 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 13 Apr 2017 17:26:13 +0300 Subject: [PATCH 285/592] Add support for serialization to enable persisting request parameters. --- .../main/java/com/cloudinary/Coordinates.java | 3 +- .../java/com/cloudinary/Transformation.java | 3 +- .../transformation/AbstractLayer.java | 3 +- .../com/cloudinary/utils/ObjectUtils.java | 28 +++++++++++++------ .../java/com/cloudinary/utils/Rectangle.java | 4 ++- .../java/org/cloudinary/json/JSONArray.java | 3 +- .../java/org/cloudinary/json/JSONObject.java | 3 +- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java index 92749cfa..2c025e6d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Coordinates.java @@ -1,12 +1,13 @@ package com.cloudinary; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import com.cloudinary.utils.Rectangle; import com.cloudinary.utils.StringUtils; -public class Coordinates { +public class Coordinates implements Serializable{ Collection coordinates = new ArrayList(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 17c7359c..37dbe42f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -1,5 +1,6 @@ package com.cloudinary; +import java.io.Serializable; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -11,7 +12,7 @@ import com.cloudinary.utils.StringUtils; @SuppressWarnings({"rawtypes", "unchecked"}) -public class Transformation { +public class Transformation implements Serializable{ public static final String VAR_NAME_RE = "^\\$[a-zA-Z][a-zA-Z0-9]+$"; protected Map transformation; protected List transformations; diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java index b4ef45ef..513b10d7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java @@ -1,10 +1,11 @@ package com.cloudinary.transformation; +import java.io.Serializable; import java.util.ArrayList; import com.cloudinary.utils.StringUtils; -public abstract class AbstractLayer> { +public abstract class AbstractLayer> implements Serializable{ abstract T getThis(); protected String resourceType = null; 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 c1922b8c..0984ba6e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -1,18 +1,12 @@ package com.cloudinary.utils; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import org.cloudinary.json.JSONArray; import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; +import java.io.*; +import java.util.*; + public class ObjectUtils { @@ -32,6 +26,22 @@ public static String asString(Object value, String defaultValue) { } } + public static String serialize(Object object) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos); + try { + objectOutputStream.writeObject(object); + return new String(Base64Coder.encode(baos.toByteArray())); + } finally { + objectOutputStream.close(); + } + } + + public static Object deserialize(String base64SerializedString) throws IOException, ClassNotFoundException { + byte[] buf = Base64Coder.decode(base64SerializedString); + return new ObjectInputStream(new ByteArrayInputStream(buf)).readObject(); + } + @SuppressWarnings({"rawtypes", "unchecked"}) public static List asArray(Object value) { if (value == null) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java index 6c44ab51..698af43e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java @@ -1,6 +1,8 @@ package com.cloudinary.utils; -public class Rectangle { +import java.io.Serializable; + +public class Rectangle implements Serializable{ public int height; public int width; diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java index d0d6090a..82f7d19b 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONArray.java @@ -25,6 +25,7 @@ of this software and associated documentation files (the "Software"), to deal */ import java.io.IOException; +import java.io.Serializable; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Array; @@ -77,7 +78,7 @@ of this software and associated documentation files (the "Software"), to deal * @author JSON.org * @version 2014-05-03 */ -public class JSONArray { +public class JSONArray implements Serializable { /** * The arrayList where the JSONArray's properties are kept. diff --git a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java index 9ca1012b..ef0e0b2d 100644 --- a/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java +++ b/cloudinary-core/src/main/java/org/cloudinary/json/JSONObject.java @@ -25,6 +25,7 @@ of this software and associated documentation files (the "Software"), to deal */ import java.io.IOException; +import java.io.Serializable; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; @@ -93,7 +94,7 @@ of this software and associated documentation files (the "Software"), to deal * @author JSON.org * @version 2014-05-03 */ -public class JSONObject { +public class JSONObject implements Serializable{ /** * JSONObject.NULL is equivalent to the value that JavaScript calls null, * whilst Java's null is equivalent to the value that JavaScript calls From a6303bacd7b046d208462aa5701a393690d1ccce Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 May 2017 12:45:07 +0300 Subject: [PATCH 286/592] Parallelize tests. Configure surefire to run test classes in parallel. Fix `AbstractSearchTest` to handle parallel tests. --- .../java/com/cloudinary/test/AbstractSearchTest.java | 9 ++++----- pom.xml | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) 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 02683815..ffe5606b 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 @@ -3,22 +3,21 @@ import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; -import org.cloudinary.json.JSONObject; import org.junit.*; import org.junit.rules.TestName; -import java.io.IOException; import java.util.List; import java.util.Map; -import static org.junit.Assert.*; -import static org.junit.Assume.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNotNull; @SuppressWarnings({"rawtypes", "unchecked", "JavaDoc"}) abstract public class AbstractSearchTest extends MockableTest { @Rule public TestName currentTest = new TestName(); - private static final String SEARCH_TAG = "search_test"; + private static final String SEARCH_TAG = "search_test" + SUFFIX; private static final String API_TEST = "api_test_" + SUFFIX; private static final String API_TEST_1 = API_TEST + "_1"; private static final String API_TEST_2 = API_TEST + "_2"; diff --git a/pom.xml b/pom.xml index 16048500..d3745ace 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,10 @@ org.apache.maven.plugins maven-surefire-plugin 2.20 + + 3C + true + maven-compiler-plugin From 8c7032b1ab54123a60db77c957ed7ab21144c6a7 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 May 2017 14:10:27 +0300 Subject: [PATCH 287/592] Adapt uploader tests to parallel test runs . --- .../cloudinary/test/AbstractUploaderTest.java | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) 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 8cb34461..6991e62e 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 @@ -22,7 +22,8 @@ @SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractUploaderTest extends MockableTest { - private static final String ARCHIVE_TAG = "archive_tag_" + String.valueOf(System.currentTimeMillis()); + private static final String ARCHIVE_TAG = SDK_TEST_TAG + "_archive"; + private static final String UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; @@ -33,10 +34,10 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); } - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG})); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, "resource_type", "raw")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "resource_type", "raw")); cloudinary.uploader().upload(SRC_TEST_IMAGE, - asMap("tags", new String [] {SDK_TEST_TAG, ARCHIVE_TAG}, + asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "transformation", new Transformation().crop("scale").width(10))); } @@ -49,7 +50,7 @@ public static void tearDownClass() { } catch (Exception ignored) { } try { - api.deleteResourcesByTag(SDK_TEST_TAG, ObjectUtils.emptyMap()); + api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.emptyMap()); } catch (Exception ignored) { } } @@ -66,7 +67,8 @@ public void setUp() { @Test public void testUtf8Upload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", SDK_TEST_TAG, "public_id", "aåßéƒ")); + + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG), "public_id", "aåßéƒ")); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); @@ -90,7 +92,7 @@ public void testDeleteByToken() throws Exception { @Test public void testUpload() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("colors", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); assertNotNull(result.get("colors")); @@ -104,7 +106,7 @@ public void testUpload() throws IOException { @Test public void testUploadUrl() throws IOException { - Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); Map to_sign = new HashMap(); @@ -116,7 +118,7 @@ public void testUploadUrl() throws IOException { @Test public void testUploadDataUri() throws IOException { - Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("width"), 16); assertEquals(result.get("height"), 16); Map to_sign = new HashMap(); @@ -128,37 +130,37 @@ public void testUploadDataUri() throws IOException { @Test public void testUploadUTF8() throws IOException { - Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", asMap("public_id", "Plattenkreiss_ñg-é", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/old_logo.png", asMap("public_id", "Plattenkreiss_ñg-é", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("public_id"), "Plattenkreiss_ñg-é"); - cloudinary.uploader().upload(result.get("url"), asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(result.get("url"), asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } @Test public void testRename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); Object publicId = result.get("public_id"); String publicId2 = "folder/" + publicId + "2"; cloudinary.uploader().rename((String) publicId, publicId2, ObjectUtils.emptyMap()); assertNotNull(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap())); - Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", asMap("tags", SDK_TEST_TAG)); + Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); boolean error_found=false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch(Exception e) { error_found=true; } assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("overwrite", true, "tags", SDK_TEST_TAG)); + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("overwrite", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap()).get("format"), "ico"); } @Test public void testUniqueFilename() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertTrue(((String) result.get("public_id")).matches("old_logo_[a-z0-9]{6}")); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "unique_filename", false, "tags", SDK_TEST_TAG)); + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "unique_filename", false, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("public_id"), "old_logo"); } @@ -173,7 +175,7 @@ public void testExplicit() throws IOException { @Test public void testEager() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } @Test @@ -184,13 +186,13 @@ public void testUploadAsync() throws IOException { @Test public void testHeaders() throws IOException { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", new String[]{"Link: 1"}, "tags", SDK_TEST_TAG)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", asMap("Link", "1"), "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", new String[]{"Link: 1"}, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("headers", asMap("Link", "1"), "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } @Test public void testText() throws IOException { - Map result = cloudinary.uploader().text("hello world", asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().text("hello world", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertTrue(((Integer) result.get("width")) > 1); assertTrue(((Integer) result.get("height")) > 1); } @@ -209,9 +211,9 @@ public void testImageUploadTag() { @Test public void testSprite() throws IOException { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); - Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); + Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); assertTrue(((String) result.get("css_url")).contains("w_100")); @@ -222,7 +224,7 @@ public void testSprite() throws IOException { @Test public void testMulti() throws IOException { final String MULTI_TEST_TAG = "multi_test_tag" + SUFFIX; - final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, uniqueTag}); + final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, UPLOADER_TAG, uniqueTag}); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); List ids = new ArrayList(); @@ -270,7 +272,7 @@ public void testTags() throws Exception { public void testAllowedFormats() throws Exception { //should allow whitelisted formats if allowed_formats String[] formats = {"png"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("format"), "png"); } @@ -280,7 +282,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { boolean errorFound = false; String[] formats = {"jpg"}; try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { errorFound = true; } @@ -291,7 +293,7 @@ public void testAllowedFormatsWithIllegalFormat() throws Exception { public void testAllowedFormatsWithFormat() throws Exception { //should allow non whitelisted formats if type is specified and convert to that type String[] formats = {"jpg"}; - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "format", "jpg", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("allowed_formats", formats, "format", "jpg", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals("jpg", result.get("format")); } @@ -303,7 +305,7 @@ public void testFaceCoordinates() throws Exception { Rectangle rect2 = new Rectangle(120, 30, 109, 51); coordinates.addRect(rect1); coordinates.addRect(rect2); - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("face_coordinates", coordinates, "faces", true, "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("face_coordinates", coordinates, "faces", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); ArrayList resultFaces = ((ArrayList) result.get("faces")); assertEquals(2, resultFaces.size()); @@ -342,7 +344,7 @@ public void testFaceCoordinates() throws Exception { public void testCustomCoordinates() throws Exception { //should allow sending face coordinates Coordinates coordinates = new Coordinates("121,31,300,151"); - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("custom_coordinates", coordinates, "tags", SDK_TEST_TAG)); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("custom_coordinates", coordinates, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), asMap("coordinates", true)); int[] expected = new int[]{121, 31, SRC_TEST_IMAGE_W, SRC_TEST_IMAGE_H}; Object[] actual = ((ArrayList) ((ArrayList) ((Map) result.get("coordinates")).get("custom")).get(0)).toArray(); @@ -363,7 +365,7 @@ public void testCustomCoordinates() throws Exception { @Test public void testModerationRequest() throws Exception { //should support requesting manual moderation - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("moderation", "manual", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("moderation", "manual", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals("manual", ((List) result.get("moderation")).get(0).get("kind")); assertEquals("pending", ((List) result.get("moderation")).get(0).get("status")); } @@ -373,7 +375,7 @@ public void testModerationRequest() throws Exception { public void testRawConvertRequest() { //should support requesting raw conversion try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("raw_convert", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("raw_convert", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -383,7 +385,7 @@ public void testRawConvertRequest() { public void testCategorizationRequest() { //should support requesting categorization try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("categorization", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("categorization", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); } @@ -393,7 +395,7 @@ public void testCategorizationRequest() { public void testDetectionRequest() { //should support requesting detection try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("detection", "illegal", "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("detection", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); } @@ -403,7 +405,7 @@ public void testDetectionRequest() { public void testAutoTaggingRequest() { //should support requesting auto tagging try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("auto_tagging", 0.5f, "tags", SDK_TEST_TAG)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("auto_tagging", 0.5f, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { assertTrue(e.getMessage().matches("^Must use(.*)")); } @@ -427,7 +429,7 @@ public void testUploadLarge() throws Exception { out.close(); assertEquals(5880138, temp.length()); - String [] tags = new String [] {"upload_large_tag", SDK_TEST_TAG}; + String [] tags = new String [] {"upload_large_tag", SDK_TEST_TAG, UPLOADER_TAG}; Map resource = cloudinary.uploader().uploadLarge(temp, asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); @@ -457,14 +459,14 @@ public void testUploadLarge() throws Exception { public void testUnsignedUpload() throws Exception { // should support unsigned uploading using presets Map preset = cloudinary.api().createUploadPreset(asMap("folder", "upload_folder", "unsigned", true)); - Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().unsignedUpload(SRC_TEST_IMAGE, preset.get("name").toString(), asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertTrue(result.get("public_id").toString().matches("^upload_folder\\/[a-z0-9]+$")); cloudinary.api().deleteUploadPreset(preset.get("name").toString(), ObjectUtils.emptyMap()); } @Test public void testFilenameOption() throws Exception { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("filename", "emanelif", "tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("filename", "emanelif", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals("emanelif", result.get("original_filename")); } @@ -474,7 +476,7 @@ public void testResponsiveBreakpoints() throws Exception { // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", - breakpoint, "tags", SDK_TEST_TAG + breakpoint, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) )); java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); @@ -483,7 +485,7 @@ public void testResponsiveBreakpoints() throws Exception { // an array of breakpoints result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", - new ResponsiveBreakpoint[]{breakpoint}, "tags", SDK_TEST_TAG + new ResponsiveBreakpoint[]{breakpoint}, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); @@ -492,7 +494,7 @@ public void testResponsiveBreakpoints() throws Exception { // a JSONArray of breakpoints JSONArray array = new JSONArray(); array.put(breakpoint); - result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", array, "tags", SDK_TEST_TAG + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", array, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) )); breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); From c063e8c318ee4d0f96d2285deafc8e3bc72f9129 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 May 2017 14:44:45 +0300 Subject: [PATCH 288/592] Adapt ApiTest and ContextTest to parallel test runs. --- .../main/java/com/cloudinary/test/AbstractApiTest.java | 8 ++++---- .../java/com/cloudinary/test/AbstractContextTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) 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 44d9d0ac..86976189 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 @@ -35,7 +35,7 @@ abstract public class AbstractApiTest extends MockableTest { public static final String API_TEST_UPLOAD_PRESET_2 = API_TEST_UPLOAD_PRESET + "2"; public static final String API_TEST_UPLOAD_PRESET_3 = API_TEST_UPLOAD_PRESET + "3"; public static final String API_TEST_UPLOAD_PRESET_4 = API_TEST_UPLOAD_PRESET + "4"; - public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, uniqueTag}; + public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, SDK_TEST_TAG + "_api", uniqueTag}; public static final String EXPLICIT_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + ",w_100"; public static final Transformation EXPLICIT_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX).fontFamily("Arial").fontSize(60)); public static final String TEST_KEY = "test-key" + SUFFIX; @@ -49,7 +49,7 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); return; } - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", "key=value", "eager", + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", UPLOAD_TAGS, "context", "key=value", "eager", Collections.singletonList(EXPLICIT_TRANSFORMATION)); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options.put("public_id", API_TEST_1); @@ -57,9 +57,9 @@ public static void setUpClass() throws IOException { String context1 = TEST_KEY + "=alt"; String context2 = TEST_KEY + "=alternate"; - options = ObjectUtils.asMap("public_id", "context_1" + SUFFIX, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context1); + options = ObjectUtils.asMap("public_id", "context_1" + SUFFIX, "tags", UPLOAD_TAGS, "context", context1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", new String[]{SDK_TEST_TAG, uniqueTag}, "context", context2); + options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", UPLOAD_TAGS, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java index 10af2c39..69fb9f41 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java @@ -21,7 +21,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractContextTest extends MockableTest { - private static final String CONTEXT_TAG = "context_tag_" + String.valueOf(System.currentTimeMillis()); + private static final String CONTEXT_TAG = "context_tag_" + String.valueOf(System.currentTimeMillis()) + SUFFIX; private static Map resource; public static final Map CONTEXT = asMap("caption", "some cäption", "alt", "alternativè"); private Uploader uploader; From f443e85e0df984fb09f049257f2e9149a2b41e91 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 9 May 2017 15:21:42 +0300 Subject: [PATCH 289/592] Adapt tests to method-level parallel testing. --- .../com/cloudinary/test/AbstractApiTest.java | 121 ++++++++++-------- .../cloudinary/test/AbstractContextTest.java | 42 +++--- .../cloudinary/test/AbstractSearchTest.java | 11 +- .../cloudinary/test/AbstractUploaderTest.java | 5 +- .../com/cloudinary/test/MockableTest.java | 23 +--- pom.xml | 4 +- 6 files changed, 99 insertions(+), 107 deletions(-) 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 86976189..631c484b 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 @@ -35,9 +35,15 @@ abstract public class AbstractApiTest extends MockableTest { public static final String API_TEST_UPLOAD_PRESET_2 = API_TEST_UPLOAD_PRESET + "2"; public static final String API_TEST_UPLOAD_PRESET_3 = API_TEST_UPLOAD_PRESET + "3"; public static final String API_TEST_UPLOAD_PRESET_4 = API_TEST_UPLOAD_PRESET + "4"; - public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, SDK_TEST_TAG + "_api", uniqueTag}; + public static final String API_TAG = SDK_TEST_TAG + "_api"; + public static final String DIRECTION_TAG = SDK_TEST_TAG + "_api_resource_direction"; + public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, API_TAG}; public static final String EXPLICIT_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + ",w_100"; public static final Transformation EXPLICIT_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX).fontFamily("Arial").fontSize(60)); + public static final String UPDATE_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + "_update,w_100"; + public static final Transformation UPDATE_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX + "_update").fontFamily("Arial").fontSize(60)); + public static final String DELETE_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + "_delete,w_100"; + public static final Transformation DELETE_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX + "_delete").fontFamily("Arial").fontSize(60)); public static final String TEST_KEY = "test-key" + SUFFIX; protected Api api; @@ -49,26 +55,39 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); return; } - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", UPLOAD_TAGS, "context", "key=value", "eager", + + List uploadAndDirectionTag = new ArrayList(Arrays.asList(UPLOAD_TAGS)); + uploadAndDirectionTag.add(DIRECTION_TAG); + + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", uploadAndDirectionTag, "context", "key=value", "eager", Collections.singletonList(EXPLICIT_TRANSFORMATION)); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options.put("public_id", API_TEST_1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + options.remove("public_id"); + + options.put("eager", Collections.singletonList(UPDATE_TRANSFORMATION)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + + options.put("eager", Collections.singletonList(DELETE_TRANSFORMATION)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); String context1 = TEST_KEY + "=alt"; String context2 = TEST_KEY + "=alternate"; - options = ObjectUtils.asMap("public_id", "context_1" + SUFFIX, "tags", UPLOAD_TAGS, "context", context1); + + options = ObjectUtils.asMap("public_id", "context_1" + SUFFIX, "tags", uploadAndDirectionTag, "context", context1); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", UPLOAD_TAGS, "context", context2); + + options = ObjectUtils.asMap("public_id", "context_2" + SUFFIX, "tags", uploadAndDirectionTag, "context", context2); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); } @AfterClass public static void tearDownClass() { - Api api = MockableTest.cleanUp(); + Api api = new Cloudinary().api(); try { -// api.deleteResources(Arrays.asList(API_TEST, API_TEST_1, API_TEST_2, API_TEST_3, API_TEST_5), ObjectUtils.emptyMap()); - api.deleteResourcesByTag(uniqueTag, ObjectUtils.emptyMap()); + api.deleteResourcesByTag(API_TAG, ObjectUtils.emptyMap()); } catch (Exception ignored) { } try { @@ -135,7 +154,7 @@ public void test01ResourceTypes() throws Exception { @Test public void test02Resources() throws Exception { // should allow listing resources - Map resource = preloadResource(); + Map resource = preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS)); final List resources = new ArrayList(); String next_cursor = null; @@ -169,8 +188,8 @@ public void test03ResourcesCursor() throws Exception { @Test public void test04ResourcesByType() throws Exception { // should allow listing resources by type - Map resource = preloadResource(); - Map result = api.resources(ObjectUtils.asMap("type", "upload")); + Map resource = preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS)); + Map result = api.resources(ObjectUtils.asMap("type", "upload", "max_results", 500)); List resources = (List) result.get("resources"); assertThat(resources, hasItem(hasEntry("public_id", (String) resource.get("public_id")))); } @@ -185,19 +204,19 @@ public void test05ResourcesByPrefix() throws Exception { // resources = (List>) result.get("resources"); assertThat(resources, hasItem(allOf(hasEntry("public_id", API_TEST), hasEntry("type", "upload")))); assertThat(resources, hasItem(hasEntry("context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))))); - assertThat(resources, hasItem(hasEntry(equalTo("tags"), hasItem(SDK_TEST_TAG)))); + assertThat(resources, hasItem(hasEntry(equalTo("tags"), hasItem(API_TAG)))); } @Test public void testResourcesListingDirection() throws Exception { // should allow listing resources in both directions - Map result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", "asc", "max_results", 500)); + Map result = api.resourcesByTag(DIRECTION_TAG, ObjectUtils.asMap("type", "upload", "direction", "asc", "max_results", 500)); List resources = (List) result.get("resources"); ArrayList resourceIds = new ArrayList(); for (Map resource : resources) { resourceIds.add((String) resource.get("public_id")); } - result = api.resourcesByTag(uniqueTag, ObjectUtils.asMap("type", "upload", "direction", -1, "max_results", 500)); + result = api.resourcesByTag(DIRECTION_TAG, ObjectUtils.asMap("type", "upload", "direction", -1, "max_results", 500)); List resourcesDesc = (List) result.get("resources"); ArrayList resourceIdsDesc = new ArrayList(); for (Map resource : resourcesDesc) { @@ -232,7 +251,7 @@ public void testResourcesByPublicIds() throws Exception { boolean found = false; for (Map r : resources) { ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains(SDK_TEST_TAG); + found = found || tags.contains(API_TAG); } assertTrue(found); } @@ -240,7 +259,7 @@ public void testResourcesByPublicIds() throws Exception { @Test public void test06ResourcesTag() throws Exception { // should allow listing resources by tag - Map result = api.resourcesByTag(SDK_TEST_TAG, ObjectUtils.asMap("tags", true, "context", true)); + Map result = api.resourcesByTag(API_TAG, ObjectUtils.asMap("tags", true, "context", true, "max_results", 500)); Map resource = findByAttr((List) result.get("resources"), "public_id", API_TEST); assertNotNull(resource); resource = findByAttr((List) result.get("resources"), "context", ObjectUtils.asMap("custom", ObjectUtils.asMap("key", "value"))); @@ -249,7 +268,7 @@ public void test06ResourcesTag() throws Exception { boolean found = false; for (Map r : resources) { ArrayList tags = (ArrayList) r.get("tags"); - found = found || tags.contains(SDK_TEST_TAG); + found = found || tags.contains(API_TAG); } assertTrue(found); } @@ -338,15 +357,15 @@ public void test10Tags() throws Exception { // should allow listing tags Map result = api.tags(ObjectUtils.asMap("max_results", 500)); List tags = (List) result.get("tags"); - assertThat(tags, hasItem(SDK_TEST_TAG)); + assertThat(tags, hasItem(API_TAG)); } @Test public void test11TagsPrefix() throws Exception { // should allow listing tag by prefix - Map result = api.tags(ObjectUtils.asMap("prefix", SDK_TEST_TAG.substring(0, SDK_TEST_TAG.length() - 1))); + Map result = api.tags(ObjectUtils.asMap("prefix", API_TAG.substring(0, API_TAG.length() - 1))); List tags = (List) result.get("tags"); - assertThat(tags, hasItem(SDK_TEST_TAG)); + assertThat(tags, hasItem(API_TAG)); result = api.tags(ObjectUtils.asMap("prefix", "api_test_no_such_tag")); tags = (List) result.get("tags"); assertEquals(0, tags.size()); @@ -365,7 +384,7 @@ public void test12Transformations() throws Exception { @Test public void test13TransformationMetadata() throws Exception { // should allow getting transformation metadata - preloadResource(ObjectUtils.asMap("eager", Collections.singletonList(EXPLICIT_TRANSFORMATION))); + preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS, "eager", Collections.singletonList(EXPLICIT_TRANSFORMATION))); Map transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("max_results", 500)); assertNotNull(transformation); assertEquals(new Transformation((List) transformation.get("info")).generate(), EXPLICIT_TRANSFORMATION.generate()); @@ -374,12 +393,12 @@ public void test13TransformationMetadata() throws Exception { @Test public void test14TransformationUpdate() throws Exception { // should allow updating transformation allowed_for_strict - api.updateTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); - Map transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + api.updateTransformation(UPDATE_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", true), ObjectUtils.emptyMap()); + Map transformation = api.transformation(UPDATE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), true); - api.updateTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); - transformation = api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + api.updateTransformation(UPDATE_TRANSFORMATION_NAME, ObjectUtils.asMap("allowed_for_strict", false), ObjectUtils.emptyMap()); + transformation = api.transformation(UPDATE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); assertNotNull(transformation); assertEquals(transformation.get("allowed_for_strict"), false); } @@ -407,24 +426,23 @@ public void test15aTransformationUnsafeUpdate() throws Exception { assertEquals(transformation.get("used"), false); } - @Test + @Test(expected = NotFound.class) public void test16aTransformationDelete() throws Exception { // should allow deleting named transformation api.createTransformation(API_TEST_TRANSFORMATION_2, new Transformation().crop("scale").width(103).generate(), ObjectUtils.emptyMap()); api.transformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); - api.deleteTransformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); - } - - @Test(expected = NotFound.class) - public void test16bTransformationDelete() throws Exception { + ApiResponse res = api.deleteTransformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); + assertEquals("deleted", res.get("message")); api.transformation(API_TEST_TRANSFORMATION_2, ObjectUtils.emptyMap()); } - @Test + @Test(expected = NotFound.class) public void test17aTransformationDeleteImplicit() throws Exception { // should allow deleting implicit transformation - api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); - api.deleteTransformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + api.transformation(DELETE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + ApiResponse res = api.deleteTransformation(DELETE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); + assertEquals("deleted", res.get("message")); + api.deleteTransformation(DELETE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); } @Test @@ -439,15 +457,6 @@ public void test20ResourcesContext() throws Exception { assertEquals(1, resources.size()); } - /** - * @throws Exception - * @expectedException \Cloudinary\Api\NotFound - */ - @Test(expected = NotFound.class) - public void test17bTransformationDeleteImplicit() throws Exception { - api.transformation(EXPLICIT_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); - } - @Test public void test18Usage() throws Exception { // should support usage API call @@ -574,10 +583,12 @@ public void testListUploadPresets() throws Exception { api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET_2, "folder", "folder2")); api.createUploadPreset(ObjectUtils.asMap("name", API_TEST_UPLOAD_PRESET_3, "folder", "folder3")); - ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); - assertEquals(((Map) presets.get(0)).get("name"), API_TEST_UPLOAD_PRESET_3); - assertEquals(((Map) presets.get(1)).get("name"), API_TEST_UPLOAD_PRESET_2); - assertEquals(((Map) presets.get(2)).get("name"), API_TEST_UPLOAD_PRESET); + ArrayList presets = (ArrayList) (api.uploadPresets(ObjectUtils.emptyMap()).get("presets")); + + assertThat(presets, hasItem(hasEntry("name", API_TEST_UPLOAD_PRESET))); + assertThat(presets, hasItem(hasEntry("name", API_TEST_UPLOAD_PRESET_2))); + assertThat(presets, hasItem(hasEntry("name", API_TEST_UPLOAD_PRESET_3))); + api.deleteUploadPreset(API_TEST_UPLOAD_PRESET, ObjectUtils.emptyMap()); api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_2, ObjectUtils.emptyMap()); api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_3, ObjectUtils.emptyMap()); @@ -751,7 +762,7 @@ public void testUploadMapping() throws Exception { @Test public void testPublishByIds() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "type", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "type", "authenticated")); String publicId = (String) response.get("public_id"); response = cloudinary.api().publishByIds(Arrays.asList(publicId), null); List published = (List) response.get("published"); @@ -765,7 +776,7 @@ public void testPublishByIds() throws Exception { @Test public void testPublishWithType() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "type", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "type", "authenticated")); String publicId = (String) response.get("public_id"); // publish with wrong type - verify publish fails @@ -794,7 +805,7 @@ public void testPublishWithType() throws Exception { @Test public void testPublishByPrefix() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "type", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "type", "authenticated")); String publicId = (String) response.get("public_id"); response = cloudinary.api().publishByPrefix(publicId.substring(0, publicId.length() - 2), null); List published = (List) response.get("published"); @@ -808,9 +819,9 @@ public void testPublishByPrefix() throws Exception { @Test public void testPublishByTag() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", Arrays.asList(uniqueTag, uniqueTag + "1"), "type", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", Arrays.asList(API_TAG, API_TAG + "1"), "type", "authenticated")); String publicId = (String) response.get("public_id"); - response = cloudinary.api().publishByTag(uniqueTag + "1", null); + response = cloudinary.api().publishByTag(API_TAG + "1", null); List published = (List) response.get("published"); assertNotNull(published); assertEquals(published.size(), 1); @@ -822,7 +833,7 @@ public void testPublishByTag() throws Exception { @Test public void testUpdateResourcesAccessModeByIds() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "access_mode", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "access_mode", "authenticated")); String publicId = (String) response.get("public_id"); assertEquals(response.get("access_mode"), "authenticated"); response = cloudinary.api().updateResourcesAccessModeByIds("public", Arrays.asList(publicId), null); @@ -837,7 +848,7 @@ public void testUpdateResourcesAccessModeByIds() throws Exception { @Test public void testUpdateResourcesAccessModeByPrefix() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", uniqueTag, "access_mode", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "access_mode", "authenticated")); String publicId = (String) response.get("public_id"); assertEquals(response.get("access_mode"), "authenticated"); response = cloudinary.api().updateResourcesAccessModeByPrefix("public", publicId.substring(0, publicId.length() - 2), null); @@ -852,10 +863,10 @@ public void testUpdateResourcesAccessModeByPrefix() throws Exception { @Test public void testUpdateResourcesAccessModeByTag() throws Exception { - Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", Arrays.asList(uniqueTag, uniqueTag + "2"), "access_mode", "authenticated")); + Map response = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", Arrays.asList(API_TAG, API_TAG + "2"), "access_mode", "authenticated")); String publicId = (String) response.get("public_id"); assertEquals(response.get("access_mode"), "authenticated"); - response = cloudinary.api().updateResourcesAccessModeByTag("public", uniqueTag + "2", null); + response = cloudinary.api().updateResourcesAccessModeByTag("public", API_TAG + "2", null); List updated = (List) response.get("updated"); assertNotNull(updated); assertEquals(updated.size(), 1); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java index 69fb9f41..e785b5c0 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java @@ -1,6 +1,5 @@ package com.cloudinary.test; -import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.Transformation; import com.cloudinary.Uploader; @@ -8,6 +7,7 @@ import org.junit.*; import org.junit.rules.TestName; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,7 +22,6 @@ abstract public class AbstractContextTest extends MockableTest { private static final String CONTEXT_TAG = "context_tag_" + String.valueOf(System.currentTimeMillis()) + SUFFIX; - private static Map resource; public static final Map CONTEXT = asMap("caption", "some cäption", "alt", "alternativè"); private Uploader uploader; @@ -32,20 +31,18 @@ public static void setUpClass() throws Exception { if (cloudinary.config.apiSecret == null) { System.err.println("Please setup environment for Upload test to run"); } + } - resource = cloudinary.uploader().upload(SRC_TEST_IMAGE, - asMap( "tags", new String[]{SDK_TEST_TAG, CONTEXT_TAG}, + private static Map uploadResource(String publicId) throws IOException { + return new Cloudinary().uploader().upload(SRC_TEST_IMAGE, + asMap( "public_id", publicId, + "tags", new String[]{SDK_TEST_TAG, CONTEXT_TAG}, "context", CONTEXT, "transformation", new Transformation().crop("scale").width(10))); - final String publicId = (String) resource.get("public_id"); - resource = cloudinary.api().resource(publicId, asMap("context", true)); - assertEquals(asMap("custom", CONTEXT), resource.get("context")); - } @AfterClass public static void tearDownClass() { - Api api = MockableTest.cleanUp(); Cloudinary cloudinary = new Cloudinary(); try { cloudinary.api().deleteResourcesByTag(CONTEXT_TAG, ObjectUtils.emptyMap()); @@ -67,36 +64,37 @@ public void setUp() throws Exception { @Test public void testExplicit() throws Exception { + String publicId = "explicit_id" + SUFFIX; + uploadResource(publicId); //should allow sending context - Map differentContext = asMap("caption", "different = caption", "alt2", "alt|alternative alternative"); - Map result = uploader.explicit(publicId(), asMap("type", "upload", "context", differentContext)); + Map result = uploader.explicit(publicId, asMap("type", "upload", "context", differentContext)); assertEquals("explicit API should return the new context", asMap("custom", differentContext), result.get("context")); - resource = cloudinary.api().resource(publicId(), asMap("context", true)); + Map resource = cloudinary.api().resource(publicId, asMap("context", true)); assertEquals("explicit API should replace the context", asMap("custom", differentContext), resource.get("context")); } @Test public void testAddContext() throws Exception { + String publicId = "add_context_id" + SUFFIX; + Map resource = uploadResource(publicId); Map context = new HashMap((Map)((Map)resource.get("context")).get("custom")); context.put("caption", "new caption"); - Map result = uploader.addContext(asMap("caption", "new caption"), new String[]{publicId(), "no-such-id"}, null); - assertThat("addContext should return a list of modified public IDs", (List) result.get("public_ids"), contains(publicId())); + Map result = uploader.addContext(asMap("caption", "new caption"), new String[]{publicId, "no-such-id"}, null); + assertThat("addContext should return a list of modified public IDs", (List) result.get("public_ids"), contains(publicId)); - resource = cloudinary.api().resource(publicId(), asMap("context", true)); + resource = cloudinary.api().resource(publicId, asMap("context", true)); assertEquals(asMap("custom", context), resource.get("context")); } @Test public void testRemoveAllContext() throws Exception { - Map result = uploader.removeAllContext(new String[]{publicId(), "no-such-id"}, null); - assertThat((List) result.get("public_ids"), contains(publicId())); + String publicId = "remove_context_id" + SUFFIX; + uploadResource(publicId); + Map result = uploader.removeAllContext(new String[]{publicId, "no-such-id"}, null); + assertThat((List) result.get("public_ids"), contains(publicId)); - resource = cloudinary.api().resource(publicId(), asMap("context", true)); + Map resource = cloudinary.api().resource(publicId, asMap("context", true)); assertThat((Map)resource, not(hasKey("context"))); } - - private String publicId(){ - return (String) resource.get("public_id"); - } } 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 ffe5606b..a826989c 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,6 +1,5 @@ package com.cloudinary.test; -import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; import org.junit.*; @@ -18,6 +17,7 @@ abstract public class AbstractSearchTest extends MockableTest { @Rule public TestName currentTest = new TestName(); private static final String SEARCH_TAG = "search_test" + SUFFIX; + public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, SEARCH_TAG}; private static final String API_TEST = "api_test_" + SUFFIX; private static final String API_TEST_1 = API_TEST + "_1"; private static final String API_TEST_2 = API_TEST + "_2"; @@ -25,15 +25,15 @@ abstract public class AbstractSearchTest extends MockableTest { @BeforeClass public static void setUpClass() throws Exception { Cloudinary cloudinary = new Cloudinary(); - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=in_review"); + Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", UPLOAD_TAGS, "context", "stage=in_review"); cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", API_TEST_1, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=new"); + options = ObjectUtils.asMap("public_id", API_TEST_1, "tags", UPLOAD_TAGS, "context", "stage=new"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", API_TEST_2, "tags", new String[]{SDK_TEST_TAG, SEARCH_TAG, uniqueTag}, "context", "stage=validated"); + options = ObjectUtils.asMap("public_id", API_TEST_2, "tags", UPLOAD_TAGS, "context", "stage=validated"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); try { - Thread.sleep(3000); //wait for search indexing + Thread.sleep(5000); //wait for search indexing } catch (InterruptedException e) { e.printStackTrace(); } @@ -41,7 +41,6 @@ public static void setUpClass() throws Exception { @AfterClass public static void tearDownClass() throws Exception { - Api api = MockableTest.cleanUp(); Cloudinary cloudinary = new Cloudinary(); cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); } 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 6991e62e..c8dac352 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 @@ -43,8 +43,7 @@ public static void setUpClass() throws IOException { @AfterClass public static void tearDownClass() { - Api api = MockableTest.cleanUp(); - Cloudinary cloudinary = new Cloudinary(); + Api api = new Cloudinary().api(); try { api.deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); } catch (Exception ignored) { @@ -224,7 +223,7 @@ public void testSprite() throws IOException { @Test public void testMulti() throws IOException { final String MULTI_TEST_TAG = "multi_test_tag" + SUFFIX; - final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, UPLOADER_TAG, uniqueTag}); + final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, UPLOADER_TAG}); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); List ids = new ArrayList(); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 151b774e..3c5a3341 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -1,6 +1,5 @@ package com.cloudinary.test; -import com.cloudinary.Api; import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -16,7 +15,6 @@ public class MockableTest { public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; protected static String SUFFIX = StringUtils.isNotBlank(System.getenv("TRAVIS_JOB_ID")) ? System.getenv("TRAVIS_JOB_ID") : String.valueOf(new Random().nextInt(99999)); protected static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; - protected static final String uniqueTag = SDK_TEST_TAG + (new java.util.Date().getTime()); protected Cloudinary cloudinary; protected Object getParam(String name){ @@ -29,25 +27,12 @@ protected String getHttpMethod(){ throw new NotImplementedException(); } - protected Map preloadResource() throws IOException { - return preloadResource(ObjectUtils.emptyMap()); - } - protected Map preloadResource(Map options) throws IOException { - Map combinedOptions = ObjectUtils.asMap( - "tags", new String[]{SDK_TEST_TAG, uniqueTag}, - "transformation", "c_scale,w_100"); + if (!options.containsKey("tags")){ + throw new IllegalArgumentException("Must provide unique per-class tags"); + } + Map combinedOptions = ObjectUtils.asMap("transformation", "c_scale,w_100"); combinedOptions.putAll(options); return cloudinary.uploader().upload("http://res.cloudinary.com/demo/image/upload/sample", combinedOptions); } - - public static Api cleanUp() { - Cloudinary cloudinary = new Cloudinary(); - Api api = cloudinary.api(); - try { - api.deleteResourcesByTag(uniqueTag, ObjectUtils.emptyMap()); - } catch (Exception ignored) { - } - return api; - } } diff --git a/pom.xml b/pom.xml index d3745ace..57ff9557 100644 --- a/pom.xml +++ b/pom.xml @@ -67,8 +67,8 @@ maven-surefire-plugin 2.20 - 3C - true + methods + 12 From 321bcd36da880ef802d5168c1f4fda3991c4c86b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 9 May 2017 15:30:35 +0300 Subject: [PATCH 290/592] Run parallel test in classes mode. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 57ff9557..d3745ace 100644 --- a/pom.xml +++ b/pom.xml @@ -67,8 +67,8 @@ maven-surefire-plugin 2.20 - methods - 12 + 3C + true From 072be5e6343cdc9964fa534cfbb530b70e7d6659 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 9 May 2017 21:07:12 +0300 Subject: [PATCH 291/592] Fix deletion tests to prevent cross-tests pollution. --- .../com/cloudinary/test/AbstractApiTest.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 631c484b..ebc78013 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 @@ -327,28 +327,29 @@ public void test09DeleteResources() throws Exception { 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); - api.deleteResources(Arrays.asList(API_TEST_2, public_id), ObjectUtils.emptyMap()); + api.deleteResources(Arrays.asList(public_id), ObjectUtils.emptyMap()); api.resource(public_id, ObjectUtils.emptyMap()); } @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", "api_test_by_prefix", "tags", UPLOAD_TAGS)); - Map resource = api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + String public_id = SUFFIX + "_api_test_by_prefix"; + 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); - api.deleteResourcesByPrefix("api_test_by", ObjectUtils.emptyMap()); - api.resource("api_test_by_prefix", ObjectUtils.emptyMap()); + api.deleteResourcesByPrefix(public_id.substring(0, SUFFIX.length() + 10), ObjectUtils.emptyMap()); + api.resource(public_id, ObjectUtils.emptyMap()); } @Test(expected = NotFound.class) public void test09aDeleteResourcesByTags() throws Exception { // should allow deleting resources - cloudinary.uploader().upload(SRC_TEST_IMAGE, - ObjectUtils.asMap("public_id", API_TEST + "_4", "tags", Collections.singletonList("api_test_tag_for_delete"))); + String tag = "api_test_tag_for_delete" + SUFFIX; + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", API_TEST + "_4", "tags", Collections.singletonList(tag))); Map resource = api.resource(API_TEST + "_4", ObjectUtils.emptyMap()); assertNotNull(resource); - api.deleteResourcesByTag("api_test_tag_for_delete", ObjectUtils.emptyMap()); + api.deleteResourcesByTag(tag, ObjectUtils.emptyMap()); api.resource(API_TEST + "_4", ObjectUtils.emptyMap()); } From 4da149a7de635f6535ce6ed9c51f87e980f91a67 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 May 2017 08:48:29 +0300 Subject: [PATCH 292/592] Change public ids ion `SearchTest` resources (Some were already used by `ApiTest`). --- .../cloudinary/test/AbstractSearchTest.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 a826989c..303284e0 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 @@ -16,24 +16,24 @@ abstract public class AbstractSearchTest extends MockableTest { @Rule public TestName currentTest = new TestName(); - private static final String SEARCH_TAG = "search_test" + SUFFIX; + private static final String SEARCH_TAG = "search_test_tag_" + SUFFIX; public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, SEARCH_TAG}; - private static final String API_TEST = "api_test_" + SUFFIX; - private static final String API_TEST_1 = API_TEST + "_1"; - private static final String API_TEST_2 = API_TEST + "_2"; + private static final String SEARCH_TEST = "search_test_" + SUFFIX; + private static final String SEARCH_TEST_1 = SEARCH_TEST + "_1"; + private static final String SEARCH_TEST_2 = SEARCH_TEST + "_2"; @BeforeClass public static void setUpClass() throws Exception { Cloudinary cloudinary = new Cloudinary(); - Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", UPLOAD_TAGS, "context", "stage=in_review"); + Map options = ObjectUtils.asMap("public_id", SEARCH_TEST, "tags", UPLOAD_TAGS, "context", "stage=in_review"); cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", API_TEST_1, "tags", UPLOAD_TAGS, "context", "stage=new"); + options = ObjectUtils.asMap("public_id", SEARCH_TEST_1, "tags", UPLOAD_TAGS, "context", "stage=new"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - options = ObjectUtils.asMap("public_id", API_TEST_2, "tags", UPLOAD_TAGS, "context", "stage=validated"); + options = ObjectUtils.asMap("public_id", SEARCH_TEST_2, "tags", UPLOAD_TAGS, "context", "stage=validated"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); try { - Thread.sleep(5000); //wait for search indexing + Thread.sleep(3000); //wait for search indexing } catch (InterruptedException e) { e.printStackTrace(); } @@ -61,7 +61,7 @@ public void shouldFindResourcesByTag() throws Exception { @Test public void shouldFindResourceByPublicId() throws Exception { - Map result = cloudinary.search().expression(String.format("public_id:%s", API_TEST_1)).execute(); + Map result = cloudinary.search().expression(String.format("public_id:%s", SEARCH_TEST_1)).execute(); List resources = (List) result.get("resources"); assertEquals(1, resources.size()); } @@ -74,7 +74,7 @@ public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() th assertEquals(1, resources.size()); assertEquals(3, result.get("total_count")); - assertEquals(API_TEST, resources.get(0).get("public_id")); + assertEquals(SEARCH_TEST, resources.get(0).get("public_id")); result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc") @@ -83,7 +83,7 @@ public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() th assertEquals(1, resources.size()); assertEquals(3, result.get("total_count")); - assertEquals(API_TEST_1, resources.get(0).get("public_id")); + assertEquals(SEARCH_TEST_1, resources.get(0).get("public_id")); result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc") .nextCursor(ObjectUtils.asString(result.get("next_cursor"))).execute(); @@ -91,7 +91,7 @@ public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() th assertEquals(1, resources.size()); assertEquals(3, result.get("total_count")); - assertEquals(API_TEST_2, resources.get(0).get("public_id")); + assertEquals(SEARCH_TEST_2, resources.get(0).get("public_id")); assertNull(result.get("next_cursor")); From ee887f54e5ca4e72bad65400f57a3465b2df4efa Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 May 2017 15:23:32 +0300 Subject: [PATCH 293/592] Add video and raw resource types to `UploaderTest` cleanup. --- .../cloudinary/test/AbstractUploaderTest.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) 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 c8dac352..d90c07f6 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 @@ -45,11 +45,15 @@ public static void setUpClass() throws IOException { public static void tearDownClass() { Api api = new Cloudinary().api(); try { - api.deleteResourcesByTag(ARCHIVE_TAG, ObjectUtils.emptyMap()); + api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.emptyMap()); } catch (Exception ignored) { } try { - api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.emptyMap()); + api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.asMap("resource_type", "video")); + } catch (Exception ignored) { + } + try { + api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.asMap("resource_type", "raw")); } catch (Exception ignored) { } } @@ -144,11 +148,11 @@ public void testRename() throws Exception { assertNotNull(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap())); Map result2 = cloudinary.uploader().upload("../cloudinary-test-common/src/main/resources/favicon.ico", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); - boolean error_found=false; + boolean error_found = false; try { - cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); - } catch(Exception e) { - error_found=true; + cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + } catch (Exception e) { + error_found = true; } assertTrue(error_found); cloudinary.uploader().rename((String) result2.get("public_id"), publicId2, asMap("overwrite", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); @@ -180,7 +184,7 @@ public void testEager() throws IOException { @Test public void testUploadAsync() throws IOException { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true)); - assertEquals((String)result.get("status"), "pending"); + assertEquals((String) result.get("status"), "pending"); } @Test @@ -209,9 +213,9 @@ public void testImageUploadTag() { @Test public void testSprite() throws IOException { - final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); + final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); @@ -227,7 +231,7 @@ public void testMulti() throws IOException { cloudinary.uploader().upload(SRC_TEST_IMAGE, options); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); List ids = new ArrayList(); - Map result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5" )); + Map result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5")); ids.add((String) result.get("public_id")); Map pdfResult = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", new Transformation().width(111), "format", "pdf")); ids.add((String) pdfResult.get("public_id")); @@ -428,7 +432,7 @@ public void testUploadLarge() throws Exception { out.close(); assertEquals(5880138, temp.length()); - String [] tags = new String [] {"upload_large_tag", SDK_TEST_TAG, UPLOADER_TAG}; + String[] tags = new String[]{"upload_large_tag_" + SUFFIX, SDK_TEST_TAG, UPLOADER_TAG}; Map resource = cloudinary.uploader().uploadLarge(temp, asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); @@ -442,7 +446,7 @@ public void testUploadLarge() throws Exception { assertEquals(1400, resource.get("height")); resource = cloudinary.uploader().uploadLarge(temp, asMap("chunk_size", 5880138, "tags", tags)); - assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); + 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")); @@ -480,7 +484,7 @@ public void testResponsiveBreakpoints() throws Exception { java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); assertEquals(2, breakpoints.size()); - assertTrue(((Map)breakpoints.get(0)).get("url").toString().endsWith("gif")); + assertTrue(((Map) breakpoints.get(0)).get("url").toString().endsWith("gif")); // an array of breakpoints result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", @@ -532,12 +536,12 @@ public void testDownloadArchive() throws Exception { } assertEquals(2, files); } - + public void testUploadInvalidUrl() { try { cloudinary.uploader().upload(REMOTE_TEST_IMAGE + "\n", asMap("return_error", true)); fail("Expected exception was not thrown"); - } catch(IOException e) { + } catch (IOException e) { assertEquals(e.getMessage(), "File not found or unreadable: " + REMOTE_TEST_IMAGE + "\n"); } } From bcde370c328339190ab8a1a2d770d80341aeed20 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 11 May 2017 10:14:32 +0300 Subject: [PATCH 294/592] Adapt another test to parallel testing and increase search test sleep time. --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 4 +++- .../src/main/java/com/cloudinary/test/AbstractSearchTest.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) 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 ebc78013..712e8d74 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 @@ -375,8 +375,10 @@ public void test11TagsPrefix() throws Exception { @Test public void test12Transformations() throws Exception { // should allow listing transformations + final Transformation listTest = new Transformation().width(25).crop("scale").overlay(new TextLayer().text(SUFFIX + "_testListTransformations").fontFamily("Arial").fontSize(60)); + preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS, "eager", Collections.singletonList(listTest))); Map result = api.transformations(ObjectUtils.emptyMap()); - Map transformation = findByAttr((List) result.get("transformations"), "name", EXPLICIT_TRANSFORMATION_NAME); + Map transformation = findByAttr((List) result.get("transformations"), "name", listTest.generate()); assertNotNull(transformation); assertTrue((Boolean) transformation.get("used")); 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 303284e0..212b15e3 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 @@ -33,7 +33,7 @@ public static void setUpClass() throws Exception { options = ObjectUtils.asMap("public_id", SEARCH_TEST_2, "tags", UPLOAD_TAGS, "context", "stage=validated"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); try { - Thread.sleep(3000); //wait for search indexing + Thread.sleep(5000); //wait for search indexing } catch (InterruptedException e) { e.printStackTrace(); } From c42208cd186c60088ff0039a34949a9e48ac7e29 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 11 May 2017 10:36:21 +0300 Subject: [PATCH 295/592] Set max_results to 500 in `test12Transformations` to ensure stability. --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 712e8d74..a3c5e920 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 @@ -377,7 +377,7 @@ public void test12Transformations() throws Exception { // should allow listing transformations final Transformation listTest = new Transformation().width(25).crop("scale").overlay(new TextLayer().text(SUFFIX + "_testListTransformations").fontFamily("Arial").fontSize(60)); preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS, "eager", Collections.singletonList(listTest))); - Map result = api.transformations(ObjectUtils.emptyMap()); + Map result = api.transformations(ObjectUtils.asMap("max_results", 500)); Map transformation = findByAttr((List) result.get("transformations"), "name", listTest.generate()); assertNotNull(transformation); From 09e08bb01da67809c0634e6737364e715960bb42 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 14 May 2017 14:16:15 +0300 Subject: [PATCH 296/592] Migrate to gradle --- .travis.yml | 14 +- build.gradle | 22 ++ cloudinary-android-test/pom.xml | 98 --------- cloudinary-android-test/project.properties | 15 -- .../src/main/res/drawable-hdpi/icon.png | Bin 4147 -> 0 bytes .../src/main/res/drawable-ldpi/icon.png | Bin 1723 -> 0 bytes .../src/main/res/drawable-mdpi/icon.png | Bin 2574 -> 0 bytes .../src/main/res/layout/main.xml | 12 -- .../src/main/res/values/strings.xml | 5 - cloudinary-android/AndroidManifest.xml | 10 - cloudinary-android/build.gradle | 125 +++++++++++ cloudinary-android/pom.xml | 58 ----- .../src/androidTest}/assets/docx.docx | Bin .../androidTest}/assets/images/favicon.ico | Bin .../androidTest}/assets/images/old_logo.png | Bin .../android}/test/UploaderTest.java | 67 ++++-- .../template}/AndroidManifest.xml.sample | 8 +- .../src/main/AndroidManifest.xml | 5 + cloudinary-core/build.gradle | 60 ++++++ cloudinary-core/pom.xml | 15 -- cloudinary-http42/build.gradle | 81 +++++++ cloudinary-http42/pom.xml | 48 ----- .../java/com/cloudinary/test/ApiTest.java | 9 +- cloudinary-http43/build.gradle | 80 +++++++ cloudinary-http43/pom.xml | 42 ---- .../java/com/cloudinary/test/ApiTest.java | 4 +- cloudinary-http44/build.gradle | 80 +++++++ cloudinary-http44/pom.xml | 42 ---- .../java/com/cloudinary/test/ApiTest.java | 5 +- cloudinary-taglib/build.gradle | 80 +++++++ cloudinary-taglib/pom.xml | 34 --- cloudinary-test-common/build.gradle | 70 ++++++ .../java/com/cloudinary/test/TimeoutTest.java | 7 + gradle.properties | 13 ++ pom.xml | 201 ------------------ settings.gradle | 8 + 36 files changed, 705 insertions(+), 613 deletions(-) create mode 100644 build.gradle delete mode 100644 cloudinary-android-test/pom.xml delete mode 100644 cloudinary-android-test/project.properties delete mode 100644 cloudinary-android-test/src/main/res/drawable-hdpi/icon.png delete mode 100644 cloudinary-android-test/src/main/res/drawable-ldpi/icon.png delete mode 100644 cloudinary-android-test/src/main/res/drawable-mdpi/icon.png delete mode 100644 cloudinary-android-test/src/main/res/layout/main.xml delete mode 100644 cloudinary-android-test/src/main/res/values/strings.xml delete mode 100644 cloudinary-android/AndroidManifest.xml create mode 100644 cloudinary-android/build.gradle delete mode 100644 cloudinary-android/pom.xml rename {cloudinary-android-test/src/main => cloudinary-android/src/androidTest}/assets/docx.docx (100%) rename {cloudinary-android-test/src/main => cloudinary-android/src/androidTest}/assets/images/favicon.ico (100%) rename {cloudinary-android-test/src/main => cloudinary-android/src/androidTest}/assets/images/old_logo.png (100%) rename {cloudinary-android-test/src/main/java/com/cloudinary => cloudinary-android/src/androidTest/java/com/cloudinary/android}/test/UploaderTest.java (94%) rename {cloudinary-android-test/src/main => cloudinary-android/src/androidTest/template}/AndroidManifest.xml.sample (69%) create mode 100644 cloudinary-android/src/main/AndroidManifest.xml create mode 100644 cloudinary-core/build.gradle delete mode 100644 cloudinary-core/pom.xml create mode 100644 cloudinary-http42/build.gradle delete mode 100644 cloudinary-http42/pom.xml create mode 100644 cloudinary-http43/build.gradle delete mode 100644 cloudinary-http43/pom.xml create mode 100644 cloudinary-http44/build.gradle delete mode 100644 cloudinary-http44/pom.xml create mode 100644 cloudinary-taglib/build.gradle delete mode 100644 cloudinary-taglib/pom.xml create mode 100644 cloudinary-test-common/build.gradle create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/TimeoutTest.java create mode 100644 gradle.properties delete mode 100644 pom.xml create mode 100644 settings.gradle diff --git a/.travis.yml b/.travis.yml index be01f315..cad9ae03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,19 @@ language: android +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 - oraclejdk7 - openjdk7 - -# Temporarily disabled, test fail because Hamcrest needs java 1.7 (probably) +# Temporarily disabled, test fail because Hamcrest needs java 1.7 # - openjdk6 -script: mvn test -B -Dtest=\!*#*Timeout* -DfailIfNoTests=false +# ciTest is configured to skip the various timeout tests that don't work in travis +script: gradle ciTest diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..130271ed --- /dev/null +++ b/build.gradle @@ -0,0 +1,22 @@ +buildscript { +} + +allprojects { + apply plugin: 'maven' + apply plugin: 'signing' + + repositories { + jcenter() + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + } +} + +subprojects { + tasks.withType(Test) { + maxParallelForks = Runtime.runtime.availableProcessors() + + // show standard out and standard error of the test JVM(s) on the console + testLogging.showStandardStreams = true + } +} diff --git a/cloudinary-android-test/pom.xml b/cloudinary-android-test/pom.xml deleted file mode 100644 index 525b4f7f..00000000 --- a/cloudinary-android-test/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - com.cloudinary - cloudinary-android-test - 1.12.1-SNAPSHOT - apk - Cloudinary Android Test Project - - - - com.cloudinary - cloudinary-android - jar - 1.12.1-SNAPSHOT - - - com.google.android - android - 4.1.1.4 - provided - - - com.google.android - android-test - 4.1.1.4 - provided - - - junit - junit - 4.8.1 - - - - ${project.artifactId} - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - 4.0.0-rc.2 - - - true - - - true - - - maven-antrun-plugin - 1.8 - - - copy - validate - - - - - - - run - - - - - - com.google.code.maven-replacer-plugin - replacer - 1.5.3 - - - validate - - replace - - - - - ${basedir}/src/main/AndroidManifest.xml - - - %CLOUDINARY_URL% - ${env.CLOUDINARY_URL} - - - - - - - diff --git a/cloudinary-android-test/project.properties b/cloudinary-android-test/project.properties deleted file mode 100644 index 518fade5..00000000 --- a/cloudinary-android-test/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 -android.library.reference.1=../cloudinary-android diff --git a/cloudinary-android-test/src/main/res/drawable-hdpi/icon.png b/cloudinary-android-test/src/main/res/drawable-hdpi/icon.png deleted file mode 100644 index 8074c4c571b8cd19e27f4ee5545df367420686d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4147 zcmV-35X|q1P)OwvMs$Q8_8nISM!^>PxsujeDCl4&hPxrxkp%Qc^^|l zp6LqAcf3zf1H4aA1Gv-O6ha)ktct9Y+VA@N^9i;p0H%6v>ZJZYQ`zEa396z-gi{r_ zDz)D=vgRv62GCVeRjK{15j7V@v6|2nafFX6W7z2j1_T0a zLyT3pGTubf1lB5)32>bl0*BflrA!$|_(WD2)iJIfV}37=ZKAC zSe3boYtQ=;o0i>)RtBvsI#iT{0!oF1VFeW`jDjF2Q4aE?{pGCAd>o8Kg#neIh*AMY zLl{;F!vLiem7s*x0<9FKAd6LoPz3~G32P+F+cuGOJ5gcC@pU_?C2fmix7g2)SUaQO$NS07~H)#fn!Q<}KQWtX}wW`g2>cMld+`7Rxgq zChaey66SG560JhO66zA!;sK1cWa2AG$9k~VQY??6bOmJsw9@3uL*z;WWa7(Nm{^TA zilc?y#N9O3LcTo2c)6d}SQl-v-pE4^#wb=s(RxaE28f3FQW(yp$ulG9{KcQ7r>7mQ zE!HYxUYex~*7IinL+l*>HR*UaD;HkQhkL(5I@UwN%Wz504M^d!ylo>ANvKPF_TvA< zkugG5;F6x}$s~J8cnev->_(Ic7%lGQgUi3n#XVo36lUpcS9s z)ympRr7}@|6WF)Ae;D{owN1;aZSR50al9h~?-WhbtKK%bDd zhML131oi1Bu1&Qb$Cp199LJ#;j5d|FhW8_i4KO1OI>}J^p2DfreMSVGY9aFlr&90t zyI2FvxQiKMFviSQeP$Ixh#70qj5O%I+O_I2t2XHWqmh2!1~tHpN3kA4n=1iHj?`@c<~3q^X6_Q$AqTDjBU`|!y<&lkqL|m5tG(b z8a!z&j^m(|;?SW(l*?tZ*{m2H9d&3jqBtXh>O-5e4Qp-W*a5=2NL&Oi62BUM)>zE3 zbSHb>aU3d@3cGggA`C-PsT9^)oy}%dHCaO~nwOrm5E54=aDg(&HR4S23Oa#-a^=}w%g?ZP-1iq8PSjE8jYaGZu z$I)?YN8he?F9>)2d$G6a*zm0XB*Rf&gZAjq(8l@CUDSY1tB#!i> zW$VfG%#SYSiZ};)>pHA`qlfDTEYQEwN6>NNEp+uxuqx({Fgr zjI@!4xRc?vk^9+~eU|mzH__dCDI=xb{Cd}4bELS9xRaS!*FXMwtMR-RR%SLMh0Cjl zencr8#Su<4(%}$yGVBU-HX{18v=yPH*+%^Vtknc>2A;%-~DrYFx^3XfuVgvZ{#1tA== zm3>IzAM2{3Iv_d1XG{P6^tN3|PkJMnjs&CWN7%7_CmjoVakUhsa&dMv==2~^ri?&x zVdv*rnfVyM+I1^Kg*S=23mR@+0T9BWFZUu~@toA8d)fw6be=`Yb6DSX6D?jB%2YT~ z*aHjtIOozfMhA!Jd*?u5_n!SnX>vX`=Ti-1HA4RiE>eI3vTn zz+>Ccf0HX6Ans-ebOB>RJST-Cyr#4XAk+mAlJgdQnoE{^iIN)OcYFSpgJUmXtl@tT z-^ZuUeSj5hSFrQwqX>~EtZ*{>Gi8Bu9_|o06oNtaXP?E936!a@DsvS*tsB@fa6kEA z5GkjwmH?EgpiG&itsB_Tb1NxtFnvxh_s@9KYX1Sttf?AlI~)z zT=6Y7ulx=}<8Scr_UqU-_z)5gPo%050PsbM*ZLno;_-ow&k?FZJtYmb2hPA$LkP)8 z=^d0Q6PImh6Y|QT?{grxj)S=uBKvY2EQUbm@ns9^yKiP~$DcD)c$5Em`zDSScH%iH zVov&m=cMo`1tYwA=!a}vb_ef_{)Q2?FUqn>BR$6phXQRv^1%=YfyE-F$AR4Q?9D!f zCzB^^#td~4u&l~l#rp2QLfe3+_ub9@+|x+m;=2(sQ`s%gO|j$XBb>A7Q(UydipiMw%igcweV#Cr~SP);q>w`bxts_4} znKHg?X==JDkQl3Y>Ckt%`s{n?Nq-1Fw5~%Mq$CAsi-`yu_bKm zxs#QdE7&vgJD%M84f4SNzSDv)S|V?|$!d5a#lhT5>>YWE4NGqa9-fbmV$=)@k&32kdEYetna>=j@0>V8+wRsL;po!3ivVwh<9tn z2S<1u9DAAQ>x1Sn=fk`)At|quvleV($B|#Kap_lB-F^*yV=wZ{9baUu(uXfokr95^ zA*!*W=5a>$2Ps`-F^+qRQT^{*cN>vipT*4!r#p%{(#I7s z0NN94*q?ib$KJjfDI_sjHNdmEVp5wB&j54O#VoFqBwy)gfA$%)4d_X4q${L9Xom2R3xy&ZBSNgt4a1d7K^CDWa9r zVb-_52m}Vp)`9;ZSKd#|U4ZYj5}Gp49{4utST|=c`~(#>KHF6}CCov1iHYw zt{bWo)A@yF2$~c(nR$rSAaFQ$(Wh{vkG1AlutDMw=mM`C`T=X&|Ad9fb5Od}ROt1z zOpczHqrb4Jo^rSCiW#&o(m7jFamnrsTpQb;*h4o8r#$aZ}2RaT-x2u^^ z%u@YyIv$U^u~@9(XGbSwU@fk6SikH>j+D1jQrYTKGJpW%vUT{!d}7THI5&Sa?~MKy zS0-mvMl+BOcroEJ@hN!2H_?coTEJ5Q<;Nd?yx;eIj4{$$E2?YUO|NtNPJ-PdDf;s} zab;}Mz0kbOI}5*w@3gROcnl#5)wQnEhDBfn!Xhy`u>C}*E~vWpO^HS)FC>8^umI=+ z&H;LW6w#;EF`}vQd_9Muru`KnQVPI9U?(sD)&Dg-0j3#(!fNKVZ_GoYH{la~d*1Yh$TI-TL>mI4vpNb@sU2=IZ8vL%AXUx0 zz{K0|nK(yizLHaeW#ZhRfQXoK^}1$=$#1{Yn002ovPDHLkV1n#w+^+xt diff --git a/cloudinary-android-test/src/main/res/drawable-ldpi/icon.png b/cloudinary-android-test/src/main/res/drawable-ldpi/icon.png deleted file mode 100644 index 1095584ec21f71cd0afc9e0993aa2209671b590c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1723 zcmV;s21NOZP)AReP91Tc8>~sHP8V>Ys(CF=aT`Sk=;|pS}XrJPb~T1dys{sdO&0YpQBSz*~us zcN*3-J_EnE1cxrXiq*F~jZje~rkAe3vf3>;eR)3?Ox=jK*jEU7Do|T`2NqP{56w(* zBAf)rvPB_7rsfeKd0^!CaR%BHUC$tsP9m8a!i@4&TxxzagzsYHJvblx4rRUu#0Jlz zclZJwdC}7S3BvwaIMTiwb!98zRf|zoya>NudJkDGgEYs=q*HmC)>GExofw=92}s;l z_YgKLUT5`<1RBwq{f)K~I%M=gRE6d)b5BP`8{u9x0-wsG%H)w^ zRU7n9FwtlfsZSjiSB(k8~Y5+O>dyoSI477Ly?|FR?m))C!ci%BtY!2Sst8Uri#|SFX&)8{_Ou2 z9r5p3Vz9_GY#%D>%huqp_>U}K45YGy__TE!HZA@bMxX~@{;>cGYRgH~Ih*vd7EgV7h6Pg$#$lH+5=^lj{W80p{{l+;{7_t5cv3xVUy zl_BY4ht1JH*EEeRS{VwTC(QFIVu8zF&P8O$gJsMgsSO35SVvBrX`Vah$Yz2-5T>-`4DJNH;N zlSSY8-mfty+|1~*;BtTwLz_w5 z+lRv)J28~G%ouyvca(@|{2->WsPii&79&nju7ITE6hMX4AQc{|KqZN#)aAvemg3IZ zCr}Y+!r}JU&^>U1C2WyZC<=47itSYQ`?$5{VH?mtFMFFExfYTsfqK%*WzH@Onc#i` zI@a|rm-WbKk{5my{mF}H>Duc$bit&yLAgFfqo2vVbm~?FeG#0F?dSP*kxSo0Ff!o@ z(C}B;r&6pa-NY4;y~5lX8g&*MYQ>yLGd^tDWC4(sGy$Ow-*!eh%xt;>ve|J1q$*w< zh;B#cz!6l2=5bkX#nJ9PJQ`ew8t>7z$bxqf*QB=l2_UB$hK|1EIfloN-jQ=qcwChF zYAkkyp=;FwcnUB3v0=*tMYMA(HdyQ`Og{P|8RRXpj5bgrSmEzSMfBn+{{vpNxw?;5UX;iv9sYxy_`IQHs$i<61a_iv^L>h8s-`D(`e@|IgS*Fj zNGM876Gf;3D8*1UX9a%v>yJKD*QkCwW2AirU(L{qNA)JghmGItc;(H<$!ABY&gBy1vJIEUj-b8%el*o|VkG)LqNx#TG>Jvj^jIte!!+RY z)T4j$7+PoF1AkRBf}R#^T=-q|PaK1$c<4UH)Hpq3$4WA|xtr!ZQLC=*vNE>O6E9kp+5X0eKB$6>C(lPwI@3#oY zhS_%x7e|j!$yG?ECXmh~EH~^OeuK}+sWoJse3Z3?ha3n`MM9KvA?uqpEnBg4Q46)7 zM$p%a$@l;+O}vfvx%XjH`}a{(-HHth9!JaUwV0*VqGR48^gWNYN<&~7x)y$e!X>e` zZ5!6KZoxbKuV9XUDI%#M1~IVh?pNSdeb~6@$y`v|yk=XK+fHxnDqnUK4&=QRNyIVf zYbDM*cI>~qIy*a7=z7uqkw@agd(<=y-Q7L!ty_23SGdXmahO<;N=wB+j;lNm%=OHC zy zU|>La6h%92y4IPufI$9>Xu!@y`TaNgtg&41@PwMwBdmSm7)xAWDLoqjZ==P2#*k7! z3o1)cVSI3KP_!?d8G^Lg0FtLXC~JYdxi|c%h~lXEixY=%VSFF@!*3&&9>(Rb|iK54Cx5;s~PY5iaV1het%w`dgQFBAJ;aFK zImQC}(|QaCFYUm1JVfzSc)ebv=)ObI)0jwJb``}Zj9J0n0Xgn*Zc(rFM9$xh_makZbm-at_v5^SW zM1y1SW@%+FuIy*WR)i3A2N_q;(YO`O!A|Ts^%z}9ZepCj3ytlw#x%N_fNrKKtPh`< z|1{UqF`4LxHaCQ79+E=uUXCOZ35jAMRz%R%0(P!0FMv=sk>Nr8%+OzY^c-M9@+fz=G`qa@v4sF5u-2289-#$**LWnyNNDwDf1( zkUiMnw|y$tn>pQP=Vn!#|17L^5AGrjtBkN$D@v)Z7LXc5EFhLB4<;7Wehh)CMqX|W zqsiZaO^benJ_hwa&V0ub$-_HUk**?g6fm9|!@kguU6*zhK)$qn-<3*kFrYPIaqR=V zUaUvk>@F_89b@tHs8R!*QKY;INJ<2_U+K6Ca3e9Gsl2{qY0%a7J?uICWgHuLfj+MB z=GkAN1&ifT#2u}B+2S#~$5jA(Qn^;H%CCmIae4AE-Dsng|Hl*Ov!z72k3ZnJs{pp| z+pW`DDueC#mEWOf=ucJ!dTL}hzOeiS-i?m2E;`EKz4<&Lu~NnW?peqVU^@<+T3KKu z{yrI%Qy-Z%HEvLUz}n^~m?7x`xuCtNR#L2En!T>dQtIKdS#V-Hzt3RtwTeYtmQ&dR z6qXZvac*oc@BUYEH%@Ylv_1&tSjkbzzU6*h1(3^C`;1z;g_SmOtclS?KWk2VYE zM*oS<=C483XckW?GN|1jfh3Ro(h - - - diff --git a/cloudinary-android-test/src/main/res/values/strings.xml b/cloudinary-android-test/src/main/res/values/strings.xml deleted file mode 100644 index 76455bfc..00000000 --- a/cloudinary-android-test/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Hello my-android-application-it! - my-android-application-it - tests - diff --git a/cloudinary-android/AndroidManifest.xml b/cloudinary-android/AndroidManifest.xml deleted file mode 100644 index 99d7ce4e..00000000 --- a/cloudinary-android/AndroidManifest.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle new file mode 100644 index 00000000..412bf359 --- /dev/null +++ b/cloudinary-android/build.gradle @@ -0,0 +1,125 @@ +import org.apache.tools.ant.filters.* + +apply plugin: 'com.android.library' + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.0' + } + + copy({ + from "src/androidTest/template/" + into 'src/androidTest/' + rename { String fileName -> + fileName.replace("AndroidManifest.xml.sample", "AndroidManifest.xml") + } + filter(ReplaceTokens, tokens: ['cloudinary://123456789:abcdefg@hijklmnop': System.getenv('CLOUDINARY_URL')]) + }) +} + +signing { + sign configurations.archives +} + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +configurations.all { +} + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.2" + + defaultConfig { + minSdkVersion 9 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile project(':cloudinary-core') + testCompile 'junit:junit:4.12' + testCompile project(':cloudinary-test-common') + androidTestCompile 'com.android.support:support-annotations:25.3.1' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile 'org.hamcrest:hamcrest-library:1.3' + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-android' + name 'Cloudinary Android Library' + packaging 'aar' + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-android/pom.xml b/cloudinary-android/pom.xml deleted file mode 100644 index 9b37e2f0..00000000 --- a/cloudinary-android/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - 4.0.0 - - - UTF-8 - UTF-8 - - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-android - jar - Cloudinary Android Library - - - - com.cloudinary - cloudinary-core - ${project.version} - - - com.google.android - android - 4.1.1.4 - provided - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - 4.0.0-rc.2 - - - 23 - - true - - true - - - maven-compiler-plugin - 3.1 - - 1.6 - 1.6 - - - - - diff --git a/cloudinary-android-test/src/main/assets/docx.docx b/cloudinary-android/src/androidTest/assets/docx.docx similarity index 100% rename from cloudinary-android-test/src/main/assets/docx.docx rename to cloudinary-android/src/androidTest/assets/docx.docx diff --git a/cloudinary-android-test/src/main/assets/images/favicon.ico b/cloudinary-android/src/androidTest/assets/images/favicon.ico similarity index 100% rename from cloudinary-android-test/src/main/assets/images/favicon.ico rename to cloudinary-android/src/androidTest/assets/images/favicon.ico diff --git a/cloudinary-android-test/src/main/assets/images/old_logo.png b/cloudinary-android/src/androidTest/assets/images/old_logo.png similarity index 100% rename from cloudinary-android-test/src/main/assets/images/old_logo.png rename to cloudinary-android/src/androidTest/assets/images/old_logo.png diff --git a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java b/cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java similarity index 94% rename from cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java rename to cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java index 271e0d2b..2ec636c7 100644 --- a/cloudinary-android-test/src/main/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java @@ -1,17 +1,20 @@ -package com.cloudinary.test; +package com.cloudinary.android.test; -import android.test.InstrumentationTestCase; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.util.Log; import com.cloudinary.Cloudinary; import com.cloudinary.Coordinates; +import com.cloudinary.ProgressCallback; import com.cloudinary.Transformation; import com.cloudinary.android.Utils; -import com.cloudinary.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; import org.cloudinary.json.JSONObject; +import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; import java.io.File; import java.io.FileOutputStream; @@ -24,32 +27,34 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -public class UploaderTest extends InstrumentationTestCase { +import static junit.framework.Assert.*; + +@RunWith(AndroidJUnit4.class) +public class UploaderTest { public static final String TEST_IMAGE = "images/old_logo.png"; public static final String TEST_PRESET = "cloudinary_java_test"; - private Cloudinary cloudinary; - private static boolean first = true; - - public void setUp() throws Exception { - String url = Utils.cloudinaryUrlFromContext(getInstrumentation().getContext()); - this.cloudinary = new Cloudinary(url); - if (first) { - first = false; - if (cloudinary.config.apiSecret == null) { - Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); - } + private static Cloudinary cloudinary; + + @BeforeClass + public static void setUp() throws Exception { + String url = Utils.cloudinaryUrlFromContext(InstrumentationRegistry.getContext()); + cloudinary = new Cloudinary(url); + + if (cloudinary.config.apiSecret == null) { + Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); } } + protected InputStream getAssetStream(String filename) throws IOException { - return getInstrumentation().getContext().getAssets().open(filename); + return InstrumentationRegistry.getContext().getAssets().open(filename); } private long getAssetFileSize(String filename) { try { - return getInstrumentation().getContext().getAssets().openFd(filename).getLength(); + return InstrumentationRegistry.getContext().getAssets().openFd(filename).getLength(); } catch (IOException e) { return -1; } @@ -72,6 +77,7 @@ private File getLargeFile() throws IOException { return temp; } + @Test public void testUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -87,6 +93,7 @@ public void testUpload() throws Exception { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUploadProgressCallback() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -117,6 +124,7 @@ public void onProgress(long bytesUploaded, long totalBytes) { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUnsignedUpload() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -132,6 +140,7 @@ public void testUnsignedUpload() throws Exception { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUploadUrl() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -145,6 +154,7 @@ public void testUploadUrl() throws Exception { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUploadDataUri() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -162,6 +172,7 @@ public void testUploadDataUri() throws Exception { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUploadExternalSignature() throws Exception { String apiSecret = cloudinary.config.apiSecret; if (apiSecret == null) @@ -184,6 +195,7 @@ public void testUploadExternalSignature() throws Exception { assertEquals(result.get("signature"), expected_signature); } + @Test public void testRename() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -202,6 +214,7 @@ public void testRename() throws Exception { cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); } + @Test public void testExplicit() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -212,6 +225,7 @@ public void testExplicit() throws Exception { assertEquals(url, result.getJSONArray("eager").getJSONObject(0).get("url")); } + @Test public void testEager() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -219,6 +233,7 @@ public void testEager() throws Exception { ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); } + @Test public void testUploadAsync() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -227,6 +242,7 @@ public void testUploadAsync() throws Exception { assertEquals(result.getString("status"), "pending"); } + @Test public void testHeaders() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -234,6 +250,7 @@ public void testHeaders() throws Exception { cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); } + @Test public void testText() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -242,10 +259,11 @@ public void testText() throws Exception { assertTrue(result.getInt("height") > 1); } + @Test public void testSprite() throws Exception { if (cloudinary.config.apiSecret == null) return; - final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()) ; + final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_1")); cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_2")); JSONObject result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.emptyMap())); @@ -257,6 +275,7 @@ public void testSprite() throws Exception { assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); } + @Test public void testMulti() throws Exception { if (cloudinary.config.apiSecret == null) return; @@ -271,11 +290,12 @@ public void testMulti() throws Exception { assertTrue((result.getString("url")).endsWith(".pdf")); } + @Test public void testUniqueFilename() throws Exception { if (cloudinary.config.apiSecret == null) return; - File f = new File(getInstrumentation().getContext().getCacheDir() + "/old_logo.png"); + File f = new File(InstrumentationRegistry.getContext().getCacheDir() + "/old_logo.png"); InputStream is = getAssetStream(TEST_IMAGE); int size = is.available(); @@ -293,6 +313,7 @@ public void testUniqueFilename() throws Exception { assertEquals(result.getString("public_id"), "old_logo"); } + @Test public void testFaceCoordinates() throws Exception { // should allow sending face coordinates if (cloudinary.config.apiSecret == null) @@ -322,6 +343,7 @@ public void testFaceCoordinates() throws Exception { } + @Test public void testContext() throws Exception { // should allow sending context if (cloudinary.config.apiSecret == null) @@ -331,6 +353,7 @@ public void testContext() throws Exception { cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("context", context)); } + @Test public void testModerationRequest() throws Exception { // should support requesting manual moderation if (cloudinary.config.apiSecret == null) @@ -340,6 +363,7 @@ public void testModerationRequest() throws Exception { assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); } + @Test public void testRawConvertRequest() { // should support requesting raw conversion if (cloudinary.config.apiSecret == null) @@ -351,6 +375,7 @@ public void testRawConvertRequest() { } } + @Test public void testCategorizationRequest() { // should support requesting categorization if (cloudinary.config.apiSecret == null) @@ -362,6 +387,7 @@ public void testCategorizationRequest() { } } + @Test public void testDetectionRequest() { // should support requesting detection if (cloudinary.config.apiSecret == null) @@ -373,6 +399,7 @@ public void testDetectionRequest() { } } + @Test public void testAutoTaggingRequest() { // should support requesting auto tagging if (cloudinary.config.apiSecret == null) @@ -403,6 +430,7 @@ public void testComplexFilenameOption() throws Exception { assertEquals(complexFilename, result.getString("original_filename")); } + @Test @SuppressWarnings("unchecked") public void testUploadLarge() throws Exception { // support uploading large files @@ -437,6 +465,7 @@ public void testUploadLarge() throws Exception { assertEquals(1400L, resource.getLong("height")); } + @Test public void testUploadLargeProgressCallback() throws Exception { // support uploading large files if (cloudinary.config.apiSecret == null) diff --git a/cloudinary-android-test/src/main/AndroidManifest.xml.sample b/cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample similarity index 69% rename from cloudinary-android-test/src/main/AndroidManifest.xml.sample rename to cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample index 3c47997d..3669bff2 100644 --- a/cloudinary-android-test/src/main/AndroidManifest.xml.sample +++ b/cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample @@ -1,20 +1,20 @@ + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.cloudinary.android" /> - + \ No newline at end of file diff --git a/cloudinary-android/src/main/AndroidManifest.xml b/cloudinary-android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..03acd910 --- /dev/null +++ b/cloudinary-android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle new file mode 100644 index 00000000..b2d12121 --- /dev/null +++ b/cloudinary-core/build.gradle @@ -0,0 +1,60 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +dependencies { + 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' +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-core' + name 'Cloudinary Core Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-core/pom.xml b/cloudinary-core/pom.xml deleted file mode 100644 index 17327b97..00000000 --- a/cloudinary-core/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-core - jar - - Cloudinary Core Library - - diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle new file mode 100644 index 00000000..281301ff --- /dev/null +++ b/cloudinary-http42/build.gradle @@ -0,0 +1,81 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +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' +} + +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-http42' + name 'Cloudinary Apache HTTP 4.2 Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-http42/pom.xml b/cloudinary-http42/pom.xml deleted file mode 100644 index ddac49e5..00000000 --- a/cloudinary-http42/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-http42 - jar - - Cloudinary Apache HTTP 4.2 Library - - - - com.cloudinary - cloudinary-core - ${project.version} - - - org.apache.commons - commons-lang3 - 3.1 - - - org.apache.httpcomponents - httpclient - 4.2.1 - - - org.apache.httpcomponents - httpcore - 4.2.1 - - - org.apache.httpcomponents - httpmime - 4.2.1 - - - com.cloudinary - cloudinary-test-common - ${project.version} - test - - - diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java index 4d4f7f4c..b3fa3556 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java @@ -1,14 +1,15 @@ 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; -import org.junit.Test; - -import org.apache.http.conn.ConnectTimeoutException; - public class ApiTest extends AbstractApiTest { + @Category(TimeoutTest.class) @Test(expected = ConnectTimeoutException.class) public void testTimeoutException() throws Exception { // should allow listing resources diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle new file mode 100644 index 00000000..841e84ca --- /dev/null +++ b/cloudinary-http43/build.gradle @@ -0,0 +1,80 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +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' + compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.3' + 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' +} + +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-http43' + name 'Cloudinary Apache HTTP 4.3 Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-http43/pom.xml b/cloudinary-http43/pom.xml deleted file mode 100644 index 4a1db79a..00000000 --- a/cloudinary-http43/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-http43 - jar - - Cloudinary Apache HTTP 4.3 Library - - - com.cloudinary - cloudinary-core - ${project.version} - - - org.apache.commons - commons-lang3 - 3.1 - - - org.apache.httpcomponents - httpclient - 4.3 - - - org.apache.httpcomponents - httpmime - 4.3 - - - com.cloudinary - cloudinary-test-common - ${project.version} - test - - - diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java index 706de631..530b644f 100644 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java @@ -4,12 +4,13 @@ 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 @@ -19,6 +20,7 @@ public void testConnectTimeoutParameter() throws Exception { ApiResponse result = cloudinary.api().resources(options); } + @Category(TimeoutTest.class) @Test(expected = SocketTimeoutException.class) public void testTimeoutParameter() throws Exception { // should allow listing resources diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle new file mode 100644 index 00000000..30dac294 --- /dev/null +++ b/cloudinary-http44/build.gradle @@ -0,0 +1,80 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +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' +} + +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-http44' + description 'Cloudinary Apache HTTP 4.4 Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-http44/pom.xml b/cloudinary-http44/pom.xml deleted file mode 100644 index 7309b471..00000000 --- a/cloudinary-http44/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-http44 - jar - - Cloudinary Apache HTTP 4.4 Library - - - com.cloudinary - cloudinary-core - ${project.version} - - - org.apache.commons - commons-lang3 - 3.1 - - - org.apache.httpcomponents - httpclient - 4.4 - - - org.apache.httpcomponents - httpmime - 4.4 - - - com.cloudinary - cloudinary-test-common - ${project.version} - test - - - diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java index 706de631..c39a89e2 100644 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java @@ -4,12 +4,13 @@ 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 @@ -19,6 +20,7 @@ public void testConnectTimeoutParameter() throws Exception { ApiResponse result = cloudinary.api().resources(options); } + @Category(TimeoutTest.class) @Test(expected = SocketTimeoutException.class) public void testTimeoutParameter() throws Exception { // should allow listing resources @@ -27,5 +29,4 @@ public void testTimeoutParameter() throws Exception { "timeout", 1); ApiResponse result = cloudinary.api().resources(options); } - } diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle new file mode 100644 index 00000000..539bc7ba --- /dev/null +++ b/cloudinary-taglib/build.gradle @@ -0,0 +1,80 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +configurations.all { +} + +dependencies { + compile project(':cloudinary-core') + compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.1' + 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' + compile(group: 'javax.servlet', name: 'jsp-api', version:'2.0') { + /* This dependency was originally in the Maven provided scope, but the project was not of type war. + This behavior is not yet supported by Gradle, so this dependency has been converted to a compile dependency. + Please review and delete this closure when resolved. */ + } +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-taglib' + description 'Cloudinary Taglib Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-taglib/pom.xml b/cloudinary-taglib/pom.xml deleted file mode 100644 index 37e3ce93..00000000 --- a/cloudinary-taglib/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-taglib - jar - - Cloudinary Taglib Library - - - - com.cloudinary - cloudinary-core - ${project.version} - - - javax.servlet - jsp-api - 2.0 - provided - - - org.apache.commons - commons-lang3 - 3.1 - - - - diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle new file mode 100644 index 00000000..99e16052 --- /dev/null +++ b/cloudinary-test-common/build.gradle @@ -0,0 +1,70 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +dependencies { + compile project(':cloudinary-core') + compile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' + compile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: publishRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: snapshotRepo) { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + groupId publishGroupId + artifactId 'cloudinary-test-common' + name 'Cloudinary Apache HTTP 4.3 Library' + packaging jar + version projectVersion + + url githubUrl + + scm { + connection scmConnection + developerConnection scmDeveloperConnection + url scmUrl + } + + licenses { + license { + name licenseName + url licenseUrl + } + } + + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + } + + pom.whenConfigured { pom -> + pom.dependencies.forEach { dep -> + if (dep.getVersion() == "unspecified") { + dep.setGroupId(publishGroupId) + dep.setVersion(projectVersion) + } + } + } + } + } +} \ No newline at end of file diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/TimeoutTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/TimeoutTest.java new file mode 100644 index 00000000..ab67bd48 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/TimeoutTest.java @@ -0,0 +1,7 @@ +package com.cloudinary.test; + +/*** + * Marker interface for Junit categories. + */ +public interface TimeoutTest { +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..61f06bc4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +publishRepo=http://localhost:8081/repository/maven-releases/ +snapshotRepo=http://localhost:8081/repository/maven-snapshots/ +publishGroupId=com.cloudinary +projectVersion=1.12.2 +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 \ No newline at end of file diff --git a/pom.xml b/pom.xml deleted file mode 100644 index d3745ace..00000000 --- a/pom.xml +++ /dev/null @@ -1,201 +0,0 @@ - - 4.0.0 - - - org.sonatype.oss - oss-parent - 7 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - pom - Cloudinary Java Client Library Parent Project - - - cloudinary-core - cloudinary-android - cloudinary-taglib - cloudinary-test-common - cloudinary-http42 - cloudinary-http43 - cloudinary-http44 - cloudinary-android-test - - - - Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. - Easily 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. - - http://github.com/cloudinary/cloudinary_java - - - - MIT - http://opensource.org/licenses/MIT - repo - - - - - - cloudinary - Cloudinary - info@cloudinary.com - - - - - scm:git:git://github.com/cloudinary/cloudinary_java.git - scm:git:git@github.com:cloudinary/cloudinary_java.git - http://github.com/cloudinary/cloudinary_java - HEAD - - - - UTF-8 - UTF-8 - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20 - - 3C - true - - - - maven-compiler-plugin - 3.0 - - 1.6 - 1.6 - UTF-8 - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - @{project.version} - - - - - - - - - org.hamcrest - java-hamcrest - 2.0.0.0 - test - - - pl.pragmatists - JUnitParams - 1.0.5 - test - - - junit - junit - 4.12 - test - - - - - - release-sign-artifacts - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - - - - - - local - - - snapshots - http://localhost:8081/nexus/content/repositories/snapshots - - - releases - http://localhost:8081/nexus/content/repositories/releases - - - - - doclint-java8-disable - - [1.8,) - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - -Xdoclint:none - - - - - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..f192e7f7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,8 @@ +rootProject.name = 'cloudinary-parent' +include ':cloudinary-core' +include ':cloudinary-android' +include ':cloudinary-taglib' +include ':cloudinary-test-common' +include ':cloudinary-http42' +include ':cloudinary-http43' +include ':cloudinary-http44' From 68ae43e6bb618f5472bba6802155c4d6c945deca Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 28 May 2017 15:40:07 +0300 Subject: [PATCH 297/592] Add gradle wrapper. --- .travis.yml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54208 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++++++++++++++++++++ gradlew.bat | 84 +++++++++++ 5 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat diff --git a/.travis.yml b/.travis.yml index cad9ae03..41642900 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,4 +16,4 @@ jdk: # - openjdk6 # ciTest is configured to skip the various timeout tests that don't work in travis -script: gradle ciTest +script: ./gradlew ciTest diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f5eb2e3af674c037c7cac40486d9031ecd4b5c71 GIT binary patch literal 54208 zcmaI7W3XjgkTrT(b!^+VZQHhOvyN@swr$(CZTqW^?*97Se)qi=aD0)rp{0Dyr3Sh^@l0Q|jx{^R!d0{?5$!b<$q;xZz%zyNapa2&?V6XpHup!C=N zhX0SFG{20vh_Ip(jkL&v^yGw;BsI+(v?Mjf^yEx~0^K6x?$P}u^{Dui^c1By6(GcU zuu<}1p$2&?Dsk~)p}}Z>6UIf_t;3xI;Q!-+TL0_KK>j|^*1_~2FZI8DApgt9)Is0K z%J~1+74e_0t`7QkcE%3>uaNcc5Wo>I0D#25{$&3iqWYhq!fwWf&Q7)tG=^6Cj*dyH zVV;O9@IO^?RPO3fqiD7CVF17a@${~(@kp48o9}Yem=+7e>XMe8VU@@g$h%DD0v?5D z+Ut$@U9uh{je2vf;M{rAHy=Ddu|8Su9hE8ud5;e#FWa4IFBu0@lbT)kIjFk7YO#M{ z_UhnpU=OAk&ToalWXHkwGoip`@1`{c+$_;-A@{BrvWGd1n0C?8BkXAcUB}hJ9ifTd zXmGZt20UMPJ>A`K9d~etf4lL_aN-^=h4i~6pTIuc#?fUTya6@joGghByrRwEp6ns& zd&Qr~-rb(T@gNSHuKk&*dp$9}97J6mjOctPsOd%;PFee`sqIx2e8rg2HGO8p@5D2N zJx=u&A7;Ik{?$cw0C8-bXwMvJD{jWVnSq0IeuaU4jg5tdi++wN3k_ZD5gaT^Ec7l@ zUa~ZunVxelrCFSv!-1zS-V#TvVX@6od@PY3xJ>bsOEg6JdG z+K!pzxd`b3k$evQeZqTUAhmZe`x3ix`C8_(`>+zEQ}shDw<}~?e3?djT#2A`La?}p zj0O5dtyyJ+H zqUrjYQ4e+(l4AV;pwtqZhOgYr#WFAg%Zl0&ZaMV`k(g8ES^@~SDWgW;9h;u?1=8tr zhoiZ1-y%eL8TN8Sq8K9aFLZim@CgG=D^T~Tb1d}pBShxg|^!mT2n2oXVB%PHm=LAMNN;P01p$$>)?(n+ zt_o!OLxHg0))2ibNbMM!m!??fAwd5`p_L`rU%9-n#*mCH`H8X7;!s8YR z7UZrCqE{W1h4i|mwPjfp^5UjtSi?jhvKz5ei8I0GPZvW3W6GIH3k2?0@tX3AV4CQ< z{qH~842S+LBE*9hht~B2_$?~!>HbXJ(&b;_>F%_5bd|ftS_R))_HLz#Esy9^RdnFu zBgJN*TpZ%VLaeq_Hqj=~1P{T;OVbKxRz>M$deAaQe4Xw1sB`Oqv5TsUHs%!O)+OCZF->(mdYr{;SrB4(jk^ zHZpUJJoL;_=KPKz?N{}Y^7pJ`JJ&N>Z z4bP_VWj74DedTUNKgk1mDPJK;g;5OgKb8A-ZeQTO^LBGyQvwBnMU;rV9wTj}MRDg$ zt|n+v8Y6kiEZ0i2U!2cO(&cn2?=X_Hr!>#iWxdijtGQ0swst>+l#{rbdxYt6E`)(l zK-pE2f}6?2XA9U0G`_uiH`uQ_pKQsee6%AMX{ncaa3&0wZcrqdm$>LNm6iVfiptW$ zyn7m8(>gfzQYFKW{#7zG9^e^<0i0MFp*w9&FtQoL=-)E%@@9Cc zP9_=$*WH0TVDAK@nl1s3ydV_122<5;>(ebz*twQa8o!2`5X@d4!f}PF8SWX5KQ<#O zZuQ|zNK9)>s#<)>#vY)+HTMZch!(bdU^n;EbZH+w)yAsxiM?!Iz9LKQaW76UeCfZ& z&G{g4dIN;I!b>@<2;XBvbcCH!LUaV3T0*)*P6u#2xaYWWJO~LkbIq~$aHvhr*O_SX zx4chD#{l!&zjREIRfzCw^;)IPsP~fnr3%Vm`Gyh-~&Y^4uB1MIJgVi9;LZdkV z;tGh}#^1RI=8T+!GDV6U_DZU8wUcFcLli|4uc)bS%8C-A%%PD+f~?Q(%_TW)0`NvR z)12Xrfts1p=!g*RQ4@C>tJZR5clwKQ*@H@hGwcIRlg6vo5%{FB88gio7O9D-+%d&0 z88=QAENyEV-ZX`EH9c>0KW}#-rJiyvuVq|ZO+gFPZf$Rv-B=@dX3*w4^SSj5J@fQ! zyC%Z-xP)EC?5lHyq#n4UCeO7-b*}O80=5`}y2v@X#xL5mk8=;U#Z-IJ>cn`b_W5KE z0CM~Q;A0JfZpNUVt}7Ba0OqS2fQ$$co!Dhg^Hs9>#H-mGEY9I_uQA^8j`@An8P0IV)J8- zy=Fx~&V9q*kLhS%Fn}efjT>!tx9S08$5~Sujy`}~Wv58)7+=-SAo(hE0+24Ok0-gn zDneFf@Xcl`&(3wUx?cyqL?>3gxsW9~sZ05O(GKM5b^N_0gKg|Ym@ZzM=4I}()T{vg zrx$~)J*tpI0N$#&Ey_zDF~7gE$)>m(Ij*=`;-$>MU(O~yDbLqqCM$i7pAi$c`N7oJ zdW!qfp6-&3jDMiev3r3X_wb=9q=YLZ;Chc-ij#gUZjQfuvasr__nS{NKdAbBw zSDX-k4sib4(T^BnR5U0 zmtS(P^I{9gar_Fr)O1zXpI|rtduN&QQ%za5UiEwLWQlkA@FBy)z5_LB_0?d~u%ATI z=&Ne#|M)~xcXC8A5=2)8zUGE5S7LS{HSs4~AZ{Ih=Pp=_0Bd!!YT8I+Wj_lwPAzR6 zpC*s$B>OJ-0{##F`wysfa;fH6{ucyo{567q2SegQwyri-w)#f@34?^A`XKu0pn`uU z&yJDcJ0WzQ4DLEBAb|Ph9(7t6SR^>lop>^S7xeejx%YRDg-tV;;U^L$S0<$n8I>TczV;H-4&5 zT?qE8Wh52_lPc7X-{!*wGh_7M8q&5&tUV`2v=T*r7aS{w@Y%`zZVN=wny{91zFK{> zy6N=={^v>!Ud<^_e*pkszyJV{{QFAf^qtK39UYCW4Xlj+8}zBX>0Xp`1 zhMan0#!`s*faP1m*3$dQl+6eriIhV!0w|3r7okb@9rbyt9&OS$l-%>}FWw2uahtQU z51v1z%{yz_lDVNIZ~Qk?p6RR)SvQjzEkEBg7e7FDFh7xdT#JZI0K}&V`w}< zsKW1!;WMM3YiKeDjtpKpL)OT;q5Bc^M7Ih^x(G+K6Sv6pkIHe~C_^j8-y%pmk^7qT zUYI-ZC$yq>TV&m&q`E41-pIUic2@0;)u^P>BTZ9H;g-o%pc>2dP@egvoY8w^Y|idJ zRt_E(&gS|SK2PITHWtqM_B@=9>ik~s!9I#JNX`|p>bZawbmhCZLSqhETMj8t219ao zMm9drVP#=M?`4Fbnlq?T#3Qvei7dhctcJ-9F&V-EBm^<($!9tWv)Nc$Dsbs!M`bQf z>y43Vf}fDD##=1L*jB-t&x zZVPr;U3yaKpab^Ek8cvubvkv@uAB)QAFNLUmK)VdrW;N6pb&;!btC7C%kAPrpa02d1c|Oku&)PqpeJo(Q^Yqz{aX zwfQ4OM$+(OzPlm>p=%MVdvklYGFuiQ2U zm(W$DcEay%?jVKdU zk06WOukkk%?Yl!sQ<+^@&8ktWZZp4pYjDk1B0ok{8I!iEr9&)Mu5JbIVG#cD+jvE*J7r-E?MQ<%4G4G`tU(9*$7eXT%)KXb$%^bJIqk3)f!GHu^U3FG`}z z5*qT@rr07#9n_Q;%Zbu6S+PO8=!A#unVJ|I80$N>;M!hX8t%flkZ7(vH#uUji6`72 zAE!j~(RAe?BR`X~F~@HWFwMZQffSth&eD&$r~d_sJD@h9(%Dnha$nohXX#lcZ}yYSa6@i6&aNd=PJiVs`JzkN0P8>}?&4(-C28Wwp_$Kj6H_R5{ch}o{u!yvk ziY8X1E2udz4&M*NTO<(Z4)&E@l+f*>Cr;!?j7LI;zWPbaU6yHEGOygY0ykb1Yymb? z7`(tNL=)_iS3RmbhXHd%(5xW%TU7%&wZ9g19U0e&yKy6xfV&deCr8JGX5H&245TR#;AZ$ZrRyRc=NhTka0F zE(C)7ZH#x0-{o$0q%9GH5}#0vOFzM;Z%~Tg)!v&?_E@674X=&rKhEB$ydk?^=)b`{ z=tE*|g?_)KjR4hU7IlUa)27F9yu(v@o^Bix!^ZMRT9da_JAGOq1IIjV^Rsm&*xXd@ zZ$azq>oCbOC@wT+5*{>U{{;FrU*|#MK5>;Us`V+?jJ}=0z1EjeyLgO_2)5yi_w^R> zaA2*YpF?u14h=xZkfNibNv7JHMHVKk9yD%`O2}?m!e;AZS=qDsDc2Y<=@8t}s_(gq z@BR>HBZ`nL+!-MU)ZnGJM>J2g(Yp^i1?%b_1v@YT) zb@oI~8=DSUb|;&&zE|gMCZFzIz4OpGM>1zh7KGPE7AE}ZxTg(w?WW$k1BOB_VQs~{ zB?zK3pgi1ZwR^Op9q^fM9hP${R+Ba-8%?YjDny#OpWAkYTwRG-$Guen2jy2h^M%;a z45j9D1Qj{qr?$0;dMq--dyT1Q zRP=pw0f)|e|K4k{*`lrc&ZGAEvJe=wA%BTPt*GcQ^#}oBlO=PL9noEQGu3Yb!j0sV znN^-RcL;?V+cxAHj3L~xE3G?cpDpYubLvLQYF44x7?#8#L+?U4SOWr^?7s^3(X~s z%p~`biV0K9W+<%UiXXq2TLKCW_cS67S^bSVh*b!m_fs<32}rK`F!*53Bj!(siro(! znHl{eD?PLoXsmvHU_mx2y~^mV-j%!X-hzvq-LT**r9#_X%-3Q+)jMhg^Hp+MwAW@1 zv|(uAnmuRW9r*#*JC3#6-r=|4itP&tB_Ppzoq@8{YSdI?T@61b%jEHTb#;?e_pt}* zA)6PevRssjAMI4Z zDSI3|iA14*Dw%?Ho{H`E7R#DO;Sb%N16^Qr#heT@o2&raa9+KaN6!oP8a6o3n1eXX zLma;mu>gyBau7dETp8)Hw^L(`XH$cwR`lvp1z$Pe2v#*=T$aZt9*XRd2w`r7M>nbs#qq8*v)eA)1U+rP4NW+42jP+P480(80rJ zW%u&i%#&A%2cvw59mZ}m1_q0d6=okiJ2eg?d$1%Q7d#IWzB;i0Z6#;eluVel4g%p6 zXi!lybOy_Upf2!m(%MX`nRRZi#L+DOfhDp*uybli@s6b3X1_GOV@J3o-P6WE54?+! zdaK)7Dc@_A6U9^t2IFTX6vVcfxdD5$>v>&CVh3GCFMfUQV**; z!W)R4dkC13NlYnE8pvxGujq$orL9fsq${&dVzzbU^9K?)9Ot-J)Jl<&f`Ei!?>ja< z&0M)=MU65R;zJx*IIu)LB}ENBLbygQfB1Y!y+Ktyi&ZVB#SW_FR{ax${qq;$E49d6 z;WEPT=^YWzAo(XI=x4~)M-N-T2U$4UG&ppEkiEoX0iMfV@7fW*2s7aIrT#sH zF{ZDcPee;m$W!(f*%osjh_3>pJ3v2gbLR42VYe`5Jpq1KtGZuX5F-hDZ$WSk@MDd) zXXl8E^S*wLR9+Om{42ZKSV(TLcl#e)(TcSZHiV8}Vu7@jGRRqDHwFalcMfLVISgBw zGy7T>byEB4Nxw5;_oTjX|Jm(X;90~r0s;W200RK9{d)!bN4G~LWoxK!C1mdC# z>|}0h^IxRDf~F)UKhpQK$<~rng?&@=x@Mz$sO81_zNREU0tkL%5DKmrnN&Q!O#2#i zf^@`>M4#Mk9&azMG8bd;d?}pQYMSE*jpOP>52`Of=THUvq+S&mtgQ6oB-V^~=c7Ey zt2Ogzj8YEW&S`iKfr@%(4Z@qxW;vzw?Y$v$=_MQsM%witHuZW~q_6qhjU=`&{M+5O z9-ilvkj1b&u2T7ZOkmgf|cRsdlxFgQK#UMv&f*VLyvem7U&n9E@eaiR4vUe=EKBt!`1hVgqQ zmdb~my*pk-J~J*mBn&~mG9#*W7+VC`x6G4EPOMfhT2W0xxkpTyM~^^(M-z~j$O{-0 zs*p7K$wlKuSSoz}FiK++Ln@TFfT+l1L&5@^MjU~!*Twak08I>c85=^jSPR9q)LQSs2!5vFzQ3~zGU=`whAtT?(orpa$#q_1 zp!jv7j0T|$>DEFSs#q?$ zbgvQ43cUA(eB%^&uQTd{CbqrFrJ37byNTctGXJ5#OiHI442Ah7@WuVp+a+ga%Ti{r zM+1zu{NU)BLTPe4Dj$aL3KQ42oy5HaN9)b#bB;uTK@Ov!R)}#jYM^Fixkc_WIcnP> zpx%0t3l=kdM{AdE85ag=#h$cTYGc`)2@NV zKs41wCN9OTw>rK;N~ZCqU!lN;Y2d~X@ETKgc7_%WXV7C( z?P@%os#ef5?Dv)*nyOhS+91RkL)C>xWra(4ACw6;-#8r77t<62T-<+!R=R&rFhzGqCu8fs+4WbC zbTT(~6w|l)D`x%|#T2EYsi>)p^vxp9hL1Jg#U!R#*c7O#Kr2SvNP$Fz3`7i8q;rm+ zNfHw5xIZQiX#4c8p^IgD9$*VI%{IN5LN^-e{UTbnBSUbwJZ@C~yl(03dDYa@v?BBU z{t?3q*coc;eL7U=PmX&|cQ)WGMVWfnM;K-MmaC^CL!i)+w`&dR2yyIf)?bJ!&rTy& zM>Zslt3)O4RtZ1hRsv6{mb9O|d032U$+J1!q0mV>^nvisPk6m62%7Hi?AN@iVdd`e zJ-t8QPcZZ-b%+w>n6d6noj5-!M0UIyoCXHTB&}{TJSSx;ENSfQ_YOY5lxYc6-P;@f z$8&r=x5<5)?#ax>QoALk=_!#WK;53YDSs_E6E(_))Z7Rp_=JiRUSf4!L;}`&LxZDg zBX8Aac&-J-Is$QIma!pSyk!b$9kE^U44Dm=tk6;|51p_m3Z^?9wX+mEkeGk&=66^tnGsmq3S9Gxk#5L+Ir*U=-wVlNS7R-XxxJbsK3x_jRBs;X#^)WGNM#ffO3{r!#~XdQAN@vI9@nFWi%Y zBS6yf2rcWj;Xlyyg!&dT%O;U$rjFkGsokkbO$Q!+q1h!$Tx2Qbt)ZwO8Zj?vOAO-I zMR?T)z*;-<5#WB+B}y71ofQOrg%Ew6{h6HA-GlwjjS`w-tb>lTy_9sEeYkZIEgxH$GV| ziVTMZHZ@BG<=T;I{3gggUj$ICBg%D1Tu4}Z>A;|7Wjx*LjVg@bY_=l z@Wx$?*VudeRP>JmH2~co{%B~lemZ$As_uXcvE3HI#j2|TX4cez4^g*Z9Nd0E!LLvJ zL}rm^kq}V_v)z@J-_D!V$UPrpI35Kdaw{-%LXTz5$5!0jSy#2RxhizCM!`wcwO*ymdhcAcPrf1x}?5HX)-|T1} zP^fRGqOoDW?b&(X>4WW=<_TeOg*lJZ-Rxne4(pPj-p6!t)h|v=LmNTZqvbJ4sr3;W z!r@mNdGHEg(Vu>Q>%W24?5ai{i)$G)@;D(NAJXVQ!nvlc3pydLiF#OfhNs*zkT2N< zP>P-rH#J7vKkl1q^;uE{;qExD&`HwCQ_1wXCapJl0qT}Ki|BYh=>Btm%c?+oUHnT1 zu)zL*O9VEKPWo0>MD+g&nzH^<4@j!$KC;gY6DEJ)H0(6Z=0sMhpds_*!2KY=tp!u~ zFaHejM`P~eqdD{wwo46;)fW{y-7As2-;s1*<3`E9) zj3iEoA7$a*h}cfzc!8kKI5n;>u20$kp@@hFiq@}Q%qva_fsK&FG=VMTfxtZ<5w}lN zc+arjs~!<|gp}h>+)E-@meh`aFh_j9;Z+MECq*yeRRBnq__mRYhj0LO=vz`;;JZH9 zl$on!j}k)L6slvy6juE0cjr#(cxi;$6qqLoq`B1xGOyrVZ4@+j!bxa&CMw@eG@}xDaNGoqU3RputERQN%a}LT*+R zD+J!EK#PUE%`$B2(dwYPz-d^(xyV!VBID6i;$bNUsc;lqC8pmxkD$TURMab>!;bs! z&_(A$F={!jt5ky=j>`;3vn3MJA~-{#RVGu2CVKvd4Y_GHy>)%4TSt24Nhu{5_sSFU zGS%kGn}YWZriRonR2!ojJx)6s+higW^#Rqkpm&>qO5AJ?<749adish}G@qe@74Hb- z^!?brxA2p+=p1Z=ZxFGLT0@(mi41*-M~&r{Pz*@V-mwjvv?Cs)_XQfwp%o{tn3@Z; zUYo41BHa-e^y>i_Y)<>0=oh_|XnwDN?sUPkR}vg0wGD}aFXRcD)a>X8H~x{9TWb~j z2v3a>S0nCFRA(>LorODZbRWF>l)oH1@BAGD4f$YmBGk;vouT^|;%B1##Z%z}^!}YG zhEMeY>T6N7?p}Scs?#S%&zwDI14nslxxUN@b7%Qpd-P6t&W_)r4!6~MF|CXk1G@7~PhZtFv!GDR3oSZ$wc*(=F+{y~kKy_Xx~hThKCO(Y1(d-A9mNtAC?)`Ob*Vbn5I$P1>!U4FH|i3kNwY^8y*Zp zko^)3el8ig*1E$S>&>L9)6^&djWlF}RUuT$!HdvY^drp^=bPKrwIdj@AE5WURf0HW zrdBV|JVhcRh(T{79hwU5wM_n${Lk@qqS`p0)Po4)i~6=dJGGjTs5uCfC=zHJTsJycXbi1`-|>XFE-G~>DxFsB!%!&Oo4z_^waNjSh43rbUlR){LMDdKoA`tvbH?Ed7xh((Y9^o;1DR;7Cj}88`2X-Xj4P+a)Gh^^~D#Wd?^3V*^>j&*W zYvPTEM#{}!%)j&(^Hcvj<`=NFb^6OD=-Wx_o7*Tl={q?658zjKT~LAhMw&<_6hbit z{4EBBKR9imC}A#c2GI%*lF4TX#+-*V)a?RNpE%Ayw1wLK0(-lj(w&T&k*w(PzV186 zE5NB*k6>$;p6Qsf)|19b`1AGoVhW(sC(9tL6ub@^+H~RYq6&F+zLJTqLJcJE zWhSd;V^ZfPC5bePXn6Wd3$#fYEnV@Yx>kp{d>1hwj|(qOqU-dBl+-T$V-Tn%a5bu$ zhRQ5EcOv%H)YdC$TKBIhlNqT=`-ull^=5O+V)^)7dGh=8ILR_&!j3+w-;;KYss2Xongwkj(@ay7vH8n`GU6eA=)`gWN^pzz zX>TUvQj+z@>QSr?{)V9H#sUOvL{7BV?E|)gr}Gf8Z?UlN3TE@^YkN>bvN{k9&o0T< zV>XuU6Ma?Vo1u9df7dP#43tIk3ZE&B*1?ExS2yScgWwq{MRlO&T?B7$o*wuR=u3H( z=o9pk{LOI~qSp}G z)ki?n9BRtwaJDHS#K`3!Q@~($VQhoXz@vf@L-~rsdpqlcMEA|>8J%v*TH7A_+3fBe zq{Ao6q_Xonq9KY{t5UpJgGiBxeO3wN2L2H>HEWw@t#WnMK0C++x)xoh%E$e+1l_Jv=S z32+|8nKPuuOv?;bL_j^2_uyMX)(tI0-4bZdL zGuBZx^QLcf-Z^hMus}KPjW|V`{w{tlKSId;h8#|Mk;{JsH)9MNDXIa6;fu0x7)Zpz zDS1iR!=66}m5{L~WcMaQefcI|iz#na;lz0Tl=y4Ir_t}g4{O!>vTM;4C{{TSU_S)4 ziMF!tp7Kbw`ER7~u;4-w#$QR!wp|ISzJtC+wQg9Uz}zlDW(qiiWi&!YKLCLo;1V8> zc*FFydcjoS`>cT`LI(!VVraMTjg(Pk)pJ zrmV9EEs31VlOa=HIPSLb!o?C7jDouHni5sU4F5b&$kL~#L0wfC_=4^gm666|b572MjQs zL~P{DD;&?h)bWtTA14!^&c_M8fm zw-U4h7Z)$@%7BF3%^Up7iE$ls<4k(hyc~ez3HJA*83=eav!+aVml5l?H&xB4AYDjo zg6cOjwl#M%os(r$P@|Cq204dQl0s0sUkGVSj`;dkL;?sn(22B1p=?Xaig9AB^pp9t zD>2xDJ@Cdlp~G=|mEZ=>5Qe zP5-Y{8kF#1J1>Vc(vvbmQA0m$CzXnr1tF{&Y)elPYy=LE3vNR4QI(icEoq*I6!jDC z8-y`5i2DirSrB>B42_`H5SyLtc*CCaK;irS{SLhgCz~L)YXX#FN9ngwN+KUXC8Qn7 zDX^JjhsPf`s}~wm^2-%{6?|Zwae!g-1gh>_{3=z)+OrqEUVC7_reuJ}b-T!f!nYNSkQ4?wR&ktwh5%!f zXDjUkE&x7Ed9hr-VFc z58{Bz_B@1^28e3NqS$w);rh0iTJcb>R$~tG{@&1nZW#HrXqQtEg!dQtZ0^|_(m|oG zNaiEdvbf0@hd`hYpTajdNs15NeNrVDi&!${K7|pqgt(99R|auJXporuZ@eZy_ zdi-ZZ=2r#C(GC>WaL>gnEbvd*&-~pEh7VE8x9G^v`DFQ`)-M7&Gb$7wRfa`2}YwtJ@ z>BXDMa!xISdxyLLS#7?nmtL;#<+aeK5^qa$3s@k-^kk$odB!hn*J97%reX${7todQ zBdZoqIqYl1*;bt9W2Os?!&ZQ3g%atB|IjAvMg$7XHe zV+Z;PR~IQTkoQdTa&6|+>GgrPHt`K^eQA?Ke3|iaDK#67YRL>hUl!@i>Z+30T1Wg0 z`%3b4PwDY7nG)0c>MkTX=YV0BGW8q^dN3<1@74C)p4n*^mGU7 znDz`y@v!&AtG6>NaTiBw+PaIL)OyGJ*h%ULjsx`_mj;#K;lr&-gt7o5%W2PMPqSef z_taY1$7)HvWsFvb_259 zIG3P1+z!mjia#+mPb=^CA67;xmE78h%AZ5z#hVfiRa{K23hC;JmGhD~@dKu$mXUx#p&`BuShfqjsi1=S+FiushWf%9hqSukXa~7$sDxgF8-drUqqk|k!yMZ%IcEi(k8*?lx~ zVzsQn!Ph0AazfTio4!2ZnlT}_ss_(8%qs0MT^;XkLO)5g>+E$POk4Q1R`JSnCEO5= z`*h!yDTrB|znNib9Ey{HrjX~sYN^w+Ou*4{Ji8$_!w5nEX9%+R-!Zt;s0V%!1l;X;xT$pp{^%?saKshn# z+st|6o`iZ{rDvyXRbk&W89vfVSDCTSJ}f-lq_pW9oG@N5a_!}K4N&EFOQ+BjyrVdZy|DCX1)xe@ zb7qU_#6RO1T@OP)Y@`S<>Jw_;hvLAk9v1&HY_XDBQS0Ed^x$o1jm$CzqT9{f`fbuR ztKq<16NNub9Jrd9vE;pAbLQymi1Y3TH=|QLG*=DX7JRuVS@BqEWoGFkS;x5^`*Fe+ z!(H|%IX}m;UJa@bNCm4eX*UA>K8TlMA7=>&zMLc`S$or`?kt`k7e#UgR>$Se#WmUk<8At4j36`;2Y?Q&(o^D2?wfDs$`As z!m=wf?s#dX7ieHHw(InLidG(sf^)T8e!ohlFcuJs|7xiDq#X}rE}(h!jB?ctznaTw zW{7b@bvFBx6~R=O9lM9tTOssqTb?&ye%ApyQ==amDcr8gtvK=2Nh>^lbcT3W z5JSFMF|(x|_VR(pq5Hf}V!%Ud?)QsXz&!1ul*VkX$$YTL^jn);z1|;7@g^3mc91KG zbXq~#a8M>~fBCs>DT-Z@(^Z)&&P0)hQPp`eJKSs1rbKob3-O-vhCj%lsiYg69H_Mp zWvw#d7yDergTfJ1N062Mw7bR_d5trqj{@5wA zTD|4jv&M}?w1&>{;RBFrj9B2vwauin+wkC2df0W=S91h@x9_1Uy}@F+f5c?%o}QCm zPPsi6YzPq|PeHAut}QIw5JhP86#-Yc{GDa@)^Cr2nzclj(6`(FTx44^FEeu+U9o6H zHMROwpFP; z$Lzi1G<>&_%n%+s%HGfOf0FC$2I=-9KT!$1I}C%`F{-x28ldWLO_{9)Sg%Uvdb-ue z_`VdN{ze;U(T8g}K!U*^%JQ5QMSVoP2JA!-z16@PDqs`g7JPN=|6&rkrNJ6$Kr0x= z_saCl+1o~Kdr`jjPQXc_nbZe;>IUqI{Ec44x=^jF-)X++`U`ds+35vD}J5^;pTVg2M14sFx3$ZcU#rSYmg*K_y zfGcdsR^!%-FEB3j?xuith}9>uaGOnOR+r!xt>NuMAdmhJh}E6#ra!=5D0Z>-s$-#3 zf#9iy*yI8X!gwWV>x`4(OxP~f6hpAdeH?1PF7SLpdYNK9VSQAKUh|jaukRJBQZM%b znnoG!>27>A0b5|5gJF?pF|RGXP(mP2aj&6ZN1x&VR>p>z+0u7yWOF5B@pO9Yvh|4I z!0(x|tuDcKMAv^Sb#nhb`;d^m!xt`k=LwRR(RBC14ryl! z{LROHg{4n@%`GO{1MWMj?(cplnmGpaXotQXf}H4OOKMy{7%ae7CJhD%CGzY?B{fMoC|1M02`>PF_5%g_qv z#PFrk+~^7#M{06?W`Otboufh0QZ|W%z z>_gw(z%)GgUxtaR)~35-6ah+kbr9@_R($dg2~u!xBG!8SnV=d@@26B&XuP2RWHWwQ+ii^s0Q#nz(1-H|vZXr&Qvw+SL# zsNkw-T#0O14`ExFBPW4GgBBA5KCMo2*@Z1FOr+T*lPfctK zWa0+c56Xine#z#v2q3rOO@qXUAN5vMsfeK zWRtdrv`~ceQlmyO{DsCeQ#B8cq?*R$dZlpu?2!eDF46WnJ18Pvkg<}-%Y@4o_Fz8R_zJx56w@fmD|HIfj zMQPS0``%e;+qP}H(zb2eS(%l#ZQH7}ZQHiZlW)Jhzumk0>~EZlHO6zXZq_qn#*By= z|6dpsG4bTO5=-};R;xo%1BSqeBxlta85gs6koVjRvWu2cRmmFDX z2AWx31wXM!8jb=fiUJmu8Ww06R|7_3Xtye(LD?=!=hySCsbKBe>zRF$e+-tffpQM4 zbXrd%;S|m6HIHMlN|h*XS9H4-6s^=eWH2J$e(;zt2a8N-GsLY)sJD&9@}PJ>YEvkx0= zrQDHbKKV1o3d@?!RybN8e~3SR$--%XnacdsP@6ujC|taV)Tz7{E~l!KRYk9g4%ZcWEzdryaimVbE-g>co&qS%7+S8BC4;z$|=VEwBT}0b< zkfg0j0uvz^@sT-=*~Ks7n8>b^7RhUj3BN{_d7yh%>aqKIMG;VjPz@>CZ<75epBFdM zfWVF2ocZS3#jW(%Qm?XC032Hc;&PS9D znaP>obCz|f8xAUW-RRr_C~V3lvd~I&|CC|ueCoNki79CjpNwTRgqVVzFdm55ZdQ2F z_yNi?y^yV9OeP0hTgc^emwGg^)<|i}$ok;cX_pFjj0a=}0_P^{vjV$cN&7S{eZA13 z2Gr8sirJmepvBij)9LbCjGCIJ{iovo(`B@jyx6vr8LB{?eca-Ajl&ksT!q$Co0NP! zX@eYiCA+iW%1_y}#XO`rPJJ8kISuNDGMT`|#hn33qZ<&i+GnLZ3z@AveOk%Dv0FtK zSC@tYaT+dTYh(NJ%t@U7@>2H&M(M(%WB0_(V%>6Pet*i3h+kE+ir<`prCGibCm3&; z!r&8x#K-GKJueBAB5J_MsksNri=s2+&B|NnclX5VBOXW}0p8A%TNtnWQQcjvS{^*z z(iW(Ic6FG@y^2S-wr$J2(5@6;Y&u2LJe50TnVWSFz>clY0sF$)hz*kv7MNDjPU7yY z0tHtB18<>euXAM45ObqHHB>~G}S&CdXV^H9n3B!Z$d97w+ zKd1f1+dnPl(fi()8g;{L67lvw3~GQVeZrp@tof?oDK;T$LW1+MWdIZLc5TZWqAyim z@O?xNu6ilgt&`P7f-@b3ZYE>Wp))bDk$EeLRS+$KM6{2D8$ad#G)clWo_SCkna|*^ z)ChG1Eof^a)KtB=FPh$_@%Tnr1VP*+b0FAMj5Vnf^%;E zmBh7-Tgh{|=4uI5R1OsBJnFhV--;@C3I! z&Y)G91H9Wa$S<)2ygMw)uYj$7wQ5DsvUk)krGq~&IP*DGjnNWaN7)qbj)_|+PVatO zlA0b;kwv$3JBFW$dZvSBrWIK7?a~u6BP&9?!A}D;%YL82cvSDdN4s_`l|N~=g4R9W zd44q7wu%bkf^79F@{tttShmrPnU*9LiqKeF#2vNAx5M)6z@sOJ3`A6utB|C~3T5-ZPaaJYO(k(JCe zKGlLFWv#14b0LnsF;tQM)^md6*bnVkRms`Cl4%)jcSAo|e;D9Q`}`j3X+H;MBZ$|U zMVm#y)$iGFQZ|;JCSO5Xq^9th-szr4lL_6Xp^3+e3ngNT_m)ht9aw_aO+rp&ZP}d& z-jhgPupat-jmR~2;^1NavHX(ymN>e1cFMY8J22-Efv3JHL_suzeo<@3d+)qzzQWrd zk=toq;i>gN;Seu%NrE(ZK%Wani)9!Q(dPYXEY=e$uQYqUFLsepka=8fDcesZE}86x z*&_a!&*+QCvrJew^>s!tajotWC&@NGL!^t)qB{Eqz19~sbYooH$=#BSz`wSHYivlX zbsH%x)T-wXu7N(u{n7uVttStVdD^JP9i%|JOU_a_e*oG&natl|uF1F-(}#wKG~_fo zSb%}rr1PfcsBkT=TLT>JX_a{AqNQgXY@h;-VrpC-`xLIY0r!-*P_UCb&k}q8N`jq! z(UyZ!95BwnCJ{%+gvJ`jvsf&lT*M?ZN<22l?g%KR@Mm4l+G658n8;GEB@-xZuR|<@ zoH@wdXv+Q|H^*=KZP6K*EF>YYmg7qvS7!P0b`93h`ux1nnqEK6{LMAvN@S8!cWg5{%OA8< z?c%wT?9mYNWIrbmZE-2p9jf4}B0&z#y$(6p)kh>zqqopI#;kaHS2*t<7ic%mFywAG z8JfIe?gv81W>n+ZVstQZ9w~!~s@SB3?YHzVqvf!3&qA!hSkp2@c&i+07L>M`4zJYcSS`;)Io8^1X>X5 zRA!vYg7)vMIhNSh86Bz$s%Lb>5+qG$mQG0JAf^pkF&-cEtT{FW{|MEeOy9w_BGn&G zL%)JLC_eiHgjKaF=V`w-$l zy@tJqS8)m!FC2p0ftp1<*^I(bg4}@}-_r>EY(2DDxj6b5(t)D{Qew19T3|-F2!wBo zB%M~_H^kC%LzpPKV2-8@%5|AeoriS{IN_c(2XdYzng0puJZJ1gky8w0CchStDR+%n z(FrCpntx{wu_5^stD7*H^1#`OCnDIdw*YjHUy#DzX2yr(s}4mWu+1FkWa~2g~+YXzQcmz!8y_lUt4jWODJhF@GSY?gR z5CB`Lq6``kOO13bnl0{Yjv&e=bCG?y)d3i1Nb&1IrY`}#JW6p{(Lp^VMdnbk(5<>~ z{C+yrIKc2oME~C0J65))5}VS$H#++c3MewOfeu;0;}=(HqQDkX z17UE}`6Ni(RyFqH1aLZY?5 zDD6HgB3nBuguFV?~tqf&LGcr*q^I&ontUF-pEX=X;oUIvsKJc~a#juDK8w`2|HGYo&DXm=m z0z?5z?45v}_y1h{v04Y>{u8k`kmk)itj~o?=Pq+8Tmzu3&gyVYlqtEVH~hu{ycLZg z4-P+i_>N&80n7?bkzut>|HD(S_O0e*JsJaUmQdK6=CE3D{0~vDC&JMZey1(J)Rj;_ zw=X$604H3}X7u(4gW4;-fg!jZHza;1*!Bl(4dT`#Kt@N%AlmUywGT`yKDwE)?dD*U zSt{Q=ymcv$L>>QROLMZ17@go`yxw)ZndlkU)^UKVRz2~71!9iyyrM*;Fp%_8Sm zQ_2xxO4*YaxRor^}>#iJ*#+HB1SGf|fi^HGe+McxnHvt_?i)#HsfNYpOZOF`hdpAlhAhJvB*0UwT`{gyVs`l-4rn6 zMdMTv9IHP8G8%^yI3b#T1sXWP6F7k@>H7rNz{{_CVIudA@OqU^C1yrjy8ye#T6W=T zTG=vaEO_p~;JO5<$c!|kD}g+Rj7j%_E5jYG0mJ?TUzuxZgxT`}1S9*i>C5|DY($vO zA}lG4avsrIvcq-ud6cp(?s&uJdJ-iZ*k?cy%qRw4tUL$_RC*)pacg2;g{2<;)-$ISKJ zt{(o$-Bl&T>y?AxHw&_1Z;VQ>D3zQ$zWXj4r*<&e*vYS%EkzR@xHGY`o4)aqV4h$t z3XY%Qd_p5|5|cKU!I_yG>#{>4mxmy~mP55SQuihg<2T$*lem7POzKCcGc0F5+D8a* zP?sIp(RwdVwsD9#PEJ*FgEap5;`^VawLhVHXL*nS0F>z8&;Px&Ci)L1A8M?V2q#0@LaY<9 z_3CVDgS6|MQ(Qyh20O%wRQjdURmW_{({oo_J+)-;O*P;4$>vk%hxgT6=TQ8Y`!fST zdOs=(m))PR3Aa!!9m?cn3ikXwF~9I@2axLPy~JPb5|=uayDZH^(Vib}m3~X5B{6C! zZXMk1vIAJxA|SR3@)y2a6$WIRgzlZnw6^hMYs%}(Rl;?OV}sB_#u3%0~1AU8D!M1TEa>LkW1%CD(iMEk05`94OIy zeU!X@(Phu*yj8nMZh}2zC|(i+tlXu$bI%cY*@?{AcYAk`o%noR!Mbq~S+{#* zaWks#&t-nq;+mI9V@n^+LZ83-qHW8bQ9CQQxqf-6BKpVU zOAP@0qLr&JuWsxp-@DfH5#8F^=-9vs_I!eIdVB;2ZjCx2ySI~)jR<E9%H+@i_10p=ImLFQuD5R&a{So6^ zJ%OFu5LRW@dn`T_jXv_@Lu@=oA`O9uwSX+&;R?`uQH`0TrgKaxDo8Z`RcstQTk3Rg zPlU03Y!lbcWy6D6Am7XW6Oy`=OUbN`Mq4&2PB(F=*7ww5GoK7(6epqtV-q71gPR6V zHjTR>PeeixIHAB?<3fHHHTrBMp(mQ9#Y4nk#x5Nr`YaT|{G1mnI6{KZWEU7i+)wfj z;#Ibony8bGiZWPWjTyt8{ID6t_?=sL)j`DJI7BCP)KC(Q5;IS+-W7apxllCr#M1vl`*ty5$dbkiu&X+N)f?uHG|UIh90S z)bLRTRK>ZU9kY5|sCp{;fKWAE3nS4P#LTZwcCnW;e z)-QZjIYC=HPne&+e3Z}eL4133Qe~-7jheEN&S!g=pJ83*&s?9m1c6*E8G}qLYR!^8 zd@S!!U#Mz_JDoD>$%YI%?9qKLcSeLJr$iM$CP&0!rUou%!@p85n#{wzTUi#x&AhZH&BLX*O6@z-xU=lhSdSmvXJrD8l0|1b;p=%M*vOzv(UdYMg@QJZOwvrMyZuTLKFA)7Bw#-O$tW=z_p6Ok>{51y5f`;4u!cSSqwgHz_o~iZvAJpY4~Zm5_ioH)upg zgI;5EH=J!5t@mmW(HftAOd-G`+0P3oDhE~;BNHJnTrDrKG^kW74t>Z|*=D@8vy>*+ zM@yHpSPv177Kx0NW46crlnyJI6XvNhbblnLi_l6>CEc)slYZ+@Tq2INmJ`k=WetgJ zkMgtXOKS%HLuC-(j`wF4;)sSc&ZFK6t~SNpf!^NGZz;#M)x4szJX>~;hE>~}?g<<2 zP;Z7yTl9!rvV-<+mVsv>4O@CBGQgQ&q0Z|-%8r>cP3G31l8U@7aBw(D)kqfSu5r+j zdYGU-99MIuME}5Lpl}YS+)hqzokL{%)SWpDo3QL%6U~Mg0NL`K6iJWg<`% zf^w|Q4Zo|NYr(>@M~uFD<(tFg{!ux7wkdR!=d}``%|~WfT|}?_%p+^M;HK$ou&gqD zFQoIbL(zM}m1U~q-IL^4dt{b$_?vJUW7I}O=9nGv@&dzV?86sxG}(m5eBEaP}#k(vdu z&LbITTz7)1H>&PtUR4h|7bd2I5c)R_Psl4qP{wVfTOneej^;bc8YQJvrUfFnOmXL6u3-QT(#c z!*{hZs_K4iNKD9}$Qm5f;^^dpf7_-hfMRO5yivrJS-gfGf00$D8{*o8R+WuMnyurM z@|KOJlYd^Nezid>wCmCE4DG z+*DTvk^qlhtD1}$nnMlfCkzri+$wo+3+h^TV)O6vLwx=rgwG^=KLmqVwT#?mg7fyWU`0Nq;In-CQ81)g; zTB!J2^dTp~fk_tC_*v>55UWVzGY2m8348%BN^S2_RFD)qV^n0w#d_qF~ivNiWM5Yx_&zPoSa)Wc3<1!>rkXrQHx(4Pj`7I+`_lvt2$1yNOw58U?87Fg26?Y6BrBHVT8A%5=l>u+-o!+kPXS7^ z7vn$yT9prkZ8a*ko<|HuvB^WM>55trDZM2*hisppC|9Vnbo$Ji7^|AhIHzp<0IX{! z8OvK3D6d7C)C2mmhGquo#n58>E&X!d>bSgMv~rVpS=Sa_74LCX+ItAyWK_fAFUizw zb@f4hme$*}GYFw&AH|pkM1x_~g(gWtZ&!FFe~zxRJk1wcPI?{TSjAY0)cjgOQy$mZ zvc6zMVhr#R0E58kYXQyZ3`-wcwR6Ff@}R>aUQX?dZ{ zr_)c~R)6(UzxUF(P67*yJU2FJQ(()*(fz@;7=THo#B^524@|3ysncciQAXzUBT;=LDT;ptPL8uo7+X_;{CbUYj%zvAQj1*q0r<|jWs;+D5fktH5N0j{Sb zqV*gKFH(cQ)8ZEc&;jouFQQ;3-75(p2_3Kb`uHk9sk=H-Wm|YZD(PvdHOySu-I?A_}C^uh=Yu*C()oQPI5Fjr%Ckt0>-G=(llFVSdMG!G4a z6Rd_aQN$?=(+g>~&ew=2{}pnyJ@!2Ix$zP_w2aq~w(d4e}dInH_$nQNe2 zHwM?9D|ZWbov%*1hbniAhJ(Iy#RhY}?h<>wPD$>FlDn+fxxwtyOZ3E8BEYt$C;3mfDWyWocm(?)3 zXYTV(KBl|;@bd`dk1YZ14De5m;S}n9231d43M3SUCR7P=fsPiR#1CpY>qSH*Cw(ND zJMF4UrqJ|KR0T(&%LxE0{p6Uxh7Waw_6eqb?6o;35p*b0c6t0a&D%JPj!5jcnZA5$ z!T%RC{bwpBWNTw$ZtCoy|KA*)$arg6BmwxLueGB^e_lV|ygb4Sf{dJPCI~oX24!dz zF)yJiyCkB6sC8|Y8%1+MhMPdVZaCwN4$Yj3wSG3HdZxSVj|;80x2Y*zfWvF@V9Asb zJ=SpS2H6aC2^>=|^k6>vI*h8tq{H8hf)}j4(rx5tS1U#n6G9 zuVE*e(1j(%hMd;<;w;59PaRDDKtZ{iN_X8Ex@uM~(de_f=X+*|(RrKmOl!6NBtdSC zO%pL{&QGOTw#!iuO`h|0?N27_Eoiy@;5F$NKFaWz1AQXY1`Z7Do1Rm-W(KbJ!*sNlM3Z$<|vEzXsb%k%hQ6( zz8=d&MBCy_g;x+t|CsI&{n}E9Qo@tr6P>)`5l%E~E(sBuyYTQ_gi62qrPR2s{)q{L z0zIRnYeQSja@wXj@p^b!9?9km<3G#AIc7<2w`e>v=m*TtP4;jAtiuxI!sH1f8I3jUYAP`{=2^4w>c?=P9D&e5=CK23yC|W4V==<(8r9PsEXDc-c z@EPIprn~l9NV_DbQ8zZ;ufVB}30f(c_!AR$y>{~?q}O6lTcdV3Y{>Zb7)A;Zi~@To z-@gh(FgxJDzv$n0H6>ySpc(UlTPi`tNAVpCQm=p%;PK-nVk)5Pa)4X%K}SaMqs8wE z;Kby8r6>dx7>6B6#FSy;;slb!>u13Vi1{rfVj7?oRQ-;>VNlR@GHFZR{G)(Iob&4+ zQ2(>ouopP3i~C(Ld9RNi_Vyw$9?a# zjBowWzu_1EdR@rY+WH$HBV}%5ET`}AeI^jg+WocD6u;S3Hl}|c3yF#sGDzRVqB*#x zghuVrWb!mWRW zYrxNrN1I%Zmpn(4|2P?bl7wQwhz!;mC%~BWHsb*=< z+UfQI1+hP+L$@^Ye8y_Rx~4Ch9Ix3prs{WF1~(nW)f=?AG>_72p7SiFQ&=+)Tj&VU z8!cI>R$TpY3HVC7Vi$C|JzZbf?WEZwPX%|q@D)kc2fom-%-U*5 zwSoUy<0kI(4N}_HkSFE1 zWJr`gI;R7A>|tyaHMD_FshL}aAqEvR(newS)tZdZGiR2b@(_#^LrqxJS<38nLaqbF zDfHmiD;Ae$9xmf}1|O5h*iR0d{B)cXSi#HS9xkqRWArn}mcpm|QTH~Qb&{ZK$GOf@{1Y z5<&h6rVZExA1Lu(tU;4jUR*oO_?ET$1438xk#6)a$az_)l@4^~xB^$8(b<4xTzW!b z6QbKNaiYDssRu2F{jjauX@2RML}X0U^f(jzeGzHD?`?8@n`3*e*H83Cc3V@;O;e=H zXBoyRHTw%%eI+lwGn zA}{o>V}<)qd4652AA_{V6vxy07RS-1<63rC=Ldk?U>GRM9A;h037NPmLpedDI}9nR zQi3uyyw`zmZK`n+4_`4?Cz=t- zIB`X@@e?b!FdHuci)1Ab|9f2FFqOsWq0{Mv{n~`nO6TaceCQck$96!lGj-6yEp`l}!%rU154&bJ{K`}xoWu_34r0BZ z=EhBOa1CNTh|*9%gg0vPwA3$#nV^HHAkU3@FlWe4lzc0)2fmENei0c?Qbc^v6Va%A z|2RoKX`1DiXh-=WWt7c+5woe9;821NvvRS4CF0{^7fz`S%mVdc5w<2X*#Ae5r#dZ{Pd{1rfZLwSkQ)|`m{t-l4{^cg+=zm{Q`&(7H&!`JVqmA8aalnj8YORv!_NoQg>y$#dt{*?PC_Bg}Zs<$;YoB zT5?s2Lvc9OvARj|SMa(E6FL$#d-XL-Hq zF>gHu+onno=Cn_P3n4>;mzLyx(HH!33^}c+z;To%NhS(<^ybXXd2Hl`n8N*fDtb}^ zE>32?>Y_MQlt~CtA+ZTy9b+oaQt_41o7^E;*C zC$^sMvNP-sfHp^~9Hk*?UtQFH4U!JZm!1eIl>uuT^4*Hw@!!S z9@eF+1%)1P7cywH3x`9~1jTM&rG9Xy9;U0>r96i~iqZo&&&ptv+!`jJR4Gr`^Hr-- zWacDWsBl~cOmP0VsI`o73@1-`ThCD@J8K6}CL@~f2#U{&x6}dw=ZHs1bUQynaa{o7 z&al^hP@3?ns9Q?KRLMPgs?BofYo9CzcZ|T)pNm}y>9@#f;?EJ{&Vtgryap6!-AF$6-_sMw?968wD;sUy>LT$n<+b58i8;q1~& z<-+C|;ULo)jO`vE_7)8zoDMnPwpRUAc=&7i51z(zs`crS!`*&d?*3uw`UL6-&UKvR zn-u0P|48aEQi0&4&0H#GH_@Bhpf@f^G2IHgw?_O4P!%(dcFRr8rh^mNx~I{sW1c&T zI0RXHbJMK_qJas>8u?;XiA+^DA{}1*LM@=O#LKe6=71)aY3|7Do>$^5i)!oVzof-~ zd38oB)h3DWNCx=Zvy1#^Z2zCZx{$u3@wX_z*v8S^>6@Sachvs#N8^ks}s}F=F(X|!68sJAt7zh$u2+lqI0L?I2v35xw?ArC&9!O4m*7N#q z2DqZel7a$75=!wrnru0m>D{c94}?|j)3NK-QM%; zjdISUG2L|nlTBMZ(@tpj1NwN>o>;Xt)K+rBb?cdjq2+mKE=}d{O6p#j0H%3mKJckz z9dj7t_#X5CuGT@F7Ej8_Kw~IVtBKf&1F=FOj!X3%t>WA_bwKJUxYGJu%t&;#BgnV6 z&r)pQiGvW6qik4OBvI4X0{w0Sez8AIFCvOldG~buK~A!fI0#aWh?QO1&Y0wDuSB|* zEpJ45qxb8jY%#V8xHkHw>zy);u{|tEU}h=oz!a%%62=BdnxI(>?eAL*x(3;7{WXnc zL_r%577SJ*(TB?y5jacnt-O7YVPFMdX*xL=VQ0tUi2l56qj_-juvN}^nc`gG)G&CV zr>fU<`*ud=(x>>cyPPkF*uF6Px!Dln=p@nLIArP73v}>Yt1l7#lTvRtD}EH!2;70h zvP6AM^eq^5Do3dZ<&RDB;8X|p@!T@5^!8AH5XL(4(p>Y>Y!UMDVk#GZ;ma3)fyC7( zqR~zM4o6g9ALytNM&;VS!4?ZO5PtWgj!w`kPb44z0o4nPz*}&K?jrO~lpy$q&eqGz zKB6NOb@?ls+M#sozZ2bmSgWpabkVn!9)CaoHvI74Vvv8Pmj7gM1V#w_#o+k)W!9(x z<#Ny(VktBwhYb9)2dUqsgvK0D{K1Zv+cy|dQLELC_l^(GWb^F94R9Df7+gp=;MmHh zY1_IorDj-qO+x$9a)QhpXU&=DD(;(lEQq0ccG|tMkU(G(P*|H-QbCOpF1WCJD=4lVx>vZ9M^x}7CVt8R0~s zhLzS-@izmy2k<>1@gw^|#&SQiiU(Z`o2ZzOk$mNM703qiJ_Ehxhq_ zlV1pVrbsmz+^co7ubepUC59z`phz5XUDF2;v~g;5(bu{Wz*NDY^cgH2sd2;aI#Adk zNzu87y$s=)BCseFxMTLJOpmOi-Fm?tMho-ejG2r+8ZW9(E=|}%;?YZco*ZasN~p@y z3KC$%VDjkG^CJG+eGI9#{V!ZsW}KvKFF$hN6bP`e7oS{T-g!4LCX(|W zk$ePI9x?ip5LXg|bucs##FvCBDee1@Px3wFGKOX0J?hJoZ(8NOOOfprT{XaCttLMz zmb=wqZK5be@CCLD_zDsNq_>Ees-l9fKd{ZHpb1}p}R z@x7*|e-#e?b4~yEC5)7pmh9t)_nuoEoUbk;n<8X}6seY`5R*p+goN1qbJA)h&Q`aP z@W~4I3E-2^ES(D+FNl_u>0W>JjSf3{I>YMbnZ$9z$w15?R)ng8$=!k~w(5CLpxEg` zuUcV05P0;nHikD|iXJCOSTy3d8!zp0xtjZh=M*g{`ieeC|V0PT?Np=rv z-(|sFk*Sbyz_}yK*!YS@(lX-#p|w?|7BF@(nO+@m=>yd};j-(G`Vv7^zoL}RZ>Hy* zMk9zslYX&MVSK}ijm1)X;I5Y|%Ol6Zf4YS1Dyxz#q(ol0M z7hi}}WD`4+5aBQXtEvM}-7_d_ElJhv51da}=j`A3Mm2@%y}MeEE2dYrK5rS`&wJIn zK45krd}8duYlKN883Q<*6=KcdvLqFR6UEs#GdvI&72;|`gYc|3FYulGNo-GG*M-1v zO`tVA0rp-4WL)j;_`3vKUt;}BgbvW31x1#Ri2iKYD+cgMk$I!^aWhWN9V#Q`hu$Q* zq~iF7$O*Se1{PkMh>(w2CJb6r=q408jEM&7k!YhD+=+jz6e*U|i{zE1H5Dt3^A+Up z3EA4Lj=_kPCV>0Y#CcRW*GpE@a+xB6iBi1}_(PLXI*_MUi;9xPoO=sBL>o~mD^M|t zJSx;d6fM=UsnK7nRLW9;IgoiFnt)b|3^W45k#?#%4TDye&f=S99KO{-q>QtuP|}d- zHf_`Fq~SPih|-J}O)62<6br)p{TYm^EaVW>(&8R@xHc3AX{`#?X=TP7F@PP*_hyd$ zPQj(jB*L{YxDm_HgQp0QSUCPl)~2a=8S%%HE_fMzr#Cw-iv&S8AiMVta&KrsbyKmzP)+zmtC_B^ zuc5qrpEl@=ETrnJH|Bh($x?T%Z{U-;v}U?|S4nY~0RFN90ApfT)uvgqmp-TQyX~i= z3I=C^Wjje>gyeH^;rnRogFDt=$P|B4T<$#0D?(d%9i$biK|?YBB%U{Wi>&c<%^onB zF1rzuesm*0ahf9IK))nGGdcbm;JA(kAGGCZyqcz#;n|RWKFs$25CnoFEq&nX#V_i< zs?7JjYX$%Zq=S*+n>#gim*u?DuQG0sSk*bD6Y=iqso%@A?M~l7C_$=&d0sStd0sML zM!`Z~{#>#I0Bnp0b_(k}LLiLB?s1yaK>E!DITIbb39g(&e__(@7K8fqN#_2fm>urW z1q02BkE0)=|1pbT6qH>f=+6XAx6?u)1^#;nR0P#qU~39Je8%Y>+)&4gZ-98BT|gJ; zHw3{k5-!`disi`_TCM4R&sk0e{XQVP}ubae4S`OxM+=V2r7J`ZNzk- z9ZIqptse9fIY@Hmv%|%+!@cbtQ1An`dYiX(hGILx&!rI8o`Z|p}E_8244Hu`G4sZ{mXIf8!V9R zd>;xn-__*5rkVdWRQzA=SN`Q-_-9nBY-4HjJ=OAm3HUmc#}xj$JmDE3)@S4ghrbC7 zAs>MU-^nEmAuKFZM%D^z1GzdLy4wD`{nz!J-E~xiN)4h)6SC$ zi6BT~zjL^Gx%QON>3un||8e!_3Si$}QviAol9PT$pge;dL+^G9b`&zRY}gqpMS0?E(IxL;dJ?V9K5}#8q_*O@zuvN^MVuIr?D*fWia}?Ur)!&WF`kT>=6__v4Dn_+&2wzl zA{cb`h#W>Y>zo)2*wDMLPx+W@++8+p>l%0{q;yg!^ouY=G=9vDEoOviPG6GZcgA9|atk5w0G*5Di zAeQINT>H^LHA5@ascQ%p(@@J3&~T31yZVf~kHZ-gLzwO-#q^25_y!#4EyDKZ$NPv< zd@NOts0UyQ;6p-d^eLf5@j@jp6_RIaPut8XsbeI*v()HGNZ8x?(r)p;rWjuuP{s{sEbJcTt0hYw(lLtz07k56%;R2ii&k^cx? z1Sr!`?2R*AdGIPCfFYYET;{e9In)re7LQ4QQr_ z004vEZPx$)b?Lu%&p+$Z>QHV<3ynHdckJ=s0^L{ue{Mp!5yLnDLEmdeVWk9MdhnoN zH!+#G-y>2fsQ~gNdGnMH^5uDY-m0aQDnG?T^D*F0@K*E}pW zPr4pcQ^%!XNgwz2&UrkmI~G^ZZmt?#H{YLIkc64TWe;azUwvNQfAZpu993g}&?JA# z;GON~Dso=v&6b9$?_p;;nQL=moG-5Q>7*_)KbmKx4{;uyD0K(Pyl@Nd#d4zDlyFZT z`Ek?kGwm~J>=9fhDAs{ z+%TJs%z1k?4Kg`F(ueOOMoK!D89dsjHXPhS42MC!C_(yD?r z=G}*9eWW}$87$@)Xfk*b1RHKW3h?3q(o?09kLX@lJr_9?^?3( zDwR(yyTZSqeIGpxYwKym7s9;F>R zEECECm?1+*lc{CCwFRVSsdc2LyeD&!eSmobCI=sSfsNSjD2){3zy9 zGn29JsjEL6d7U5|$1m$HM$Q2Uj*2eT!E2FepfLS0Wv8a-)P>qmblLlJ+M{)nPaYOnj^mbxQEFF@|C`%b$vUb zObNXZJ`58WvzT+B#&FGUm3B~!pqDzor^AT%RH?6WI(-97kS2Op#RQwikYPD`%!g|l zLX_SF7U`s5Ol^?tmr^?z!2WPr>!?B@x$AJ;FEX+5kTR9Ea>d6U?85{X#3iV6Sgq>2 zJKpkx0{%vfMJ|Vd+uv)56NtzF)KQI4A?VNy#u}HgS~p>abZr$2E46G}fLUHP4{F7qHw@g9f9Y7a31^ITPs2=7j;(42 z&dCoN=FbqqIb|OFV5af^rU-5f!LI2VuEzMeB=sGj0F?TM8#%VQZj$yCa z<8pc^evk91NemvLsf5_tTbmfUE-i5=Q*iZ11vfis|Hz40lf@Y;J;J(Td~E_%opCn` z1C{cy+aH(4rZCw-7{mstiI8R$3OvTCryoJ#qeh03nXbg~ee3hZlgCPwrzVc|DB;rS zT#lr3_i?!bImjUVlb5MLR7Yc@jzRSfax-y8wft)PB)`TL%BazK`ve98Z4r(y1O;s) zoPMUu_d!HOWAx`KprbpXZCFnWw9e(wOKb3; zZc;(>5oMRBmIZS^VxMWKdq)nwL3buq&$)H=WFMTtd}40_WQ;}8_}lISh4_a-`twb( zeK&7WFn64ys0MjD|Ma&#;sP)uku)rX2pdQ=2HhF~ z3p}Rvzd95Gux6o~W0VZ;H+K)M4Vy?b78_$`msUw1%byXO-|ox1U+z)If^HNOWO{Ul z^BLLpeBt%KLKEWdc13^3=QFzQ^BUf!o8tlf9KUoc9hj0Z?BpZh!ClZ zD2~52&*8i#OE16Gs7?(UH8?nWA*L%*rcsbtloqvt-4!1-1^Q^sB?6u~a^O+6O!S-KBGx}b6_Q^?T|T@!&EB@7aq1?XJGQc@i^{!XOcu{vurg3(4 zLuF8|AgIF~$@*}&YIiNHy@q~%b^98^_wL3SDyh8X90Y_yQJoqV?j4~qTQ6ofvxn@? z7jGhxGGWm9x+FrBW({1nQaOJ%6I$0FJ&gH%1}KoaJqA!vV-p&SK12j&pXEc)XoHi! z7$4UYW1H*^S+OC}vZ$$syfHSFDt$#Jl5C!=ycpIS>IlAZ(8EY+Adh7SyHtEK2=S$} z!sI?#ulqt0J8+497LN7n0CvU>aj`FXA^_L>UVASWW}*Fpwn=Z`e4<8a?O;Gx3=oOl_PSqyNmmQ z_Mv4rqNgl^4<7o`y2UE9bd;k|L50UC3%5)Tn* zQ#&u}{VG#=?zvrF&J&Y=$qv)ZO>bnx)d^rg2jjbXZ0}Fbx!2&AHe9L4Gc1c;v zXnYeI1j_XNCQmI=OsV6;2kVQA%Cj@dMA@O@ifr-gZ-{p$>nc06VV{N$7EaO(^!O7o zR9a_;aC&WdXKDh`;Odo|H#OSBY041rE3B52%L&9us3sZ6X5Cc3IR`URIaDnsx8^sU zgJ|G5XXLBK`tab1EiFmlDByoH=#jmEE38;RmFq`hItwVZQftxh2e=NSL$eBYia~Bs zef`c7AL>!gQZzK;5W-#1E)5E+Fwtn0{w8rgJGy-2f-hPv@%5|I?}XN@x;sNMf<|c7 z#G~TO+y0gF?{|<_A$PNN1&RWFeZp|+@wJ2vh%(K`&PtVSnG)R&etaSeyLrumnIG4( zP-dZ@9t|3Mk+0nM1{nIw^^|Ey_6grT$6a_&XgTh?OR9l(SUwjhIN%?KWfzDZ^9lhoKTDbb5SbOGxbDk->I4{bs_f6zb1pL|p>E zgp8@w`X&eUOKjvh3Db&L9Ce=yO@G0CvW`}I@GYkKO zrfE5aHiKUyC;RcRF62YXly>VW!xbz-6y9?4N9sI6-Buq|tUh&}V>J2|{UA6W1;uvc z*<`(tgx>F7eWyLlswrt@(MKdVfGXRXy}#aU<1eG>$cC*fl&9g47;oZ+{!n@0+CtGZ zAX2nXhw9ygGZjctkpXfn-xI0~l8seiws3Ti^A$&M2xDziN>qM+ycv3#IXQa@>C+wM zZ4peE0our3);E5x=D?9?2b1v8qaRu9frVp75*FyiW5Af{3#&yVG+$9;6?g>Cs;KT` z+=mOt8CCY6<6S~F7Xuy|-M&u^iWpJY4TveR(tG;l!{;R+?0(fh!-{a@s>u%Af+S8? zr%aFd0JIX$6u~ks-|a-BA&owcrYfNn73{fSOl2>}s3ApThpZx6r6{O^%`I_ojs(8j zTpR8IQG7)%i2ngHEt@V9kee`1I$e~rAu(HB+&W!EV^NgT`OU$)`P6Wl9m!E%$u-cy znuswiPsB=}5k8O1>`6&&#!5c+HE2_`rwr>E{;uL9)lx71eMUiT3#h1`{u;w8_wO6#I z-uN?Jqu8sgO#6?qiFTxYhg)&^&f_*lobsM`$9hOU58F3 zcxcA#ZR6u2Sc3h81aIwIf)58CUisJV(ioE>7g`Q}r0|k57+mgqvR^7H*VxK@Pzozm z=i0=bL$0(ZU}m-24@NJ3xZ4Fw>=+Uee9tuc#2xCO2@R3AM7&E?AHc>I)SVhz zDP$m=V>vs#yCTl> zU1_K9p5wt!#zz8a`E|Aq8-9Qv#*~awdy+w`tJsa{cN+DY>e9%%d~{MKZrQ{sCsMrpMWB)UtqWlG zebwA;km`G=^v@9Oc^@Ve&%H^BY>9Kx#wvjEo?d4<9Wux>Ax6gGs>Z$`lAKc+-e*?MbKbp1+h&A){oe8*w2dWm?t%r^> z57gJ2CHwjVlq|lXcuD9m7=FB*H`>)9T!gTMOg8lTuIO`7B~3I?dhf>6vIL(zTg>-` zn6rhN?Z^+J)RW@^YhBREqAbH6tU!0Y9;%DPd4{z1Gmt3Pxf@rxA@Y>Xahiv`NWjam za5%KnOdu;f<3_iP1P=nqGM?V%@3IGXEXj!;rCsS9X~Jw^83BQJ^m9U^8--&0cSfHu zM5gIeeBls`M)^D|d>qjC-ST`KSYP`gQkE~Vy(-Add=P}0mm$u4K)Y75AtGA|l)O3W z-c|Nj}_Es8o;^ig$~z9n|wA@x@Vj|Lfa{H1wwqS(}k#;wSuvacz2d0fzmH zf~m4X)nSBi9$$y^_bAGT@Axfd8RZo*IHgYv$h<1B`0|uBh|eMpr(4hGUv#_YJ<6jrL7w2lf~lehxhIquH%^XF^6i zftL`qtWCB%K=Y%%kEFG%z81J+=eU-KJTH#)dC-lX`T$1WK!D~Z_dhZ8O8kwPcS+gQ z31t)2#Wvf! zk;9&+bWdPj&gHl&aL4jY62cs0T_j0NLgg^A(^F?nGff;#^?VsI40v7sPDISv)j~LM zNBVPrx;ABR+OFP#gn#BK_`E^gdmuKH$O?hI|-mZt;tdT!V&+MP!MC)~Y@ zAH9z@AY1O=cSc@cv*Gg)Y+&GhI@ZOsuk5^r_9Sz5CJ)Ovz0$^vTs4%uJA=6gxstdh zDRGwXtN`SAeqRbmp}OT|ioF%&7wh*e!9UBq!g$X?hTj%v3DM}FWqNf01|v9c$eLLx!78M| z9T@_HaO}h447Cf3y}Cs1b$pzA;AiVH=1r{NnRi{*yJkIJ^g1RMUr9TdVIv%G@B9oR zY9X!;b*v8y*QRPU9=F=SC0i%yUe!yi^1EA~>I-KzY=?5XIFFcNpg>(HTc6ILN={^8 zapo>S)(Sv(3f`6`#$!yG`Cb7tbT1<*JH{GICAKNBk1uL2dUck5SNq1$d=NdAXwd}H zym!_$UAZU^ZHnm}?Pd*9GIy1HaQQ<-{R7RI;ukaA5@9>X$gLqYP1O~v5=YE+Nm9E@ z@uY-m5E9|=_W9D5eS3Q~Tbhwlm_b~?DwhwFQjE5C*IcuDQ|Mhk0(2xS@9N4E{H{uB z(~`tm%p3ET+Ovs{K{QPc=Wk!#wV-v#wLlfWM(na;q;CBUh-}!(eQd_7RDh#>8*tvN2~*W@GjqVJy_o` zG>6iIV%f|zhrP+-1nzXL`t$2aj zn6karkIP`U+syQoNA0c|o+SRfa!U|Enb^S&T>~?sE8Su1Bn&u&(`*lg@#yQ=nNJaK zfS5KBZpx9~scX}_pE>?!{g9rT0c zVh%K?sd$z7-ala$oF#V}GXiTiwhr%2M)$M-*pyCO@hZ|X zV#6iqrHOY0NnHd^{xrDN@ZcUFh}57l!FfJ9t-E*AdMkqw)6~2%O@SH4WZ|SG?XfWK zzM8WAxf!aHOEFBVB{f%8%A3Q&f|3NyghkANF;%WMFV3<6%`_-(;iX_d=4j{0ovL^^ zLss+~edMk$1??RqY;T@UG>bv0YLz7&QL=htmU0*;$p!L81ELG z*MUUPKGtrAgNJgr%eO^Xqwg-ZFhMXx=&)df)kfb2+-EuHk{0g1i$h(kL<~bXH`=5# z3`dK0qt@>!+&W$qfJj+3G7o)NrqvOFy*Mvw?1BSk2KH2wxh!;+Mr1n_#qpWT?XHw%&ta7t{ufjJ;o6l zZ=xTQbzYH@UC*kgeB|WGYh+n1$(I044Zo(Bv8tjqRb--w6)Mgn#$iHc*+$SQmw>!e z&i^W}qw{XUY|Y*HF!Scf#YdvDxjH5gHL}Lf5S`6{s zxfJVdc)CB6)oZ1p%+sD;<5FpSk<&=%x0b0r$w+X~nV$<7>*xCM5M=m}WMVH0s#H?> z5}eRl*GnCJ?b|MgUfrG;!$p~cKw8;qZ6K(~Y_OP!NjPCC#1<|2U*=itiAG`bc~AL^$tY;mFp`zc$RXZ$E84}BO>K@99!-jG!0(gK z*Fy8yW{+mB0>hGvT6=XS6;!@4b@xR4J{`W(9t9!@R{4!)erc;{l_zR_=qw$fW}o#5 z1Zhu-lX8oH?7F!+Qo?b%6XX-((Ff|sL-v-?MbA_lhs5e^lq)S{iypq12R!gKM$LdH zU&@}o=riATyK#(bl|=|y=wcBXs4A&y;@3L0RYi>)@)|6l)&_iu_$Rf8C%X(PMXLzY z^~agY@3mpp`TQxyd|~9Q0+qDk!nLM;Kn4`7(Y@OvQDLuJ!zlL;i=4bdCVa%+$Iy3& zwkV*b>{6ClsqlU><_zfLKYeQAp&SB~%1qMTrKy!(?XnrL3$@_5oJOjz86mX^##@mWMF=q=G!C1l zuth>08d>FtLXbh2vGsLeEZqkhL^8)`?jr4kr2<{**rR-$>#FNKlwHXwY^@}7?7psBv|;ax-+SX=eZO=yvy-AV3IlIEZNi33c2jNVJ-dHo!`v zz0zwzosp8iJ<*F3q*utvTE;Kv4g?QMlx`};a*Che>sZ=ExH_?6e~B|m%y3C`Zd(4>1<`Hd+|&MU8;EZc{mWQiU8XgF3~KD7cBRd*ogW zG?8Lb&ctw1P4V>lU6Pi`#pTeV47Kwu?x++}oi5)}RAX1ZMz@r15@Alh|E%7kKC!*; zjLCAd8Xt}$qmGH^&c0MlRv^wVfv!K@)@qK7ejEi1_rvgXEv+k5rDx3reN!Tg))&rO zb%Z_$(|xd?GM2I;F@hllI2{k#BQfNt~ZV!w_yt&b)n#Rvn0&2#JtqIJ5pNoW0y`d^7WC0#ny<(R^`y;yyO=LUk5S zcu|bb@ppIr%|mq|h0u1EY>wC(mAv;*VZue?fhI;_@0uCwC7JP)Vp(sd*oZP;k5G^b z$m~XmR+Lhx*glyRiME4;7r)7B<*{f<<>QPWNm5O5tjm{GqMARFgG=8-)f{eNyLeRE z>m=h7LVws)+u?3^d-rZE$hNCz8+-T%%e>U&vXnebz`HI~IDf~rM^RVwd1Lb}j=CU@ zOEArNKF9$MQGgbDWT;oqxE>K^7i!b6@PikGQxK_7#_BDI%r(`X`nC7%&11$fAnnQP z=zPL{hw+9QNb979gN|k#m{`%h6(yDfoQCynuYGpaZ?LsB`e1WZgK3%WLRR- z9gW!CjpRGzAPvy-5@xhYyFqB z@IQTSV$)hsTL6L{z~~8$yK^CYEd@AL0)9p((;#}njFPrpX9kN$3A+E3JKTSpha%>dl*WFScI39g z&iLiY?Fm>5k%~g^{d-wSLLYwkTe^(w&47>;G3wkXuk>CTYEo-8kE6l{sxMdHt8>@e zLLgy~T!+l8mH6_j2QL$sPyLFN{C?0-?G(Xu>1YnF!5B1oB>2<}PzzMJL{x@tGUO49 zcOq%3@G*UylSfed{NvN25+a)CxQUtVHtGTkMNHWovw;PJNWU#cm@}u(r|(pCx;p_~ zd!)0b`C}g$`Zg~Vn#Z*iH9nX#acir|zcse9l*4*Ymq?qMAc!Z8AehSSZI_(Njo#9( znmql3Dq`HkYEX{u6HjDJL~QA+fkz1$o502_9g}fv@S@@+8XPlJXQ%)VAGXN<0%Muoy0^PBG{5aR%;jCTA}@Xw^>LCaxuE=1(VrU2+sgmruRU0s6YG z3A2TP2_q2!he1@!;H_!}aRF2!Rm<$R1aWpE##$P_Fi!(*C7l|kOs(#c6Db2@zS-Ub zZzmkRq!HnWq+Lze%MvI)2!C?m&3A&za|-L9Ap8*u^}p$6S1(UV|*)jM>n(8H>W{A*DxEbrME2R*EG) z6WIx0wRfc2iXvry)Ak-8`*dK5$-oWM&QPHD134A;`D%<0Xkit+-o0)@@6GG*Z*?8) z_;F7!;+WN(ED}Ef3=(~azxAE@%P9CWqCn}JC$c2B{Rz0)@4hl`0IfDUnQ+;ivKbU^Q`xtTF$*Jn-arcg9ffTqbBzo_N;llM!so z7C;i(Hcmz?iJ1m^TE^{V_AAb9H^asWp2xdB9?)yLWtmLm060c(gfO)4vhmgw-i6JT z-8n>E(wRL=MWj`JgJS9()lOV*HdHV(Br;KjK^9Kbkuv)9F-OTxK-v)cLK)f;SF31A z#v&5*+}OP>Fs$8#T0TZ>pq_Xr7`2c}doTxAtW_&}XaKnh^!~7JiROiGOX^N>lGt&R z;A*46vmCgN@H;U02FRdbqvt$RXP48TRusAy!=e27Vq&#c6)$>>p#=Tabh&D>Uu^hC zktKaG<`Cbv?fsvOQ6WR)RmqKg&q&;lt*48P{Gn~FWtm~*nzGVts3XD<*%j()Lk#8) z74ycgOAX{UJ9^WAd|yZ9(IiW#c0h_7E15~))`v_Bt=r~&er+gT0kzYLUB}W95xy5- z5h=DaG((YE$t=PBaYFcZL^Mb;5nRe?^w^*{bes7H^obO|%hyyT2}5}abW_Wkp3PKJ zKmJ%LXhQc2ze|oI9NjwOlavn**LI+BQA?CdVO{4V``?^MMLBh7cjADSNZt0n8iZe7 z5(E(2F-=sem&;nS@p_&g9tPKjf$8f?7z*{S*QrolUz&YF5)thw4AoKrhqe7>cto~^Z253QhGL%jbcCxQ~Vu#TsiT@^Q7lB z>NygJS7>{)P@WvTSBINVo_-|_0-p^k3XEsKGAC!dOI1E^eb#Lsf60%>!|Oh)Nys} z4$jjKPoj6Y#mZR6tcY?C#S?#m%+f7M0+i_w!zDRNTg;JxY2`9W%jx2ly>thXe9i4Uuy;beZ~GhaS2+|>TLKGE?(s^7 zzq8Pj5vHQrM_Z)Jrgk+sey)!D%!BEBV#)2^mO}p;V;s>WmDwe}#oeKO-bWJ;qFrZ*OROFccu`iKioh<^vGUgiD z>5~IqXJ1+cAs=wWI>xGKB%)9!^jDab!;xH>busZ&9Jd&LHy=emY0suNmt=M${bh;BZdM11Crk174BsQV1Jko?LQ#*%C>ooOOCeH@FR zZOGDdDs zp{v9LhC?*Fu)yL&RUq+xUPbG&#%)q*q6u91bEB#EO?w&_J$m`gO5rv@-ctFpZMtCp^M71+W+#-K){d_nzhsw->QxCHPGYl>zEO3ly^T>gR#o zU=Q0aI~MfZU4%E(OU@DX?PsZXwAY6R>*Vfp6!~DTUlb(;+6Dz8a)<;-AZ<@P|9j8* zfgc*T5U{$vf&c>I`s?cUmv!x*drm21TU&to&L68>J{!Yd;s?{>G-M{V0750BEKOxU z^fC$B7|=hp66gkbd6D|i(n~XSX!$6?mnJbU$xWcG(6*fPVm}aQie|q#>4aYmWJoWd zvBj3?jVC=|ml?fDo2~sC6q{s@rFVa!-DkKJ&C6p^joOt?p-O;(o=#V(h!7;K~o zpsO!o`0|DpGbY}M&&;0J&g*ja!H;By#u|fioMB~Jy3rAS5{p1Tm^8LIoBy++ZHlbA zAyC&@Tfp*C!FL+ro#C*SI1JK=jm}I9W!K9HkI|@52A6IF)k}dcf)A%G6?{F=uV67I zD}%7HM%G#aNWw(;N^c%7gS3ki&=`j$k@pRcda&!Z18w843k2T~Hv<<4c zX~XtD>r~{!CJ-(SV;bLj^$;o%7Yi=*Q8+ zBOP_b&^pShhG!Eq%xPnnjs}y7Q5K#x4g(*{11kOwYcJTw>;&`ugQwX#`Kt&YMz$Z; zMiL++0wFS(1W8ZcR=A-4MI4`w%o}9z-X37HSPcM?k;LU944~8Dd09e0_mU*qgw+Y~ zux?^l@@|CVOg5loqH%Ha%0`Sn*2}~l?!)Trmjq6_3E(GyNNE#!EfNk^u2h0nKGf-H zvMS6iRt{=zdp-3w`tn)t}4mOT*Z#Ryn zyIwwC99hx=Xc##OPNcRig_?VX$)LzS15r6* zE!PY2E{;A(Dk3!up^ohoryle6;ON4|EPvimgZ+SOmR*WL8iJ7cbUQYmyzXQ_XKFa=a#e?J->p zwaaloUeAcj75x46B6#u^yp&ukgatdENi5|`n6)}fVp((hBFvtxW|+DY@V*3?kU8xj zC$Yj+yf&T0t1!D+C!@s3@V5g_hlM2I=yasY?t=(C9PQN###2mPn(iNFk~1FpC-rla z&;}^ih%82gNPB-EPr`Nh-VY_<8R@5NaDr2c6X_(a6>mqgM6!)pU4gG9>~+PlmO!>5 z*Z@8yiZlmwSvhD1G9Defn$873s)9FsFj!nE`CBz9RcI1EyKr-`3MBma0DtBDHGI1A z!elT0RupAa0D@8vb=aU`PI_|qlalL#6eQdhxbKN3#=ccly0^gIrQ@e^} zh>wQ+sHq=n@?)saG@O<>HH#EKzjpjawf=Ahp+C1VyZQis{iRzfwVR6QJQHS;GYs4} zyJfN;KBMsR{K_TLfYtc0Kr^_<)1MYiX20W!aoF7{V}4R6Ttfg~jo(Y74^j_1pSe@_ zlUo<2>}L|}E|G@0^IKMkF_G3f_#zCMO73&>9uMy$%vS0-z<4%;}S}qv3(~2?D@y-9LR7)4}?HK&;Z_+`v^P zI%O+DzVvo_#ZeUy@?Cpl`wyV0en|e>+R7I47;V!#dI<@Rx zAIXWjCU2@{55*oLj+fJ0LqfbR*ao2nx{D04cWzP-tTFfa|NRxEK{fJ$1bjuC0YRk< z|KuzB$H(-SZ)cp`isi(6lt*a_6ja_*Z~znA@ofE1n*YAXY$q!dMF|409vdCL5Rz z)KfG}P)SPZ=h?G<@~zhVbetVyB&81AyLg&mcjdR_x9?oVm$V4(k+ZnWWl|07w*37T ztKMgoxI_odB|Q=3m2k8{2_(R)E%An@Mk5KV*h92!+xeU*_h*$yeOD-$Sn%sqcH3<> zE85wS`wkvcsd@C$l+mCd=xy9SuWM2U|pT}bBwZt=G5}J4-Z$oB5t=235Z&hJx=|~ zdWM(C3@@cK~5q(tFzEFWaDAS!oIYB$l*(je1M=d`9Z(vhFm^~WQ#(wRu zN|ymqE(alH-I`USOQ9bUMv7_gp2s@$#<0SL*I5lD7x)+< zGP|E#HGE`lz$rIG?oDmfKP~$`O$ybA`P1Kwav3mbF@DD zsAj#_qaY9!56TUOu0lkH3trAY$cIyL7&wf-u4o@ffpk3q2`25W@%(f$>`=yUYQko> zoO0FXe6(P>eBL)oy8?uAGSrT+^$Npeyed<7+$(_Ob0SB)Lb91`Ur2;PfH%!@PuOIKr!4 z-H5ml8}I~lX053!%wSjQq3TtvTLZow3og|SCDTZC;=vY_C%2-dTRJx`$BDza&@9SM zHo9$!rov33Nq3pOkywhp(cst!m<|bXN6(DaBt*Oyn1zMvTYgwBRX43w;;uH~E8@(? zI=LEDausBcBg~t{K#2cwcheKozSDe6G4@9P_T-SHa`D29snp$&&C3_p(_o8WTyh(S2;*A_I(kKH_ zjqTw|>n`J_52Z9{T~4&0G4L+GKqq4?*kE;NUk4ar8Qi9IFmUtx3a_JatGrT&mT1QLm3VY+dtP- zI+GID&RAd}epn{X=a1oR5Wpj|Dd}H*J3dGh==$Bb2$v{5Jm^cVWSE&v)-moa(ZgH1 zxxJeUMsuwKl^AmAz~PurDT;K=%(81z)m$;L7x9cs5Oy%VMOqQ3^jxn`Y7$}1@pSbV zQSkYJbG~yEgiaAGX#vYb-MrATsB5pZO$7AyV!%e^|D&(-IcnSdsSf(fY+9xOPz9Am z;I0Q~bU0L@W_hea>L50uz%ch0z#!2Rze=8B>hP>TP(`nA-a4F#6gh#u-+-4A3@3i) z1<>MEi&vdU7Fy3385v5l&3D{7e7wJSfb?=MC5!5Q^@^kV)R;dfBL$0;K93Mmpr;lB z6%d)gOiWjxqbU?ksA8KUbvPvvugegYpgvB{1Xi(NkY%9e->2?m;C=>g5J$MhQ{S?$ zj&Fxvu0c-hGHgTr^tP+3$jYg~t-LeStjXSlg#?td%wQTt#%Vgg|NTIjb#tdb$pb-Q zzXK*iq~>k>53`mnqW4sHiv=xh2WuA5EJERurX9{XB!TF_I_` zPLx`-Yzt(`?>cR;N*W8`WZz;{ucZ@*ciTItZwC=Lmwvl@*y-eax16u&pq6c5p>pcP zz`dhVl=;TLHljv(j6R5w!$McSCp&_Kx#lMB9SA<~|jXum}FQ3?EHl{3$hj_%K0_ z0X=^e+-70GzEI`^F*8xOGjtB{8O#k5Dr5c^-kY(tP`+}7s!+0-wcs^|^7rX$l)WJ7 z+;FwBLK}?Pg3P?}?gIO`43O^~^T-&dobS0mK#J}|lwVfm6p2C91+l5^P0%>lg?d|n z%`Px+H1N(4Y!bVF9HBzw7{n>3IHigeNJoIK248O#LfgHz{O{>~=gq5A5x^0?1fbi~ z|Lv*)u+O$P*SD|}a?;iRmjv#g=Jn`daZq{`f0Tt=2Ux~hqVfjvvEE_K6DM0 zd3SbL`O!&&42*lGjg$??z;5L9ViKq{J}Qgf(--fSkWU{^ZXs6ThLK?A7_=9JmV|uU z*|&WPxP5N3JZ12U&}9)_9^|bN9U=q3{3X~8z!WlI9a4JUF0PT^mwtsWM$=z<(U=eIga$Um6;>gRF z?U!Tf7fH0VF)Si2lkM`o-tzhK8ILMt?~lJy8WIWb`8*D@T`m_(tLxon*~-M_er}IK zcpRUz1au&;YFr+b`Y!O;7Yx|<{BT{|!JjZD4#-QJ3oL_|Qg-J)tv{x=d?=OB8u$d3 zQ{Gh(qB_4{Yvro2!K&m#^D=$V@00Iz9PV8dyhU6AoRVY~wjpHma!xP`&ph?j33-J zhJUwdEsXYvvfW5EaHdcqaVO3<&BOYU?0*nwjz_Ro=^VYxRbAGWMzHm8;lmA2X4-__(sxHX9Y-J37GskY> zT@t@2)VL9UG2%ow;81SXTq^-iAjh+FC7YIMyc>Ttl3`gT>V16exi;YF&-JL$c58Ww z$J6r>!Yb$uDD?i$95X^k1TgMs+!enwJFYV>IId8DQ_qFEihj`6X#t$-(velbbij|3 zSnY~op+OTzXfal|g}rpuPK7(@!hz94udJzKe_f8wJ-bF@M~#{pJW=g$)rVIu@w2je z_A8v*{5G-%a}7fmpPw?0c1Q)|6id`eSeEeK!1XouCfQpgX$;y9)uUu94>Ha*Ut-ql zv*QPdhLYe+RM!Rhl#5N6zMbZ#?hgm}x2*bTn`*WXb*w+Cx}DJJ7@})YXREz&jv=UF6cBQJB$cPc`8;)Qs&Y$3%*(&Cf=DPa zoW(5GjHM#XqSyd*$0v>AXcP{qg+*0M+6SyG6tcf0A(~8`Gg2m-o_!~*_@?;=HaZeL zC>=%ojgkKcYrnZ=sFR~f7%DwJXXw&Nk2i(#7~9g66qaermF>+kGJe@|PF{hZN=Blg z*;vT7G5UCtdwKG&$-bNVarM;jx&)+GCG;q2)jz$Mtl^)!lu!VZG(gn>2p{nKQ_mhC zd-wNR_M0yi?4h8bfa=^@!~q!pDS(80KsxruW_mO>`nvkYR(8Lt;!Ds_jtq^SQ4LFp z(^NChFaiVo^B|#k#ilU;F8e@$Q*yxHfk9A!0JQ@^n*tn3e&PZ~&A$r3!|O$n5|Zbm z5t9}MsBZrEZM_6pB0sfWz^7je|HDKEc#1n`}2-JRcHS0#-H!ux$FWif03*CGvmNtnCBk@f%~9Dvf7>Fe76M^*G?wYK+_ZHoa=3xFTdGbG@O0_L}jSLUe|SSYLm~rS*Kcb19*|Rk&(>DoT*vI+a(gc^b>|L~tpS(_ zaDOPpKV$L$diQTI0gFL1ds~3As<5${KA*0xzOAj4wuQFg{~|CU0Az;%yhO!6Wg?#m zfH8kd@L!cCmbBE>HWM@kNTV6+*gq#cdHEPuE~V}8fNstU=xI#P6o4yO?BBBd`%M!A zGzy)Z`RwhC01I7X-Dio|mkfF?Ln|==bZ)>PqWdofGu+=Yy!3??1tgXDpUsd~f^j_s zXa*KQ{)+$ZDw2S|{-zlK61R5RW@f~H3W&a}?U9RW!V#dh(}3Fk%qH_(v;gDxH+cM( zmUch&=d6Av`ULE0p9LCULL2$$8RP))rUAb<&$xiAg!O z!~SzNeJ&ofzXg=EH2k04ZRvJ}0t27`zYOSHgwIHTD@1^a^fwRw@7n!-$U&InC*uMp zg?+$VBls^kBf#wV8@NB)<@abWlde7kc76CQn#k`{u)b`Pmnp8EamzIR2KRRfuUrF%ri8d?ti|if6myy{jUBB_|s?dC&15s^s@lQ(Jv(u z0QgH|{nV#@dGpJxFwb|l{q^Qw7McIG9s9!u`q#$)S-Y1xIi3l!4E|EPzY_d4L&wV} zc$omM<0bv^SihM3Ue^7kF77i6i2Yw!{y$W5U-G=vYJKLJarg_*FP-Vdmgpty zONr8FSQe+h!2Wy3^-olOUlP5P6nrK+a{CL>FZKVkU;Vwn;7hWX3T)40G+zHV+3!d5 z%TU>8%(n? literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..679e9c4b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun May 28 15:37:45 IDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..4453ccea --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..f9553162 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 4ab6ab096faf64f2ce311c54f4598f51008e9561 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 28 May 2017 16:21:15 +0300 Subject: [PATCH 298/592] Fix project to build successfully even credentials. --- cloudinary-android/build.gradle | 8 ++------ cloudinary-core/build.gradle | 4 ++-- cloudinary-http42/build.gradle | 4 ++-- cloudinary-http43/build.gradle | 4 ++-- cloudinary-http44/build.gradle | 4 ++-- cloudinary-taglib/build.gradle | 4 ++-- cloudinary-test-common/build.gradle | 4 ++-- gradle.properties | 2 +- 8 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle index 412bf359..2536e0f7 100644 --- a/cloudinary-android/build.gradle +++ b/cloudinary-android/build.gradle @@ -20,10 +20,6 @@ buildscript { }) } -signing { - sign configurations.archives -} - sourceCompatibility = 1.7 targetCompatibility = 1.7 tasks.withType(JavaCompile) { @@ -74,11 +70,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index b2d12121..d5f7433d 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -18,11 +18,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index 281301ff..ed8e3a30 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -30,11 +30,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 841e84ca..f3646041 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -29,11 +29,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index 30dac294..cd652a37 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -29,11 +29,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 539bc7ba..82815a84 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -29,11 +29,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 99e16052..28d01093 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -19,11 +19,11 @@ uploadArchives { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } repository(url: publishRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } snapshotRepository(url: snapshotRepo) { - authentication(userName: ossrhUsername, password: ossrhPassword) + authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") } pom.project { diff --git a/gradle.properties b/gradle.properties index 61f06bc4..db14eabd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ publishRepo=http://localhost:8081/repository/maven-releases/ snapshotRepo=http://localhost:8081/repository/maven-snapshots/ publishGroupId=com.cloudinary -projectVersion=1.12.2 +projectVersion=1.12.1-SNAPSHOT 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' From a535bdb701baf845d8fb941387775e88586526cb Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 28 May 2017 16:44:18 +0300 Subject: [PATCH 299/592] Configure build tools and android version for travis. --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 41642900..71d7ca3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,13 @@ language: android +android: + components: + - tools + - platform-tools + - tools + - build-tools-25.0.2 + - android-25 + before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ From 5b75eadf3a609a01f69f64e159336c59dd264b31 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 28 May 2017 16:53:57 +0300 Subject: [PATCH 300/592] Add android repository to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 71d7ca3f..f6b72889 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ android: - tools - build-tools-25.0.2 - android-25 + - extra-android-m2repository before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock From ff0e39582d43f8ebcb89c673f703539fb4b23d77 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 28 May 2017 17:52:06 +0300 Subject: [PATCH 301/592] Remove pom (fixes merge error) --- cloudinary-test-common/pom.xml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 cloudinary-test-common/pom.xml diff --git a/cloudinary-test-common/pom.xml b/cloudinary-test-common/pom.xml deleted file mode 100644 index dc408bcb..00000000 --- a/cloudinary-test-common/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - - com.cloudinary - cloudinary-parent - 1.12.1-SNAPSHOT - - - cloudinary-test-common - jar - - Cloudinary Test Common - - - com.cloudinary - cloudinary-core - ${project.version} - - - org.hamcrest - java-hamcrest - 2.0.0.0 - - - junit - junit - 4.12 - - - From 659ebdb701055d901f6e4984ea300b3ab2ac8e9b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 29 May 2017 09:19:11 +0300 Subject: [PATCH 302/592] Consolidate java gradle config, setup test logging. --- .travis.yml | 7 +++++-- cloudinary-core/build.gradle | 8 +------- cloudinary-http42/build.gradle | 14 +------------- cloudinary-http43/build.gradle | 14 +------------- cloudinary-http44/build.gradle | 14 +------------- cloudinary-taglib/build.gradle | 12 +----------- cloudinary-test-common/build.gradle | 8 +------- java_shared.gradle | 18 ++++++++++++++++++ 8 files changed, 29 insertions(+), 66 deletions(-) create mode 100644 java_shared.gradle diff --git a/.travis.yml b/.travis.yml index f6b72889..1b6c9810 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,11 @@ cache: jdk: - oraclejdk8 - - oraclejdk7 - - openjdk7 + +# Android build tools 25 require java 8 - until project separation java 7 tests are disabled +# - oraclejdk7 +# - openjdk7 + # Temporarily disabled, test fail because Hamcrest needs java 1.7 # - openjdk6 diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index d5f7433d..c82b9ef1 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -1,10 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} +apply from: "../java_shared.gradle" dependencies { testCompile group: 'org.hamcrest', name: 'java-hamcrest', version:'2.0.0.0' diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index ed8e3a30..4d182ba6 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -1,10 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} +apply from: "../java_shared.gradle" dependencies { compile project(':cloudinary-core') @@ -18,12 +12,6 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - } -} - uploadArchives { repositories { mavenDeployer { diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index f3646041..d0be4b30 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -1,10 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} +apply from: "../java_shared.gradle" dependencies { compile project(':cloudinary-core') @@ -17,12 +11,6 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - } -} - uploadArchives { repositories { mavenDeployer { diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index cd652a37..b8d3acb0 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -1,10 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} +apply from: "../java_shared.gradle" dependencies { compile project(':cloudinary-core') @@ -17,12 +11,6 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - } -} - uploadArchives { repositories { mavenDeployer { diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 82815a84..762ebd36 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -1,14 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -configurations.all { -} +apply from: "../java_shared.gradle" dependencies { compile project(':cloudinary-core') diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 28d01093..3aac3d5d 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -1,10 +1,4 @@ -apply plugin: 'java' - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} +apply from: "../java_shared.gradle" dependencies { compile project(':cloudinary-core') diff --git a/java_shared.gradle b/java_shared.gradle new file mode 100644 index 00000000..133bb377 --- /dev/null +++ b/java_shared.gradle @@ -0,0 +1,18 @@ +apply plugin: 'java' + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +test { + testLogging.showStandardStreams = true + testLogging.exceptionFormat = 'full' +} + +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} \ No newline at end of file From cb109ac1fe384831d7092d28fa5a08d1709daad3 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 29 May 2017 09:54:08 +0300 Subject: [PATCH 303/592] Fix java shared config (Move some more specific configs into subprojects) --- .travis.yml | 2 +- cloudinary-http42/build.gradle | 6 ++++++ cloudinary-http43/build.gradle | 6 ++++++ cloudinary-http44/build.gradle | 6 ++++++ java_shared.gradle | 6 ------ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1b6c9810..1785a2e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,4 @@ jdk: # - openjdk6 # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew ciTest +script: ./gradlew clean ciTest diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index 4d182ba6..3aae1b20 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -1,5 +1,11 @@ apply from: "../java_shared.gradle" +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + dependencies { compile project(':cloudinary-core') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index d0be4b30..ec07bcbd 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -1,5 +1,11 @@ apply from: "../java_shared.gradle" +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + dependencies { compile project(':cloudinary-core') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index b8d3acb0..c1c7e151 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -1,5 +1,11 @@ apply from: "../java_shared.gradle" +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + dependencies { compile project(':cloudinary-core') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' diff --git a/java_shared.gradle b/java_shared.gradle index 133bb377..00acbad9 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -9,10 +9,4 @@ tasks.withType(JavaCompile) { test { testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' -} - -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - } } \ No newline at end of file From e7e19a5b3597daf3c83a19bcf763e1fbb723fd6e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 29 May 2017 10:13:15 +0300 Subject: [PATCH 304/592] Mark more timeout tests for exclusion --- .../src/test/java/com/cloudinary/test/UploaderTest.java | 3 +++ .../src/test/java/com/cloudinary/test/UploaderTest.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java index a43a0063..efbf9190 100644 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java @@ -4,12 +4,14 @@ 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 @@ -19,6 +21,7 @@ public void testConnectTimeoutParameter() throws Exception { ApiResponse result = cloudinary.api().resources(options); } + @Category(TimeoutTest.class) @Test(expected = SocketTimeoutException.class) public void testTimeoutParameter() throws Exception { // should allow listing resources diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java index a43a0063..efbf9190 100644 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java @@ -4,12 +4,14 @@ 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 @@ -19,6 +21,7 @@ public void testConnectTimeoutParameter() throws Exception { ApiResponse result = cloudinary.api().resources(options); } + @Category(TimeoutTest.class) @Test(expected = SocketTimeoutException.class) public void testTimeoutParameter() throws Exception { // should allow listing resources From d264a180bddb9cd5e3c98527e00a8b2b959b9263 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 1 Jun 2017 10:58:33 +0300 Subject: [PATCH 305/592] Remove maven from git ignore, update test AndroidManifest.xml path. --- .gitignore | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index daa54e0d..8c7d1af1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,23 +33,10 @@ test-output/ .classpath .project +# intellij +.idea/ *.iml appengine-web.xml +cloudinary-android/src/androidTest/AndroidManifest.xml -cloudinary-android-test/src/main/AndroidManifest.xml - -# Maven - -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties - -# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) -!/.mvn/wrapper/maven-wrapper.jar \ No newline at end of file From 710d9980412d49fc34e02c887efdcbd3fe0bfc39 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 1 Jun 2017 15:26:17 +0300 Subject: [PATCH 306/592] Add description to the generated pom files. --- cloudinary-android/build.gradle | 1 + cloudinary-core/build.gradle | 1 + cloudinary-http42/build.gradle | 1 + cloudinary-http43/build.gradle | 1 + cloudinary-http44/build.gradle | 3 ++- cloudinary-taglib/build.gradle | 3 ++- cloudinary-test-common/build.gradle | 1 + gradle.properties | 1 + 8 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle index 2536e0f7..55bf12b4 100644 --- a/cloudinary-android/build.gradle +++ b/cloudinary-android/build.gradle @@ -81,6 +81,7 @@ uploadArchives { groupId publishGroupId artifactId 'cloudinary-android' name 'Cloudinary Android Library' + description publishDescription packaging 'aar' version projectVersion diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index c82b9ef1..5b35f8db 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -23,6 +23,7 @@ uploadArchives { groupId publishGroupId artifactId 'cloudinary-core' name 'Cloudinary Core Library' + description publishDescription packaging jar version projectVersion diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index 3aae1b20..2fa4364c 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -35,6 +35,7 @@ uploadArchives { groupId publishGroupId artifactId 'cloudinary-http42' name 'Cloudinary Apache HTTP 4.2 Library' + description publishDescription packaging jar version projectVersion diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index ec07bcbd..13450a28 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -34,6 +34,7 @@ uploadArchives { groupId publishGroupId artifactId 'cloudinary-http43' name 'Cloudinary Apache HTTP 4.3 Library' + description publishDescription packaging jar version projectVersion diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index c1c7e151..fd9793e0 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -33,7 +33,8 @@ uploadArchives { pom.project { groupId publishGroupId artifactId 'cloudinary-http44' - description 'Cloudinary Apache HTTP 4.4 Library' + name 'Cloudinary Apache HTTP 4.4 Library' + description publishDescription packaging jar version projectVersion diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 762ebd36..998f0b58 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -29,7 +29,8 @@ uploadArchives { pom.project { groupId publishGroupId artifactId 'cloudinary-taglib' - description 'Cloudinary Taglib Library' + name 'Cloudinary Taglib Library' + description publishDescription packaging jar version projectVersion diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 3aac3d5d..889d6733 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -24,6 +24,7 @@ uploadArchives { groupId publishGroupId artifactId 'cloudinary-test-common' name 'Cloudinary Apache HTTP 4.3 Library' + description publishDescription packaging jar version projectVersion diff --git a/gradle.properties b/gradle.properties index db14eabd..7fcff43d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ publishRepo=http://localhost:8081/repository/maven-releases/ snapshotRepo=http://localhost:8081/repository/maven-snapshots/ publishGroupId=com.cloudinary +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. projectVersion=1.12.1-SNAPSHOT githubUrl=http://github.com/cloudinary/cloudinary_java scmConnection=scm:git:git://github.com/cloudinary/cloudinary_java.git From f41eedf284986ca1b9586f0d2a5030aa47a0cbf5 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 1 Jun 2017 16:12:47 +0300 Subject: [PATCH 307/592] Add publish sonatype urls, verify cloudinary-core tests run with task ciTest. --- build.gradle | 1 - cloudinary-core/build.gradle | 3 +++ gradle.properties | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 130271ed..76fe086a 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,6 @@ allprojects { repositories { jcenter() mavenCentral() - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } } diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index 5b35f8db..19403ca1 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -1,5 +1,8 @@ apply from: "../java_shared.gradle" +task ciTest( type: Test ) { +} + dependencies { testCompile group: 'org.hamcrest', name: 'java-hamcrest', version:'2.0.0.0' testCompile group: 'pl.pragmatists', name: 'JUnitParams', version:'1.0.5' diff --git a/gradle.properties b/gradle.properties index 7fcff43d..9d8b6cf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -publishRepo=http://localhost:8081/repository/maven-releases/ -snapshotRepo=http://localhost:8081/repository/maven-snapshots/ +publishRepo=https://oss.sonatype.org/service/local/staging/deploy/maven2/ +snapshotRepo=https://oss.sonatype.org/content/repositories/snapshots/ publishGroupId=com.cloudinary 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. projectVersion=1.12.1-SNAPSHOT From cccce91492ba6a1be9684e7b0a402e449d866e1c Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 1 Jun 2017 16:48:57 +0300 Subject: [PATCH 308/592] Change property names to be compatible with gradle install plugin. --- build.gradle | 2 ++ cloudinary-android/build.gradle | 4 ++-- cloudinary-core/build.gradle | 2 +- cloudinary-http42/build.gradle | 4 ++-- cloudinary-http43/build.gradle | 4 ++-- cloudinary-http44/build.gradle | 4 ++-- cloudinary-taglib/build.gradle | 4 ++-- cloudinary-test-common/build.gradle | 4 ++-- gradle.properties | 8 +++++--- 9 files changed, 20 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index 76fe086a..d0c067a6 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,8 @@ allprojects { jcenter() mavenCentral() } + + project.ext.set("publishGroupId", group) } subprojects { diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle index 55bf12b4..3dad432a 100644 --- a/cloudinary-android/build.gradle +++ b/cloudinary-android/build.gradle @@ -83,7 +83,7 @@ uploadArchives { name 'Cloudinary Android Library' description publishDescription packaging 'aar' - version projectVersion + version version url githubUrl @@ -113,7 +113,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index 19403ca1..b3ad2026 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -28,7 +28,7 @@ uploadArchives { name 'Cloudinary Core Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index 2fa4364c..a6903dd6 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -37,7 +37,7 @@ uploadArchives { name 'Cloudinary Apache HTTP 4.2 Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl @@ -67,7 +67,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 13450a28..246cd070 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -36,7 +36,7 @@ uploadArchives { name 'Cloudinary Apache HTTP 4.3 Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl @@ -66,7 +66,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index fd9793e0..470ed770 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -36,7 +36,7 @@ uploadArchives { name 'Cloudinary Apache HTTP 4.4 Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl @@ -66,7 +66,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 998f0b58..366ea0f7 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -32,7 +32,7 @@ uploadArchives { name 'Cloudinary Taglib Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl @@ -62,7 +62,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 889d6733..8f2ae954 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -26,7 +26,7 @@ uploadArchives { name 'Cloudinary Apache HTTP 4.3 Library' description publishDescription packaging jar - version projectVersion + version version url githubUrl @@ -56,7 +56,7 @@ uploadArchives { pom.dependencies.forEach { dep -> if (dep.getVersion() == "unspecified") { dep.setGroupId(publishGroupId) - dep.setVersion(projectVersion) + dep.setVersion(version) } } } diff --git a/gradle.properties b/gradle.properties index 9d8b6cf6..39aba56a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,6 @@ publishRepo=https://oss.sonatype.org/service/local/staging/deploy/maven2/ snapshotRepo=https://oss.sonatype.org/content/repositories/snapshots/ -publishGroupId=com.cloudinary 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. -projectVersion=1.12.1-SNAPSHOT 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' @@ -11,4 +9,8 @@ licenseName=MIT licenseUrl=http://opensource.org/licenses/MIT developerId=cloudinary developerName=Cloudinary -developerEmail=info@cloudinary.com \ No newline at end of file +developerEmail=info@cloudinary.com + +# These two properties must use these exact names to be compatible with 'gradle install' plugin. +group=com.cloudinary +version=1.12.1-SNAPSHOT From 625f8f15e05d52be42935dfb93615c30c3bd0458 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Sun, 11 Jun 2017 23:06:18 +0300 Subject: [PATCH 309/592] Refactor gradle copy task. --- cloudinary-android/build.gradle | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle index 3dad432a..bb21c1bd 100644 --- a/cloudinary-android/build.gradle +++ b/cloudinary-android/build.gradle @@ -10,14 +10,6 @@ buildscript { classpath 'com.android.tools.build:gradle:2.3.0' } - copy({ - from "src/androidTest/template/" - into 'src/androidTest/' - rename { String fileName -> - fileName.replace("AndroidManifest.xml.sample", "AndroidManifest.xml") - } - filter(ReplaceTokens, tokens: ['cloudinary://123456789:abcdefg@hijklmnop': System.getenv('CLOUDINARY_URL')]) - }) } sourceCompatibility = 1.7 @@ -119,4 +111,15 @@ uploadArchives { } } } -} \ No newline at end of file +} + +task copyFiles(type: Copy) { + from "src/androidTest/template/" + into 'src/androidTest/' + rename { String fileName -> + fileName.replace("AndroidManifest.xml.sample", "AndroidManifest.xml") + } + filter(ReplaceTokens, tokens: ['cloudinary://123456789:abcdefg@hijklmnop': System.getenv('CLOUDINARY_URL')]) +} + +preBuild.dependsOn(copyFiles) \ No newline at end of file From 750998476e678a0e60c455aa7e94e4ecbd648a34 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 12 Jun 2017 08:40:03 +0300 Subject: [PATCH 310/592] Fix `testDeleteByToken`. --- .../src/main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 1 file changed, 1 insertion(+), 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 d90c07f6..0d4a60b7 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 @@ -85,7 +85,7 @@ public void testUtf8Upload() throws IOException { @Test public void testDeleteByToken() throws Exception { - Map options = ObjectUtils.asMap("return_delete_token", true, "tags", new String[]{SDK_TEST_TAG, uniqueTag}); + Map options = ObjectUtils.asMap("return_delete_token", true, "tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG}); Map res = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); String token = (String) res.get("delete_token"); res = cloudinary.uploader().deleteByToken(token); From 83cb37d577051c1c1be649393f1cb1335dc4ab19 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 12 Jun 2017 09:36:14 +0300 Subject: [PATCH 311/592] Fix commmon test module's title. --- cloudinary-test-common/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 8f2ae954..17fe3d5d 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -23,7 +23,7 @@ uploadArchives { pom.project { groupId publishGroupId artifactId 'cloudinary-test-common' - name 'Cloudinary Apache HTTP 4.3 Library' + name 'Cloudinary Test Common' description publishDescription packaging jar version version From e086a3acb29a6126eae24233ce1b31cb59eda0a0 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 12 Jun 2017 09:36:34 +0300 Subject: [PATCH 312/592] Modify gradle to download sources. --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 679e9c4b..3c093feb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun May 28 15:37:45 IDT 2017 +#Mon Jun 12 09:17:06 IDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-all.zip From 24e73385a05f97c3db6133f711e80dd3d186d789 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Mon, 12 Jun 2017 13:11:21 +0300 Subject: [PATCH 313/592] Add signing to gradle configuration. --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index d0c067a6..59aa535c 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,9 @@ allprojects { mavenCentral() } + signing { + sign configurations.archives + } project.ext.set("publishGroupId", group) } From d8a22e91518ea962ac771ba87480480c39362f63 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 13 Jun 2017 10:42:21 +0300 Subject: [PATCH 314/592] Add tasks for javadoc jar and sources jar, used in uploadArchives. (#92) --- cloudinary-android/build.gradle | 21 ++++++++++++++++++++- java_shared.gradle | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle index bb21c1bd..2353e7d4 100644 --- a/cloudinary-android/build.gradle +++ b/cloudinary-android/build.gradle @@ -122,4 +122,23 @@ task copyFiles(type: Copy) { filter(ReplaceTokens, tokens: ['cloudinary://123456789:abcdefg@hijklmnop': System.getenv('CLOUDINARY_URL')]) } -preBuild.dependsOn(copyFiles) \ No newline at end of file + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} +task javadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + android.libraryVariants.all{var -> classpath += var.javaCompiler.classpath} + +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives javadocJar + archives sourcesJar +} +preBuild.dependsOn(copyFiles) diff --git a/java_shared.gradle b/java_shared.gradle index 00acbad9..bbb305e4 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -9,4 +9,19 @@ tasks.withType(JavaCompile) { test { testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar } \ No newline at end of file From d07bfaac435e742db7fbb915be8086b5a75e3cd5 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 13 Jun 2017 10:43:35 +0300 Subject: [PATCH 315/592] Fix javadoc. --- .../src/main/java/com/cloudinary/transformation/Condition.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c53762a6..3e548448 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -15,7 +15,7 @@ public Condition() { /** * Create a Condition Object. The conditionStr string will be translated to a serialized condition. *
- * For example, new Condition("fc > 3") + * For example, new Condition("fc > 3") * @param conditionStr condition in string format */ public Condition(String conditionStr) { From 656958e0b5028784af33074926603bd268ac67d4 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 13 Jun 2017 10:59:13 +0300 Subject: [PATCH 316/592] Version 1.13.0 --- CHANGELOG.md | 21 +++++++++++++++++++ README.md | 4 ++-- cloudinary-android/README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 553e6d20..1c03f182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,25 @@ +1.13.0 / 2017-06-12 +=================== + +New functionality +----------------- + + * Add support for `format` in Responsive breakpoints transformation. (#78) + * Add `url_suffix` support for private images. (#76) + * Add `type` parameter to `Api.publishResource()` (#73) + * Add support for fetch overlay/underlay (#69) + * Add `deleteByToken` to `Uploader`. + * Rename `deleteDerivedResourcesByTransformations` to `deleteDerivedByTransformation` + * Fix `deleteStreamProfile` no-options overload. + * Add support for *TravisCI* tests + * Replace Maven with Gradle as build tool + +Other changes +------------- + + * Parallelize tests. + 1.12.0 / 2017-05-01 =================== diff --git a/README.md b/README.md index 24a7596b..af9a1f37 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.12.0 + 1.13.0 -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.12.0/cloudinary-core-1.12.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.12.0/cloudinary-http44-1.12.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.13.0/cloudinary-core-1.13.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.13.0/cloudinary-http44-1.13.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md index 0f96d21d..303d762e 100644 --- a/cloudinary-android/README.md +++ b/cloudinary-android/README.md @@ -14,7 +14,7 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. ## Manual Setup ###################################################################### -Download cloudinary-core-1.2.2.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.2.2/cloudinary-core-1.2.2.jar) and cloudinary-android-1.2.2.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.2.2/cloudinary-android-1.2.2.jar) and put them in your libs folder. +Download cloudinary-core-1.13.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.13.0/cloudinary-core-1.13.0.jar) and cloudinary-android-1.13.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.13.0/cloudinary-android-1.13.0.jar) and put them in your libs folder. ## Maven Integration ###################################################################### The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: @@ -22,7 +22,7 @@ The cloudinary_java library is available in [Maven Central](http://repo1.maven.o com.cloudinary cloudinary-android - 1.2.2 + 1.13.0 diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index dc266ef2..b68f37ca 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.12.0"; + public final static String VERSION = "1.13.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 39aba56a..80e3b4d6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.12.1-SNAPSHOT +version=1.13.0 From 349a27f5934146d69d0d78e21243bbd814960541 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 23 Mar 2017 13:20:08 +0200 Subject: [PATCH 317/592] Add support for uploading remote urls through `Uploader.uploadLarge()` --- .../main/java/com/cloudinary/Uploader.java | 23 +++++++++++++++---- .../com/cloudinary/utils/StringUtils.java | 4 ++++ .../cloudinary/http44/UploaderStrategy.java | 2 +- .../cloudinary/test/AbstractUploaderTest.java | 12 ++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 78684b73..7b329455 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -112,6 +112,7 @@ public Map uploadLarge(Object file, Map options, int bufferSize) throws IOExcept public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallback progressCallback) throws IOException { InputStream input; long length = -1; + boolean remote = false; if (file instanceof InputStream) { input = (InputStream) file; } else if (file instanceof File) { @@ -121,15 +122,27 @@ public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallbac length = ((byte[]) file).length; input = new ByteArrayInputStream((byte[]) file); } else { - File f = new File(file.toString()); - length = f.length(); - input = new FileInputStream(f); + if (StringUtils.isRemoteUrl(file.toString())){ + remote = true; + input = null; + } else { + File f = new File(file.toString()); + length = f.length(); + input = new FileInputStream(f); + } } try { - Map result = uploadLargeParts(input, options, bufferSize, length, progressCallback); + final Map result; + if (remote) { + result = upload(file, options); + } else { + result = uploadLargeParts(input, options, bufferSize, length, progressCallback); + } return result; } finally { - input.close(); + if (input != null) { + input.close(); + } } } 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 3a63f8e5..3263a7c7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -113,4 +113,8 @@ public static String read(InputStream in) throws IOException { return new String(baos.toByteArray()); } + public static boolean isRemoteUrl(String file) { + return file.matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)"); + } + } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 617e5923..e509e321 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -102,7 +102,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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); 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 0d4a60b7..ca1efe97 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 @@ -119,6 +119,18 @@ public void testUploadUrl() throws IOException { assertEquals(result.get("signature"), expected_signature); } + @Test + public void testUploadLargeUrl() throws IOException { + Map result = cloudinary.uploader().uploadLarge(REMOTE_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); + assertEquals(result.get("width"), SRC_TEST_IMAGE_W); + assertEquals(result.get("height"), SRC_TEST_IMAGE_H); + 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); + assertEquals(result.get("signature"), expected_signature); + } + @Test public void testUploadDataUri() throws IOException { Map result = cloudinary.uploader().upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); From 9aa6bc04f9db3c0478e5935afdb16f14c8644b6d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 27 Jun 2017 14:10:16 +0300 Subject: [PATCH 318/592] Update Cloudinary dependencies version for java sample project. --- samples/photo_album/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 4f7a0d60..bf111dee 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -23,12 +23,12 @@ com.cloudinary cloudinary-taglib - 1.2.2-SNAPSHOT + 1.13.0 com.cloudinary cloudinary-http44 - 1.2.2-SNAPSHOT + 1.13.0 org.springframework From 849b016aeebd87fa15225e811c62183c4d2cc1bd Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 18 Jul 2017 12:02:02 +0300 Subject: [PATCH 319/592] Remove use of `DatatypeConverter` which is not supported in Android Also add javadoc to `StringUtils`. --- .../main/java/com/cloudinary/AuthToken.java | 5 +- .../com/cloudinary/utils/StringUtils.java | 89 +++++++++++++++++-- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 73194e32..a47dc67c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -5,7 +5,6 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import javax.xml.bind.DatatypeConverter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.InvalidKeyException; @@ -230,13 +229,13 @@ public AuthToken merge(AuthToken other) { } private String digest(String message) { - byte[] binKey = DatatypeConverter.parseHexBinary(key); + byte[] binKey = StringUtils.hexStringToByteArray(key); try { Mac hmac = Mac.getInstance("HmacSHA256"); SecretKeySpec secret = new SecretKeySpec(binKey, "HmacSHA256"); hmac.init(secret); final byte[] bytes = message.getBytes(); - return DatatypeConverter.printHexBinary(hmac.doFinal(bytes)).toLowerCase(); + return StringUtils.encodeHexString(hmac.doFinal(bytes)).toLowerCase(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Cannot create authorization token.", e); } catch (InvalidKeyException e) { 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 3a63f8e5..5a364422 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -9,6 +9,12 @@ public class StringUtils { public static final String EMPTY = ""; + /** + * Join a list of Strings + * @param list strings to join + * @param separator the separator to insert between the strings + * @return a string made of the strings in list separated by separator + */ public static String join(List list, String separator) { if (list == null) { return null; @@ -17,6 +23,12 @@ public static String join(List list, String separator) { return join(list.toArray(), separator, 0, list.size()); } + /** + * Join a array of Strings + * @param array strings to join + * @param separator the separator to insert between the strings + * @return a string made of the strings in array separated by separator + */ public static String join(Object[] array, String separator) { if (array == null) { return null; @@ -24,14 +36,27 @@ public static String join(Object[] array, String separator) { return join(array, separator, 0, array.length); } + /** + * Join a collection of Strings + * @param collection strings to join + * @param separator the separator to insert between the strings + * @return a string made of the strings in collection separated by separator + */ public static String join(Collection collection, String separator) { if (collection == null) { return null; } - return join(collection.toArray(new String[collection.size()]), separator, 0, collection.size()); } + /** + * Join a array of Strings from startIndex to endIndex + * @param array strings to join + * @param separator the separator to insert between the strings + * @param startIndex the string to start from + * @param endIndex the last string to join + * @return a string made of the strings in array separated by separator + */ public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { if (array == null) { return null; @@ -60,6 +85,11 @@ public static String join(final Object[] array, String separator, final int star final protected static char[] hexArray = "0123456789abcdef".toCharArray(); + /** + * Convert an array of bytes to a string of hex values + * @param bytes bytes to convert + * @return a string of hex values. + */ public static String encodeHexString(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { @@ -70,39 +100,86 @@ public static String encodeHexString(byte[] bytes) { return new String(hexChars); } + /** + * Convert a string of hex values to an array of bytes + * @param s a string of two digit Hex numbers. The length of string to parse must be even. + * @return bytes representation of the string + */ + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + + if (len % 2 != 0) { + throw new IllegalArgumentException("Length of string to parse must be even."); + } + + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + } + + return data; + } + + /** + * {@see HtmlEscape.escapeHtml} + */ public static String escapeHtml(String input) { return HtmlEscape.escapeTextArea(input); } + /** + * Verify that the input has non whitespace characters in it + * @param input a String-like object + * @return true if input has non whitespace characters in it + */ public static boolean isNotBlank(Object input) { if (input == null) return false; return !isBlank(input.toString()); } + /** + * Verify that the input has non whitespace characters in it + * @param input a String + * @return true if input has non whitespace characters in it + */ public static boolean isNotBlank(String input) { return !isBlank(input); } + /** + * Verify that the input has no characters + * @param input a string + * @return true if input is null or has no characters + */ public static boolean isEmpty(String input) { - if (input == null || input.length() == 0) { - return true; - } - return false; + return input == null || input.length() == 0; } + /** + * Verify that the input is an empty string or contains only whitespace characters.
+ * {@see Character.isWhitespace} + * @param input a string + * @return true if input is an empty string or contains only whitespace characters + */ public static boolean isBlank(String input) { int strLen; if (input == null || (strLen = input.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(input.charAt(i)) == false) { + if (!Character.isWhitespace(input.charAt(i))) { return false; } } return true; } + /** + * Read the entire input stream in 1KB chunks + * @param in input stream to read from + * @return a String generated from the input stream + * @throws IOException thrown by the input stream + */ public static String read(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; From 3e0d8d28b9165693aac3e7edf7b5adf57e0be4fb Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 18 Jul 2017 13:46:03 +0300 Subject: [PATCH 320/592] Update gradle to 4.0.1. --- gradle/wrapper/gradle-wrapper.jar | Bin 54208 -> 54712 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f5eb2e3af674c037c7cac40486d9031ecd4b5c71..9e6e3a879ee6f094d09074132c95608a4e11fc52 100644 GIT binary patch delta 19271 zcmZ6yb8u!+vppQ!oOoi}wr$(C@k}zw6DJefwylY6+qRv2^LyW0_0_$9oa(M!y-(Gt zT4(j{?zR7xfVWnFBPq#(Ltub_z`%fjfCz)cA(12gKf6L6=`Q?lMI%l~87KV&=LF}@ z<<|K2PNGUmwRnm~Xj0;cbnV2no$ zU<-k*T)p~Z{O2xG&TkHT-D(f{MiS>5L|p`S(LdGqINSA_-_zphxW-WsB=L4d0*xCF z$pvc(t<~C;01wuVqSkIhm!sKfT>`@!ejV$IX&~n{uO^38>^_7as3p$<*{`yRJb1pf z8>r;?A}oa3!hpxOvOmW8?#xuY9*|NCARv#0TFiEcRC-6OE;4a42+31w`sLXX#8uR; zMqg9p#foBJc&i{ubscgM8h9`{*#Jp{FixN6N!7)u?h1BqK2jUUxUhYXya6BbWRXCF zb;a__m|w8Pv&{r57C-5S%P|Vhh5o84;_V#geu+X}e%#*A5b<*77&_o!^HP=xU^wcd znKm)R7b)WA1pgbXqz#H%n~JwJ7P`1!6h8Jx#J6WELYiQC@vp@;bk@Sqa8P93t*hLM zL1NLljmY?c1#n=MpMOw(5>YcvNCPpDk~Qo*H9b2f4u;;=zPen-84YzD`^gqz6^H)G z_Q*%`C@P%&%@#kEF(Rx@+=opT(0D&G2jAEC#7ts168=-(*6+thSY#s)DVUxfF`g*Y zsSYO zNhhWrQE?U0E2mRhrK|xOq+U>Tg;|`!2h{&Y&7WJ;DL7;#5D*G{5D;RJ1mywD1U@_% z0E4NWv8!vAHoOtWvOB-x+9po41d)bRto<8p>$&@_ zfJPmAjFRTZBmXP^=WgHWuIui{ zlb6gu!vRL?7jtkZhaX;;JtjMvx8AEC>4mTEs~^C?_1kpd_QH)WRQk<2RNo5$cu(YZ z!gvp3hTzOCBsd^UFn*F~{3sHtJ6MqUrsRcn^}`!c++Tax@W+enM-s5+opnJ>p&T;Du5y+C(5>=50m6R^4*yfH9ep%SIuu1y@G;2a6Bw z_1gs1C|qm7QdF85P18ANW3Jk-lFQOR+xJu_X> zl&9C&C${?HRY~cUnXAkIVw4k4^o34is?!zHv^ME=UTgW{1?>%LT6SY^Y3?aSsE<`L z`7Uerq_X^ua*ORr=j{__hjf>$(*wDohH>TG34=|V@CPx2sK1j{ZT|S{*MvlP6W8f( z9mwRW&1=dHXs~5)%#E3eO42LVf~N=1)rM2Dp~!ma^~Z-KX?XAe7HER)Cwy9Qa~6Yg zyJh0~Ma#M7WnR-`V`)i6i{cV;k@BerWg<#eP7B%1b1w~@VWRph96)k6tx@^*q!cm4 ztFlU5w~ZIocnt+m;D`o4>L{Ke$pqHu|u zvaIg>Q)>#wik<~vmEdq`|284rs)$Udby|k9WUQ;HAm3G^oNm)h4s+P8mTfne^^%2K zPRH8!xf;|`X7_z*39hvaqbi!UImh%4Xb)H_I^;;4@qTiPm}_^nE#$Np*UMdW5q0!t z;1ckTk((QKxvJjZ`gK?oA79=UTn>`~wwsOC_ven3_gn_RrFMm<0)L?0@0B%HNViSM zn7*4lwxtzdVa_3G&g7rXYv9IDNZx733n>@aRE6oubg&V8NoF=xHHmnK<2X5|;>>VR z>=F~gzyg%8v zS$RF}IXMpC`^mmMfTY^E%N0+oxhIJ5}8%pD!O642irL~ z0Dap??GfIQ8UUv}TOzWgkT`_&yP8cjkwF@@!@NNf-7{m4aMpB>)N5z}-YX9H=ggC4 z+}5Oq`U>-1uFi17G6Fnb6Wzb~1RI*2*(gGEua+7RBQ=;EkF95~aswJEcRBhvyT(L0qp56mRy z+{7hSIp%+x$x`C*&B`eoRYs;J?CCMSN(m5E(MtLmQlzQ${unExtP-^gK$uQ5QOS0Z zA(JzXv_3)~T^Fw_s}@L#V`Dar(C(G9rYi#AQry;;atv}i3&dusJlTuKyy=y5pB}l5 zMbgM~rAwumDBM8w-X?_dz%pr0Fc#3(a;T4pfGKnPmYvV(mD{qMlC>l0bRRNKtjqPf z{c5*fns5adEYTsV5pxWDb+K+>HjoVP)DNViAnaJbYIcN*t>8v8V^2YN+lXIoi|6y$69?gi=x{IC|G?c`V(flf>sWY{)mDQY) z*4GEK$!}UIjS>zIqQ#J|^&XiCLV=0l{R8~30lr`AOhBk@e-l+Q~iFoZ5NOSWsNfW8k<5f56S}v=s<0A?1{_NnyCM0jF zMYzE<8)Ru~+XK5~AjrUO_~fP1MWORor}{)z+u?2qDsfA}j17O>c8GqG=fU2%4<6vh zyzng!8l~kIEF^x%@_V(FmMuab^R!q$TQTnJz*fI0we+<8wh>4y)$@rAF4lLPx*LyC z>Q%vv`BTz|MaF~x`tL|=_m?}}37fp{)GT5E)n?y!1rYcBohCPFmeQbT5a=(D+iu*HPl zK@_5DC73`aMZb+8vfYm1tX`X-LFSRKDDpzO3M}(bmbzwl#jTNuwRA9howIcApJ6+j-7C;IqZ_LWr2NJulZ@A zX`$TFUiMOFgGcjz%W`JxNm?ybw_(LuMt;rLh8w*-u=b;o6E8*gOdzBgg@@F~-Bljz zavE@+6%qP$9tGhj6bsRCa@HBxJVn=@GrW`y96L|fi)HKl2eP@RO@62JKQE0@>_(KpeOfOJ4R+8bBreZTp z1S~<6&iIN6XXspkImNaC*$X+OkG5g^@LbW1y9nJ;4KHwnxmeDAS+3iEae7;R!R&z= z_o-X~IN8gFX}zEWej#;%6Akf(t4E_T?nB=)h#Uflp)%{!uMkm9IB6y!Od z-l%}-1J>EE<|pwD{<&8gFvj3(>yk=3pKM?meosowHO#gz!TVn13Q;#>B)I*APS=OU ztfW{<32LZME87a6{Gl;{JYEsrGqX5dQRC;}%DQ1x*QZx9u#Dt+^QBaV6tb$tC0TJx z4;S^FihLl6();U+4hhLK7`mPdLqP8n2OltM;#5fLrxp_n$rYo?X-qY=nk`iFv)2iW zl)qQ%C)&Z8Q)fHBA77+0T2yBcy5yN# z^HJxp+MSme!K%@_Ms+vhxp%2UaUx;9W7JC|s`GJJ+i+ORa5$TF{`=|cW8n)|oB=@o ztm;Mi2`p1PmZ<;UZH+sfo%5NC;B)^4_Q@owHpfh{xS2BY$X_Kb9-HOx3!?z2mDm${ zz7(M+tVIAB!vZt5Q4Cy$>3UYCf7?l#D@ zK^MC0La#nS^dXNbGTDtPQt_>p(mtcq25RyBRb;{{Nin@u3g)Lrix_--(&}Vh=#@B+ z((hkLVp$OoZOSeZAv$dOWZv?dMr|fKCU}8$PbB3Ixs!z5#b}6atdNccsK^tLPlR=q z$Ds8Jn=M{qlV4!3!n@>bwhkGDCFZEa3r1_Jlc*V|uOdaafAkng^}PuG@3L}1fpIhM zU&~_b-^2X>SCvXGj+QRwuC8*{u5RWI<}UxOGTozl;ev!P#II@RZ7{{gpTuWo+RahP zMB)|#=0d6WMkZ3fm-3NM3P_@xHwSkekd7Y%_&_U}Zg#v+&?)N5BlSkP8s{`DToc6m zn2q`1S-ey7jcBJzKG&X)vHO_Rl4874j^t7u0B35-6=Ds{!Zvi+l_^!yT2xtKV&hzv z%C_3cm4kvIOm9bb6pTpAQKaW+Fa(XHp)(=#0uQ(zI|2Wv(-IKIglYf<0&)ls0>b*= zk8?sJ1E3${1PEx)t(~)TAaS7%v(JdJtOPv>(R7eW^s4=It#{rJ7Fn7L?ec1>sMh!; zt0*nIR%q`dvj%jT&p0BPPqUyvMVSuPYhvwkDCx;fX*-p-^ih8E>!^=IZbhGd;q4AR z_TN7C)dYNAOA>)|1+VjD_q8GMg~PJT*>Kg60%E8U4d>$qxSPC%tBTie^V1DgD6toA z+}ZWi`pFR)Tl!VTaR~USPTAC#ey%%f4{MNjV>a*{8C7J^@*M3p7UW;FUEc604|2M_ zl%B#UDGGhM6Zu!8YdXvKyCcHmc2m!&@?{BnTURRad06j#tZ3~*25#+Po|h-N(|L06 z0#IL?d3ot(lAbqojM{n=4;PArB#kr+HIavG!Sa9BmYs1c^dc4l7F`yMT?RZZcfy27 z^r_5>A@;m$JgRPsL^xvbwi^tN|@1GnR_r^65$jNS=Z!qnG!4>BaC|* z<$6p4^aiw$h>7yqiL3A5lDYb?kx_OAIs%UGVn@_c{N0DiT{0K|vS$0$wXI%s9IOheuL`% zeU1A2}Jim|E0-#7{|JaALY%%&I^23DmwT z!jx*TDI-fiBnL}N;onwkWMM>4vEGr4D_BO%{3{(uNhnSpm%etX`@DJ)Kng>A`#Y5jDh<_K87-Ye+VufrZOjQO9 z$KQ2!bCm7u$zlu&cPOy17F^sCg0C_(=ruAbR zmo9SX$sW-g_$ImWi85Hp~wY8$vKBAYh=w=Mj*`29CUq*dv9 zfi5O_SR-&1M>^FdrPE&x#gR;5wUfCyMCT)@DDM{^}#}_^40sv}-mYPC0KO z5|7{h^qfz$R?WEy&@Xc!g% z9_%32suE+O2S7Ch_n z4&t4(hltv2Js|NcZurB0MxLZOoIN=dqZRq4xDjTCvLw=j6aDm<=cx?WlPuq7 z{Go2?zoy&b?1idZ0`2>LwR5XSl4P|LuE&yO_Y)IN;Ki2);Elbm0*k~p>v7M}-1EMJ zZf;V$_=xnT#(=xB+7WsFNo$1P8oCL_%|8gv$|j|jClBU#^0VlhdbzDz?)Wovmc(kq z^6NWXQJm9tg`T4V#~c+s&8f$5a-y+(*|ri>DullrQV&=*VDTd!~pn@W62Wf$C7^D z+)swL79+*O*l~PC;PJ8Ogaq0AKuLw7lIl0zvDS;)S5$SN?+WD&!`|StH-3o`_NK(q z+aDN14SdGi4-!X)o7K(@lWx2cCmr!=F=`acZ|MJ>XCcabHub!!O;F+ zd;h%BCZQM(1cdDe2nfx8^V9SQRD%BqY683}BEasq9TN$HFbJbvNL=tr@IoEp4>LSF zQxho>q}N(;vaC3TC<}Ko0_luqH;PSGX-a9`)#!vjQ;nwz&s(&czOmmOyyNw5zW|?* zf}8JKEz9o)*&oyWv7jqIIm%Xm9qM)ivIlo6ZJlzx)|ljIdO<0)ULiy^5Busa&o-Df zy#W4zjq_n!%uWUIq=E9j^TS;{wN6w=l^Y98cBB`2n&$ng_~Z41JCC+b(Lw)K!(MQ> zt<&KSvXf1kl-UvxNuvK>{L<{fFwoFU7Pm&(G|Orjjk z8_3sJaqpKzzK?jbheRU7?c4p1_q5KV5n#1Z)Vh$sryjpQ0US58t_IGZ7j&!O`BZMT&;iLJoLPfBs{U<>lCvQA6K zNPj>s8YrpyCba>RBXzpJFG17U57@f$^x*fQlP>Us{eq*b7SytIginB!+iN{xkub9TyXdCUr83 zYJEY77aBE|Z*j+kBON3Q4-2JGL{C~rNl6ncJj{mUO?A|YY5Kt@w&o&n2RI4q{;B^r znZe~^T&vE@3yR^=&FiwDo_7-Bk}f)yG#xW^k%drs7Us-mp!{n~>Bo3THODIp)&h3k>%N?HnHB>< zJP>nhd3%yon0wq36k36t9`I>4_2X1VbK*~&%(|XUu{lQKo-=fB8FYC(%bW!TCi~j_ z0P{oe-~3F6&C%Pa!(YrXE3Anw}GQdrtY4!i;QT!QMQYbUDn&D5BF8JYxvUgR|WameY_h#wqb`4iFXoxw6!d@7MhEal8q0$ zMV9f)vU$Cj`srjz3*gIRPqw10igw+jcLKKH{nKzVJGr?v?iQ!kZC#6afUj65z-LA; zwBEd!UZ(jTb9Y`WoN6(+FAPpl9o@V&%l<$I$lD~5%a ztx2v9XE&*$04#;`1dfmNU$W;FZrX{QI+Nn^qOnSd)E#iYv{2<6pd+@%T&@;$F}V;} z0Non+*)GvF4IwHVX2V}k7Z(_0~0Xia`s#_FC1yK$Abe%^ZqSg7RsIKryogVQSBKR0C zqU;eNeYwTVD?L^p!5%|e**qRYBw=aBbv6vW8hAHtTxX@2N>z2Dhr$R#N zVLQG$0a0BHA*Vyv-aHk<>tGoD%|XRP4`axRs8e10-be0?D=rLA_>*w@Gbg?EH`$w` zYT@IzCF$H=yEwI6zb`fxRV#TZkIdV;I_VHAG*kNXl39lK+wo;otO@PPa#VkG4K=s7 z#m#+`<5-1ua^d4yXAM^TBFLS0+oG>1si2=+0Th`ml{pb34OVjr0{5X`$6SDn0y7b< zQz(%lVE!zxFxuYC<&3Dbq^ zuMvB|TV&3A~zCy8WOewFl@t`(7cEOh`U6UXU(v&{g`P9$?z03axLE z0N@=IW4kNDnSiC0uFADHLs8OnY;2(Eahrg%HM;gSdWX z@9rgs|Gam5H1q0N*;|O$@l~I@YyZK06q4)ramX@Nhv^b8hewA8zu6g+yfmwiSSn7MTmYLF_g`x~uej?w3asyuz??D{I zGPQh-Ndn3I1HivoM~f2ik@kM5C{a~p^&kKENNi4vZ9r(tDmEB)4!Y&C9!EXdYfLs-wClIl@*!@QI^k&r`WYb>s-_V`sCHXo|cu@ zsnolJ^#fDdz10;w;RILX z8dSF*g}?n5pk-HkW0A>gddPLenmb)T)v!BOzbeOqN%namM2f-T27sR;x2ZR)Cd+DH2a;BPtVPri0yb9#mJ zj@=qzKGT1#|5RhTD&d3mK~sQ;X$RRPSC%9<#x8e3;lnKlFzkJ4VGi_UcK2kpFKUWT zoEeB}`)%-Nvz%bM{~p#*`UF08Dk$~v;V=B9RgrUv?K2^7`04DBkT#a@m2h-I^Tx$w zzM&5Q4V;yG=b%iS4;F{coOt4(53b`EnJ2tw+eS<425kwGR54~EQ=oqcj=L&hicCtR z(X4bO_BZnXKJO-Q!X5or)uKa!0{tI#Ocs2b5E(iM$WsD9kQRV>*d$2#d^9yVSqmxB zT1kpFO6b8xg&Y5~j`*iB^Upmp&-kbf@~JFYv~vw24V}w;_ZCdg(js*=G&Y)M_vJO6 zs^+C7OI!cT3jZ3%Q{Ng#>T7_7gM&l7@ZWFG5FU4lt*VF zT=Dc=Ht)IMyBWZNY{A%ZD@xLxR|w@x*meMe7;!e0X+j}2%&l2AiM(-GiHmZW!@l)g zFlIJkJ~f}{(X^VE#vjX2FC<&I9JKsXKF)!XJHAuuR`O+J2H~_XC?T356&rX{2_jZw|ZH%uqkk<}36fZ)!#jx7o9 zoY>}&BLuK6+8Dhagb!PfdVmzy!W!GLf>Ff)d#B(nA)cDrcZ=fwH{J^Ebz?F`#*{h} zVOGK6&rotWb%-bR2710jTE>on_6%i&0oLC1{o$Q=Tclm)s z**)BAZfXC?fXDj7E!#3F<|kfu z?#8N2&w;AhXc^0zl+8qKg#qe2ZYMkXOaY)BW7{Gp;=DMM$HQeVV)B)dP3g8etxr~q zvQ|-3tC_}Cd$DojjJ~qaanYll#%qAf#N5?$)WOLEx@!;|=}tPl0_g+M3v`poF~7hz zf_k6SFpehCyc|ZQXo?JG_s>~;(pClxN%>ZH|4fGylUcOQCrJ2?v0QeaHT@;|r3G;G zz?Ih3@CciSXXV4)-=xNKL*xf4198CY%VLlMeWse#!prBb4r!a!;~%K=K|JW2m_K4Q zIMZUXRV78bYkXmz)=JwfV-A{CmH<~S=8BSr`YE&Btkm7wIxHG%`!>J-s+m+{8;N#p zDx4}0&wG!GjoEPG^Bv=LZAx_5i)F?cErejG&3EqpsVX;Ysq}`4w=cLnip6zCI7qd+ z)0voRbts`Tt%R4De_$GIEHvhP08?w$a_%x7oG?@Q6{UnLZ{JWWXTcN{fDOQtF#DY+ zxsjRLNE)7)1)AUQ+I%T@BN^8(RMLU?$~9hkUKMP%tWM2(rqY)!kua?A2h|i} zYS}!PqMQTs{z^)2 zQ%#{8EruLfhP!qeHgi7kKoKzBuaa6yraBb>>V1o8cO7Js3~Hsgb%gVRN}QMxVQ3+8 z%bi`9cp0Wg>Y(e7W60$(G-q>A2!#_z1}SOg@(W@LCC**@GyXU@kvhX)8LJNHMPp%x z)-;VePQ+~1qQrnF<0o#~Rzl2^$3MH7lJq;ZT!zmzp+${h$%sDZHwC~@jShQ`AaY8# z)(BKJ;W(MSu_w!oX6#h|=cI@=7Gi@ARb{8=FtJAVyMA#g4oRaFOqTauaD8Mpu});H z;(2SEdDs$Kp1L1$?0ZrtzVBjEShNi0zHnOWWg`P%cV@E@QdDy~T!jcA73)ks} zcxQUHCi%RYPqu;5DLCNGH|fnQiQOzekTlp0CjW?;fn6dN$hQEA{up25$vAi!) zJoi?-U7={xt*Wm>y*s(5^v=Cga+h&*I7PiX+fnJ2)(^b-*p!ytF%RXve9UmaMl{3@ z5p|N@)`nKOkjk%MjPsTgWpNd~-UL(Z1Tw@?;}xr0{c!!oBMET5!yBj5X$uu{t@4`j zL7#Me)J3!1|19I0Gxi(_gaeAl|niOW;ZlPONxH3+kTQ0^--!ZqKq`GJ|IJ)krP5t4Y z1o&x0hl^)x9ibCx3~nsil)1#OH(^ZxTOpBf@}Ac>UGV{SO%$=ltd2kRLHORtTYMM! z5ij*v&sGyvt5iIu`l`P5+eG+-xb9(XrZH|YeGE`D-ng1$P|KXONUky)hJH4>PZd_> z+LLz^tNsPs zQ+B8LN+0#uYMwjhct5Qc5V|dnc&7bc`oR0u*4Lx-$}c?3`1(mK8(TK3_J!#;(Svm{ zfe*Nhawm;wv0kO-_Lcg$32cSbogq3!4-r&*<@Adl>p5JH(~a$-%zYv_i~A$_*m829 zI4YX$zL9dsx-h0vkj%z0=K2L|0F!Gofa*&28BShQ;0RG1ou24El&!MoUA`1kf-@hJ z&<6S{w+steFyNtpg!2fbokck^cfj%h90BSr3l|l%xUYTaa!aOJnzEtMp;uSn*HuSe zoIBc)`lN$nTKAneHL1AvRT)^EjDT-30k<9}Ju~}820ob?e?|#U;ZI>=!;D5f^$^<+6Q)n?*bHkX78QF9Q%w_0(t zobP4P^xcLFv)F&thi-SiOO4q>Iv^%gKX!jy+TYuSk@S~GHNVWTUh6@|=v!V8BFPCV z*Zso&wrAj=*oj)nFuHT1i?U`}895;*h(b5S8U1klDiFW>C6%pMm4jllvF?#)I2}D` zdInPVC3RSX+mRANLxIX-P)S*%8P~gqYx3QRQAeJTP{t5l+EJ;mZnO=a2XJ5bhxMJDhvj>B1XpYK;8(F2G8!dZv@zkTE1x%2 z>CdPwcYAD# zlA1Hfzrpz@jd-J{t$3rCxny$2jfZa4%r4K8&fyoMP{z|y>~kU($9NB@A}6EAV1yCl50uy@0%}>?z#&cgOdVi2sy@W(u1GC!Zu~lt!Q)W z5E(lE9L;#7D`NL5@t(NZ{N^TcW58()RuOK=3`pPx}qlkkO_42 zP?AX}P_3SmUys{o<(W+VPzhJ4n+`=yi@U(O0UW_&3#pHU zTdNI7STHPn4f{|YgIRct;#2Q_Rub5#F#Q*-uPN_Nyqvb;NA4LepeQ$>H>>f-FRj)h zqqw~=gRQwU$>F`^rH!%VC7~6@@`VEd30Vk5+0RhxRi}w5_e>Ya9|q#DVZ+p6BTV_X zZZH6(ff@kxlcST#&tC$VbzYDVqmpENrQ)Re~~89Rc*-gs_%WV&S~c~KP$ z8D27#i%4@WMz3zLSFBgLgI)`4ngSgY*Sg8NVOFiNjbnWq;_QJ=xj7me!ZzYuH)L69 zqPiZ(V53B82=5lnqBj<$?(lxDv`I7eFz4&UH&p;PjsaY_8?@X{P)ey1C2oFM$=U#1 zr?1T4?Kn55&$r36edZXI^6$(rHhW!}QO_IMq9?C`rfWod*WNz8@+BVvtp|DEra#Z< z4kCX4R$I_9sCD@@WiUSZ+fBiC2dCWr5wkMr9PVs~>!qgK{1@)VM>Fm#Vm0rx0?$8@ zKTaWlok_#A*ZcE<+nJ5p8?u2XLqzN$(qs|6nW!<(vD4% z|4bS?a_@YAifxCjjJoD)^yzC{_g;6(_X`JrJZmE6k9brgGSNcZ=ZkWzN!nc_DR(VC zz!yffg3N>5zZJXp>8$mJVq%Ko#0U1X8+qpwsTOV913Z&IYzX_5TAx;_IP$8A<^Sf)i0C^ujZXp}w7rKO;U{ zzEiJ{X-EP4YL>TDimmKV><_oo5-z2;rJv*=w^vK3rr>U;Alg(QeF3L3KyYw?8T^Lo zyHzIqhng05{q@OYpIeF>R91`A&4{p7BLQCOA({KSH}DT~5kor?`>cCG^N1(&8=-vg zma&xpGLMGPc*BXP*+i_+sp-rcKlurA?t|}w@i&Z4PHy!pI+cHr-hg0yN8vqb&0L6H-$w%zY zp~dR?ETs{3_JO4Pte!!5dA@tvHv_Jr^63Hw|9i|AZ0(6SUx`GD>vz!8cHWbr(!vKe zTt|<{$@i2Hm>4SwmT2#1OxU2(EvaQn#{R{o07!Az*Xn2`4k{(o>2xmODq`gqcYdvk zMobkwmtG${RV8M z6ViKURPR<}tTDovu`+$A>*>7$)Rb_aE5KpDtQ76#8}xrS-5R18W5NF0bwkF1`X7%N zo(lTEYd!=U5dWDwPaqmd#Qhsazy8xk(f@buj4}Y-5cN+>@!v=qP*~XicnJcgy8m^u zOleX7-^}{Y$U`#UzrCuE{{&+R%3in$pL4hk13eO8|L!IUgY0?_G0FYgaSWHB;3Wpo z#8b!kQeZJQN_7b@#LUC|vk+W30AJK3J)5Y4hlni?X>EH!hSN7=MlmJF^Z~oAKkjL` zo}#QLtC{mj@P*`_BQ1qaF_%cLo$30N?f?3;WqF+@`2BT>402sSyo)A)bsHWWDRqAs z6Rf_GR15UY{7B5Xk{#J$h0P)5jdBOrA(*1;`n{T<7|PrbbB00pr~cKX$u%c53TayAQSRw=8njZQDqNrtChq0JDm&t};)rV)iyw)+iD%JI9- zM{Hkw_~+u9`VsaqTYKat_Hn6)i+8nWWN{}`S0?6_W0kU628=jg@!3&hef5fiJHYGG z@9C7oN1^ul9qPcw0Sjp%KP$>d8=>g?tlpq2i3i(&^IukXz*_Xhtx*P4_9z$}G@EsX zO9SiXJ^sm*ac~+^QE#K8Jmau$tD-1>Mst4^{>l$S1LQ}nBq$Gc!^jVTW=E8cl!~Mn z=Sp~fG)G+v$;0Ac+2Oe3-yQk_;q`CF!b}3ISK5xyLOyZRo@6|wiY8sU%FjIB$5=%B8jQymAi|nc?q~GXbih@jqZ*`Zmw!W=v zIb`xkKK8Bq@pmXuZp~gA4PN8XZmvI|nwC-v+m?!Em<4_tFifhAZ3EY8H=i#=eO%8N zWBq49vx2ft$=N!LpsoxhMC z*f)Bw*Yf-umNM*G87HdN3*-eifnRFA`(JY)6XI4&H3-8#+)o%kw$^t$+njyg(aztm z$+Ni9@76uI@E=Iq)qvhW!4}#XiUWLzrF|r*rG2poBq&b!%s?EDY64M<7gSrf*1tDk z-MW2+DEQ7y?f5W|0exPXBXy=x?{)XA#bI}$U`Rm;B2+*5oNpw~X9atPa^j$SBH!SO zFcPDg%uV9zZ-@_xzl=H^&OW3U6aFIe@}rqJM58i0$PUV4rrw*vwxGjb#QyOAIU;$==Q#E|Uq;={4 zT&$&ZIr(q+dS*QMf4v%uoBv@wp_sfG)|W=?Fb1_ zM&b8-)zZR_Y!t65tHPu=^pG`-3Ped7Fhe(yw|WZ*gx|7Qwf9IF>e&PA45p;N82LR( z;v6&JML`Y@pj=I4@w-|~U0gg98FqtN?F{^ii4Tn8QSr);Dd9m=mSFDNdU`r{z2 z3A6`_wYg0Fx$qM0t({G(_TtTY2mt_w-jT%N^d0|Zxn9Y-$NnC@=|cGN#KVWuj0eN{ zC$AH&YbNEYbzh#LZCZ5NlF5Di!XK8u&e!$O< zIovUd-vVI{3q!_jzP&ZAhfbb$lW~)WQiH|#*DQ(I>qTx`$ioOl0AW@$UzdwJr}Bs9uZ0=5#F$50sVehm`+OFk ztp%bmPL-~{J(V`nB(uK1^yreca`^owBin^6b>Z{`Y-1_k?<@e*9C(uzouaT^qtC`4 z9Q%u0_ZM~NuOf?W8dxs85*vb&nnBL%&CZnOt*m&G;C!m|g-9toePlp)u9 zH19uWo2dto+VmQe8WjvvppjoHUSa$`*?$9q0l~jV1ctIoEu$s$@@ROygs@&G6#oxy zkKzeo{tG|Fim(5H-%86h*ni+hCSIx1_78ql|I#z(f8d9b0G$xu`2%39IIr|0Kw)`S zndirT{serOs)kV_XpoYalSG3VXaWwJVDjuld^Nef(i)znz35qfU zBcbZJi|_idGv#S{F*XN8#*3CTnj7c$2lFp)Sa=pOy0Jpt_*yz+afyg~>Pi`{{hJiD zC2~8d8Jsr``eC@{A7}tb_Z(rmj!=qzrA3#CO^y4%4+6N*9MbjbEe!}4?XMxZ7BTCqk8eRQpSKlg#Lz>jQ4 zpVtflD$cU4BdVGAK6)e(S$@JijggY|Vp)XwTd3t5@^;(#Jq^I>S)8+pRr4-eqt1@D zf+(^(L>jdl)iYAegio%g>_SAaJXGUtojLVsCw*UvIPgP%2H}&vV)ut|1ySrHaUxwZA-WJ^O)R@ zcBL0Dy({C=Xax|1sLkQmu=i5DuN&xY;xU{ZB}|nnlx9p*ZG)d;O7;2t@axxj& zr6c(pUl41d9f%VwY()?XZ!`R3VM3_qks%KoSU2_3EYBTsg~s-!_9W7j(*6AR)aKc2 zODor^qn+FaGV%cnuMiP8-!m5`Fc#lgdykFBy6sOUe<1;B3(3z_F))< zN+pJL#6lRpze|Ky;D+P>{sS`Y^rlNdK|qSZLH@rkt_3cpG>YFwG%009FJq==CZ@;Q zOfR*W!79ol(nBE{Da52m<5ec~qFU)oS*0S@F0sXmNX$&>-Au!eWR*voDUVfb9;>zA z+bbf_v5(2iu-+s(%{T@l)(1k>2vx|CpRLc{caKf@6usk2mg4IX0fYbB~Yv z(5JMDt>W{F*OH2|w7*s_Ik9oyt@JmE@?iTH4sYF>4Xa=uJb5TkX1ZpfRpN^!$tzB5 zeOWx*+0TDH{q)75>b$Ugd2Y@1ZA+@AFmk=9p7YDXTWcqzo=N;+nHTfiX6B>WDgA*% zMv~!~I@O^7;6KBK@dnY5=8?@h2;5LRC;iY`!Y;u#B(FylX^} ziw?vn7H9S-<_s9L%>CrR?f9>`FU_OQzq6>1^I&r0_48}pt~fl>33beRrn4?;R%#k$ zW}5fmLYYOQAza1^a{q|R@foL#HudxBoq*;2fyXzwK#tu3BdPDG=@ElqwbY%{?yPXug@;)5$P$U)Qd4*)2mUVs zpLIwkh$h_#xF&Tb<{JT@q^?WV%P)0-9}8Nb8d{|g-p6cRKW!v4HRNjzcuYpC+yY55 z8rl|6B%_gDAmBu;8BoX!Fk1O-8p%=z%|r3zHV2~_)YC{IW3b#5^>n6MBXtTBV<=!Y zM7U^iM-ZMKp2UI$4zwGhV0DrUO^yp~^l|;K885)odMXMg9$nG~zcBv_HXvkapdN7HHjADihe(8RE@vppgz*p>%lz2W3F~bz7T(BO3>*f8=JM;O8$x;?6?M zLwK>Zv53|85X{A-2%tgr9pJMW*uypgCATq~eEV(24nZI{t-^4{!D9fo0gE~i3Oyk9 zK{fb&nxUo%9(6Plbs_X18_b$$0kZ2b=HVZrzEi4K1)%A zS;D$7)d^w^FHNCDg4jk!=Ezt#)*#?rY$T90Vx}i-R}etOk>K!6XOPr_SwgdMfgm#o zK>H04I1$iWxlFZKmG!tmZ#Z%-ghA~{0Lq1+B8m=pN^8ua*)23u<~QL&3gNxgap_d@U-h7lti>rslo>3PFN)ofI)jv;Mtfd zAha3xGNp}1db=N``?c9(2KpqYkxVmDTGF{mK-h-4qViUG+)=3ZSs0jn0%P+-5NrpG zubKf%yvra=mI>goIVkv7CDsuUW*h{d#SsYf_{Pz@ecj6(D83LbB8gTr%||)fG+8j! zk)WBHjMVeP&nPgiosBiLO+h0m3n9%KjB96LG{06F$*vfs9cs15Xg45j{Ygk;gMVAG zUT)=%%|~;g|LWn=fcS+{F9UH28}!KWPk#}#W`8xLIjAED)iC2nS78E-RRicx4?>&Y z+ys{KJatTBk`thNY9-)kn>Sj~_=_wU4mb$hE<~&d74v0)E@gd-8ZsOV;pnwMd%S|D YS+t5u2D0z4jhqlCBn-B}vsV4?f5M0IEC2ui delta 18745 zcmY(pW0Ypg(luJPZQHhO+qTVqs;bMjZQEUDmu=g&?>=XrF~0l$$PsfzMvnY5SH{f9 zXs-h8D+5JPk_81r0|J7A0TPzA>Ww=X?D-40sd@2q+m92#5+O>23r)iE;wHNk(rD>c4SG^z_I7 z1x+;aRFMB&V!C>r=?nn`gaii!#QN_NIBYTit|j&_0qyzqizi}-eGTr~Bq4Ij)%+VH+$YYFK^pRJKry(g1}snhG$-QB?x{fn2Ss`dr>@q zeJ_Q<=vXotn};r)?%7z(rB8DA`$Qau$~|*-EgXvtWsQl*SOck?QU>dE+tIFAd>KDT zJN&eUd%cYI(Rp4wd-kn_5ep@p3GP;q#}xm$I3L)26H+=;vn zE(5CEE-rUtzpmSCA;Y^y_u+A#RNgc|4vgPM4leTPxUU0${ie~n3yw-YX?@kaEf}gh zurktVt#_=d6X=;?1D<<2UW-027el-hCQOEPpx1VlQbU>5pL@F^9@?)&_6w<_8Cu=x z=A#Xp9iC{&2aX!Es9eFcoQIl7(k4y+DvhLH&xb9I3*{S{hwWE?8(g%PFHlnfwo}r1 zLP;84L`u1eukaG9FE+v_gsnE=uaJM6ODLOruRBi&zRWz z)*>!l_d$KvQnMvn_gzPw^A%wTSi?aw$$Bxd5!z8A;ucHR7CVx=$X1%)B%~XuX)~(= z3EnU&b&ehAbvVO>5m$P5?!UGG_*2&O&9t?gV)1bt$ybKDiNotI+6WFYKFw-4+zS3V z7QBorZtUd0L(m(}Xole6gB9tunCa$*I)QAqspE~>+7{{FV-NDf zybmbO84x{~-0wYs3tXgt+ttB#3Z#cRoLAsq<$Lg;f&+2zXv_tBln}on^Pt^Q3%8AZ z(&W%5viwdcgp*Iqhr4XnfDhoi>-fl7eHJZtLyTNXL)+j!@f>ZvM*IG>*Y1@D8tz7i z>O%aAxTs#F7)HZ-*SZRhv*Xtkjg*%h%7SCX2-(bpxL zkkow+o+M&0z|}0Fx=y8b`$`opV~B_E&Ot74y5mj;TP;>V9eA(aD`V$H`%IhMXRN_o z=3$QC$T&bHe0j(pCa%nkxI5`_#&hYx)2sRn=|ZpEZ$xWaa4stolaI%6jv19bi>vR% zixp|z{w##lG`3j+Cay(j?fj?T3v2vPJOuFY25(D9YdZCQyVx`k{H3Wn)~PKkpMhv8 zh++z_pE3$Cd>DPJaS(;EHX~yCs}INXt~RsVsn`NP?Ys?7I&lZ($(n4fy7St!uc!h$f9=d^D#Z1?m%otLC^Pw z7h9SXd8QW~AWe=uODZJjNls^bJ|1c;ko0M9CJc!>eQOS?MN=Jv>Nq2UG%xwGU=MOAlB*AgLq@5}&4G9Iy z-gkUvhP8U#iWY4=%!f3ncyO6^TACJelz5#|;mG+CK)Xp(JBL#%ugwXr?N<8}hx-$P z^p##>u6P+`Z6zI_XkxZ)-*JHl9;p1V-b}~vh zv%d+LW9c~8^Mk6hr!CTg+_?#m6J2{Y!35&%?|89EoFCQ?4}7+wI*%x0z7n8Bdr2@&e)9!N#~u5 zRZkwvR`$%k-bI7lzIF;^HsYS#7vaB&4(dNy@jn@@iAWX#1nNKN#izP2NdZy>aOHp^ zjK&X|JZx_5tX{t?V{aQ$N%x8lKN4&tCl_w5VsG+p3xlfFv_(8fbfhik2k~W!EAIzH zIPL{mjv`Zj9oOM`&Hb|NYH0!Zet!lP!j7{iOK937vNWa-%;H#XNa(WwnHcFdH-&Ap z7fWcXx8Fk${mdYIf9DouGh8(VgfE$?GSMx4DRo`r6F@zM5`v*QUo`gr=CAc@B7`*U zyz+$JP`;VL35n64_18HX4GuMeidHes$zKyKJKHU4kIBD|e?+v~Px7JSrtcV?GHv4%LmGd7 zcp35VHQ$C8yaK|;;@^sFV6<|0nMHllv2N>+t`ItP@L0&Ex~Zh)_>ES_S174;-i)hM zxNuFpSbo38Rh*q+Ma(uX1CA>z(I}A{{ET}?;&bLqdM#=_&8O3~ih4%^-wMOib&DmN z#CYTAx{2uQ2ubflvZ3aiN5cB!+NG*kN&1G-8=1}G@hLT_ZR#&Q#dbOxQ0dy8S9pZ? zX&LYazxmX!7sNU)%kcuK^>EXCWjI)t2P|yIqM=ObM|At2jMvZO2?3yKW1WZO%6;Uk z?J^i=LyrGf8#QUDZG!zPaxfW?Apb>lz#QQJDj*Ap-G5y%QgI;vRX|(sJJeY4KtS8r zKtRO*Q9$@fy(18S94%-+v=#sF+~z;5I1t1bNOY_9t0PeOvL4pbSWqlDXxc3U z{0&F`L1W;{t~Th+u&axg!Te_l-uJi=<+sitEY7y25qm9`RsRS4KTJ;^B}^O)bN3tS zle}Z*_S@{Eyp;yc8z?)v&7iP(tB#$)e&y@Q|3_x^m=Q%XLKb|(NxJ(D_i5g7LVRNw zE+iyI^(FyCdKf%=YWHyGgB{2B#JNAa@p{O^94Gd8U0!S14hY>oivo%L6h~j&KZOPn z;V*|(dA_9uezX%mqq4uMz>ap{@J4zsUJ*fmB7WxYc5r@?wS7yD{+z!yglby7Mi3JP zGvH9Ev)Pd3N8~ezbk4FBTojJ=IUApvgfPgBqtgPcBdnH(Y4n>9SleA`8H6Y;yR~?-$cCu%hT^?$w$1kLJy~_e-j$M#rCgz ziVg-nXEWRT#sw%Z-ZYl;>RG)1D2q_l7kc^`#vumerSXo&%l>YuE(1e7W`dj8|sV zE&!gaXGECCR(314>>5X_iXA)efIR}^})9e=?#8kd5dIXh4FDYEeD-i*(_iH zhkSNzP1$R`D{+={HTAl${P>tcndNbTrgW~PgMTxXW8bi{k|)!Uh!w1+D_W5Y#ndZX)o{3Lx8&w=)iN0UbA$l=x` zO2uz8+iBlcU#k%Y@AP`b^b>kh=(QGv{W3mO&ai+=VhyH-T zz&h|{ z=k}iFGgqZ_!;R{WN>pXL8;TfSC+rg)m{g9~@u`R-Ez9ta=ZcTk{y3b?l&Z09t!hxN zo`ZW#L|b1x=^~JFgnMo)ws<_lLu+$(AwWltnj#yJ21f+(d}JU3nDyJ-NF@6>qY^NSzfdRCr>4B`Y?|2ZW$yLo$}5}s}&Jh@^>iCQ4|jB z!wd}@Xuierbr`ncd@(;E(pBC}vf%A!rPQG2EhcxoXW60{Q4H6Ors5D)ZJ1P>XWVhTso_kQ0qS)%6?J`WHs^7)vf-Ry@6REj6#~_4WY48p`o`J{RR^#_ zmlu=EeLV_}jA?~o1l^vanG?HZ9Uh@>5see7KE}-D3D2wvrB#3yGRFv~yw7xPqPGR} zB-yUJ-+?$|@z+|_tH70bQ!L@reMhz^ZfW6WmZf<(K^B%ufe)vx8grd#Nmpvd;ET-F zu~kK(^iB?Y2)V0VO8M%WNW~PZsu@t~1g=I)Z<%G?EyXV=4-HtLulhWKo;6fL(N>3Rd@*&r;iKE6ys_|Jl zRV0ySc_3Uv^!2u<2{?xjAL#7QCV2JVaHr%q)G0JmHP9re1FzZG}F{F-@ik zE%T2h&6Q83IPdW5GyXG>dUDFQmHWm$1^v86?@q<4%09p{6*=0@P@T2GIU+LCqP*oB znl#ZUdhO+S4OruL%b@I|eCLw`HKB4klDq;YaXr;mKF9vqx%M3Rw|$wf=a8))z@*iy zIWa^{q^G^szXa2h`j`27l;$@4QqE3!HQ2;d`FiOub%wv{bvKEzZy7t#ZXJ3S75Ak! z$35B;@=^e!Ch9+>G2F%FM=L-z+PX7aY$rik&)7yd#u1|>z%ie2`@d)oyc!XSFC!MK z=~eX(KFbf@hS#WU<6wHctnJ?>J#(54ymc{zGo(Rl*;&ipi#BJD&W?EReh9O=q{H*I zupOb7OHx&j+uwg;S_)vWtj)4874#zvPlzi-ZX;#X`x$>X~p zdOnLCYdl5Yz*l}ftr}bKV5+6d))_7=d$JRbmsk6N2bbb|PT!~L6(Fd%*D4hbXk?4y zfnWfFR>Q3)odjU>fvsz2)N5{pwLDI1{nnw>XcS;8-NJ#_33dgO$tVg?j09)X4^p1nmZRB~I9*k!;Z2rk`l~rl z(-Aixm$Vy}p%lb{iQ0 zO=pDPV1G475_tO_uF)4Nl^-Cb==AiQ`f|$ev*19A5`7A8CwFaQN@bnCUMJvH`;_%G(zl`?VGo^joI;{e(j$`kj5}+4E_Y@0q%{4 z#IE67s5Lb1vR?FrK-@!jdjXTUtC~!rI2RPxuL~m9*YtRgGB4}Za`6G}3Di7bhl{oM zBIpe@w1fh?WB-I8IBr5g>HMUkAKt;InFy#!o6E3c%$IcvzgwBwk^j6pyg3H%I*efP zBOZ3rR5x-Yhhsc&RUW>O8e;@3CPs3qbjY>;zQTuXH*dfAg5tyOY5Cm=v-(68>98r! z=@I&(H?n;Q;xq68v%0h05&-Il;S}$zITk(@44qz#Pc6bCPE?k$&N>;%hVM~JGmMzp z&!Yu#LG&xEk1gI8@t0=FYvGde^}T9M`sF@Q%U_mzrW=4Ra2uj~C?Yyw7WdlL4#iXB$z^%+xTeo5$1`f9 z!m86dN-RQ3TIQ9=d=xa3fGoJX7PuP)2#?_v-ieV*wYk45SUl-~)W+;Ax$se$qt6i*so4FKUtooP;vw%1u5@|-t z!~!X%ghi49F_jxBGF_1zyyn^=^3AWI#Cm`FDIfepz;18`5i*2n@~XY%?pPHbJ&szZ z3>4)ZM@W@)T(7=@KKW#{e^{psSlFGyG)S@&5MV?*-o>K1B{_cZi}~Xb@`XTuE%%598Zdqo;tyMA~n)v}#n z0{PegZ}a8%+zdkIdaA;7sqNhQ=Uw`dFqvo(C`nfU_?AZ!_B z6Q9^HGmj6|Kr_ej17+Y=I5W_X&5Eum3QwBKF!0swJw&vliLp9t|jU& z8lj0kSktF@HmLnwR9%e ztHl#%k8CIm$+nkDvg8)B4lFd~(<(wpedOyZo*qvvsIuOq%&gl26*{wet8Oqe{>G@e|AjR!izQNPP4q!*ZpnmrOG0bz=ayAtMJ2 zK-@bpq!wDV84L_xVBG@RNvKi!tr=swgoqb%sF^Y8+lkSw7snFtzxM;Z!Z6mVGeZ5! zc)B&(fM=$hj_7(K#EAU!2N&689hp!t7PM<|?3{C*tjgw6*w`n+M^n?d6*%|VO;DZ1 zC7-!e*QAqzB#i4hW1frs99aOQatHYL%v+Uf%>~e^k&R`|9=5P)boTNR859y?PodSq zA$7u@52p^L!<5bUo6J>x+Q`%Jp&7_FT9!)|Jxiwvj;knKbG~NUB6~Ca^jMct5=Pr} zfhWna#VW@b{J9R@aS`yJ?EOhO9BOQaH(zW`hY!;OJ zFMjLQFo`LB=EMyd?LUBt1b%e?r!Ak+>EKrj=`$^B#d3HM)d(Jg0QeRJ+6r+y#S`*< z=_b*(yX?A`r&Ih?f>y~-WJnby`a^^;JN52N>uK?LJ6wBCN3l3#qEVrw6&trY z3dhE~>Ivq^0hs`X)B!Q_gyt96zfp_;Nlr9YcOl<-j+H({*n&;7OBc|{8IPC}JITXy z=84Oh*MU~n)MX+{_VI8sYA%u_P%@`^v1RioSlg@;&guz;d?oyZxVn>M}UQ{+x6$$Lq?{4TlM3& z$9M%*nPU$dY_T@iq)lMG%MSiRwcg7gIprjZW+kW^E_abNamCE#LevEwV+ZL49lGX9 zg^=atyQI%CBsEXVsrNuDPA%*bp%cLHZ z1^|e9VYU~nMWWt@t`83@%u@hKA>4PYYL2_scq0Nx9^DMkY}%)3ON8aPir!7fXTjy* z;GqfBkZGXUf=cM0h_!qxwYAD3wp{qonOV;ftksKlhc4;sqcp*~u;Mg!vctv@WS;0a z%)maSJJU74i`KjK-nztVq=@La+u+%R6$7l`v7xa>(eU@u>*=7aDa2&WS&fD)(sT&U z*$(F7F&Zb$L@tlel$Hv$sQ%2u6_i%Or;y2*oHb)5x+sNaUq=t~sTGp}w3RrFD|%W6<|K@o6S}T&M0kGz^CNXc_<&3E6TCB| zRi_G3`HAtPcGUKUznEX!5+~Dpl1KgNnzDoL_8GJztLq~dQ+m(1XZoFDWC<9tvaH5c z?vkHU7+VwF4|^G=Uk&;eBc%Nd9Piz;RsW{n3ElMh`N4F+bNsq~K_5!1N$XxHHv|=MPST z!gn6sv#V8ddZ*Mg+wFsUvi~x{p9KU?^mko?aS|u#&123Z6Bzd&wyK-U&r+?SF4Iv1 zX7~CQF%=?q>FJYj6C+4jll*1V9fw!o3{%lkI6C&H!VYB8mh49ZZviO06K5_y)?ce{ z86PR*2N&lYTaLpEF42VA+oIH@b68gmP6Cha>(*-mL(v62_SL?6Pt-0+a@XXjBS%aH zkPNt{(b0eW-_51^Bb3$VFAt@zvWs(0>ueQ=NTp@dyl^_CpNm)m2>Dk@8fShkh^KC~ zJrZR(C+A2FkvlZ!KLBtWgR!G`=4Cy69cjoy8!H6nrVM)bF=C>fM!%tSu%`LGhF2TeSE>RT%cE*h?0VAo`}$gu1PDSvKMaF$qye-Nr|Xu`yZ=KD1m_Zlw+D!Qy=F zQXV|?jLgGKG~m!J&8re#qExpKUkaCs_X-x-6CU5m@v^S|YZKw;q5yy1BS$wqZ0xzj$}cvKEu7)|b$Z#omUQpd(f?fu>OoHY4#EEz>=H9BYxE2=cV-6Zppr+#F#b&{)BJ`N~6Y{TV?3sgFW{O*CvZg zZE@Y4ffOKF)^cv;j6w-|)}#~r`N_zhdrPEEwCQa20ihGM>3A0I9b((>QNH@w@ErC7~6{idRu@b}&(1xWJ-Wh;lv=Tcde&?9%MA1Y1=chf9c}MWa zbQtHj9ek96VNx^}%U@PO_;cK<#xHEiXTR6$f0O{feg|~8*9>xQekFj z({A%ve*#@OU4RzArwzn1M?usCK6?$~=L4u9;J||Lk(F4$z{Rrb#Sap9sds_rmx&A9 zJ~&mkrz^OR=|X(+Gck@>(GSH5ke6i40?O7q(1A z#fsP1cc|Cc_vCA;RZRIOW5DL7dd1Do@1^IM@*c@Bb!FZo$0Z>r2#XzLAbsnF?*M=< zcdSMO5dCRP?qr}648Q|fY&hnVJf;&k`vI|E9A=}^ttyJb*pd~Tx5-*nxP|6@ z_ZcXgmRkx@x8?Q~J{A=#O>AvYb54vQm60x(%kM!wz&gKpD^i4aP@74@S}&);6L;$H zo~P)tcIA~WEMAW>Xi9XEm)nGQbpfY%;VB5WD*0c(c%>*+!TCic8-CF^eOJeHp530k z+@{hFn4?w8zRe-p?008JzhvhQp1$?iuM_UxaQkdamwghn9~S(O5}n%}Mv986ZE7gD zyTngxPtc3HIb_{fSK2hYdiSClT|fKduv*Y$F};MLt;}>Twj3yAd7Y3!vfx;z8`+ zj@$iW)_P|$F@%I*#y!8{e$`oggjWSpDWZ4a}Urwu41+?4Z4 zG5DQkYfbqTZxEJ7IIu}L7dPj*`pb7P^jQ*n1|TNbz%LlFJ#>@P8VYcn)#Ye*J!GM6 zadsF^>-(j%7fb3kR#XhHBRtbDcZ$Bw7YiadGmn;UPoqSNBj-$2 z>{Z2PHZe95iK)?!l*aP*C?&ovj@qzuWABYmmZ<31ayBFa`b)Vf+bi z^D05Be-V68gzN5y1P<_sqSYO3PunILhN zU&|F@@<&$p&>Y*9zQ}Oxcs!x%d$HD&m`fVz62DMk?WPmY%w5)pgNz^7t0UPsItO8s zv`w|aN`;zG`IJS7Isb8rI1+T9-=1^?;;d1aMHvXJdp-r~9Re_h(oXrZzy#&Gty;VAqwCpv4z%fYmuaGv@FM?@&2@5XOacp+(@aAWkSPZO{$Y$brnqaiZU zbOvfRi70$VI{OYfd`2jIhFCuM4W&a^RQtwCJ zi_-;~5U%@f*!hk=15pe=&Ugq+g=_7W^#*j3nvfoQ-2&9cY3mE#!eo*2uM;sYe!)pHk5z;v1|NdHS!+>AA23#x*!!3rhn#kR4oh;FQh8(1}DqI_`~$5xy7Rd zMaK|jrga^z!*X)AHWf!Du8+Zgtw!i?WgJW&CvACKrt$`&PaDT&F|X!4J4Xw1(^Vch zG59Y}|KXN$ot~-T18)KT5&XY3wzdePi$WM6AR4TH22fJNC~lG!2?2mF9$q4t7-|<> z14}wwUJ8FBpdYj=oEVz>a1bNGO@fCxb1A`%%zk}7qgmc_!7Z^lWs3{?OfKu%?WTLa z`{lLwPZOPN*j=6A?%DQB&zJwrN6%=|kM}DxP}UFzIN}6LBxa(LHeo-{ojBVO;s`fpP~xuAdt{?qHBcASkZ#6$>CxyN~K6(oc!*Co{~! zSFykF8cmp&!j|`N@Bq_8B-l%Zn3ou4m>8XVe=w0ar}_C>8B`~&d!0J5^3^W-{cAAJ zvGNt8r)HP;m!BpIKymox4z%M_X*llI6kV`i5&ctc80Z&+Qd3o3OLx`5tr0+SXK$-} z3qQ>F1>WuX843W_8GATu(qO@#5p^}=A$Q6%;ak>l#metOD?w7xl-Q8h`Sg| z?vG>d@R2Z?bIQVwyNE!xz7)U>iV8iyNI&Mcd z|Kt-}cL7MaoQ4}@jGM}0azB^USkdu;rg`vmK~!8iuqyCInI4~(t38v{QMs*!6<0YC zfPbNSZz?KxnuRapBZWiq_4<9g%2e z>0zh1gIfi4X>j!g8;3xC@O&sz(tW9E81KKD$^mrWa^$fw)jQ|WS)EVy3GrTatd6G7PZqAu8?_ch?#b}isXomi{pLKqT54R@_OX?Aj= zaR5K+JjJ*RLz`S9#k04vpECV*|GdTYl21Da79O0H3+LIt3D)L^?(DtbdIM_FBL4eismj@Umx*u(E5l%k~Shf5QG?XdUnMC}^jdG8=Hk z4SGD7kyxCQRs&CuNyExm|m6p&s znEAcaZ*gcqH3U?oW)ZNh6xd}_MsVNvneyspea{Gh?C@p)5xnu4M%DfM&L9gD`Xw?%mjI z_>*nG&>Q4XF{SJr#4oxHT zWU?I;KxJ(r^P30Ttd!VfS(I4cB$!GJ%@4^m$jHE1o3x-|!fkMRo6dKPH~BS7=uOT( zrxW9n9~K6eR)MKYQA$vitXCD=ngD9F$|9Z$HFjC35`CKN5F66HU6`CrgI4lAJFTm0s`3;M=sEQL8DH=akB#Pd^(9ZJWC7g_5A)3Y zOPP4`qcOqm#7m8SM{-VVJedmjCN;EFe?mt7rfQlg;y<*ExYGS4Zi*t%ohvs!@)#~z zz^`&rQrhH}*gf}Vi^OB7j;4R|_T?5UsnO7W0&#z9XY-f}k%Xq1_~S*?+r+zMW7{vq zRJUfj9ysI8wKVYW zPjziLu}ZxOfT`339zed0yM!_d%)+!!XD9B=Kjbu|Ca7w9^h_!IRdBts$;gG?L_x?V30m0&?J5HVc|$?9Bhqx2$NLu< zB6GK3ky-x2h7jX{`E{qtuVI)FGVfvjz%7Em=i!>Moif1EWN!nh)#4i)JE*kCtTDm; z;jzIoqbQY^ssUi=B2i^Z^2x*4bYuAxf0Ih4zViVGWI--X)g5MoWDGVMzikH5QV}6# zOY4JdjgH7P9^G5aM+B%aT?HZ z>v82*70dL0%Rf$GUqWR)wE+A%uV2HwB(HY)2P_Y;zO;j& z#oow4?~w@OO|_)B5qBkq!dnO__N8V|IB(;m8HNv?phgpdMYL=11V(qunrr;BOUAC? zo_GS2qDQH9V}&?--+WvL@rdidKWj=T%anLGk;=1#zQ|6uaWEItK{K7DxY0q^l|v9Z z&C2W-Q2=4+4uvT7y|HT&75C(qFkOpNRVt0PZr{066SdRXmozP3AdT(h6NO8|RSoFV zMxa0TaBScM*m^91@^6=&uB%ICYj-Kvjep{5lYDL}29Duc&Fa~I%d)oH-F#79WDd0M zjX>);$FgLC(qr58V#<;;IhEWfUt$@o&I(3UQ2=hEU29otP}|?D>8lcZIyRTg$jyPi zg5i;Q0&L(|+!2@}YWJ@A)1M63MLlqH#ouKprkd{X2EOjqFVElEM74%?h|h7XCi49q z2T`e-p3a<-SH~8&mX@UCJ^p!aVO?G5HpVfn8(N(mEUJyM%sPTUhk2jAYMJrzn)01V z_yEu}&-Txc**`Nnp(pib?f70Aj^<81QogE-OigG3*`!NVrAOLkOvgOhryKMHDLWdk zUmFixn>Whf;!+hPq;3mun8(-*Kiv8aXd=IZP&5!(Q1jo4@Jdi`o~?J!DCoa85Gy72 z_xLJZkO{8f% zApas-pIZ=w|4fqjjOiqT0|8~gCjknHlH#Y}nxLJs!T&qQVyD?E0|EpD2A*Ues7(A% z?>MU;-L6SO>#O!~WGdsIl6MoL7#R~0G8>VF7hs6&q`g2?zzrR)94rK?Xu9vobjQgw zzvuHwo#PHL+btg`s3MEh8F?UocQU-1Tf0rZ!)`^Of2f%(Hw~9bcnU2g++8bgG90e{ z(O=I6X+xjULPu4y9e2n90p#5I4=OA~0h+u$VcplpgV*^x_55jb#@l38hd)WIdl7w4 zSd*~_Qg!yQfSt7D+s+FgQ`^gk0{WW38oB|6VAe%-L8zHD18aEmq<*Ym-p&z&4quw- z5OY{dyQze!BdHQm?rZIQy|RK2Z4kOx#EPZ@oER*v6PS;W?+QGysiSaz&oj-ZN~>`;rQcb1!uftGJ%6} z#b%EJOF}<8Dx_697Z`QM9~pBKR9%Q%=C02jWjE7EU*7D~>iu*io z$9MtofWv}3XlX3+I4FF|thyyeB;NJ6=%H!3kGr)X>N;e;5Hdy5K~|;?>_&}~4CJck zR%IyKBj+SO1y3et*f!*e?!rvZ!c0fxBEtBZS*5xaB?ge$3X?`6gARt=pgVlt=9BxG7F1FW8xJN*0df0&>3hJP8Q)}KfiBkk9-X; zVD0r}HS@mkzY*N?0*TD(OSA{}^`6%(ZyW(1+5WEwb%p>)CvZV2uCNV~RPyX${OxL~ z_9#X2YLcn4yt_GmXzXffteZ}LWp#O7)f)3673D_~&OdgikYs98V1Pj_X?_i! z&GPlk0@mm-(hUD2p@=l}ZnT;8($X|3_kj{0$rkjQY7FdBpK5JzRaBmg*2A7yr`)MZwhbH{^puRdD24!MO@u#t;3z!OBT!i}6G z$Kc}sR>sYSIbQ^S&O>OT@@e#HsLA!%D=d4O`8}?}YIKX{a zE8F->-Cz3*|97n;VMi0zgJd5=dHg)h+qHzZ=;~-6pg&-s$>?3_ga>$Tl9-|^_hio< zumWKE2W}+ZaZq4MD6Xg6F_fB58tDv~V&8}&C!{G0DUZ+@DmTy;4rxA%S+P9diHT`T zfOkTPN}%tIGxgW?(PMMiz{NWU+?Xvo|INC=o^~6iB@OcC2`r=0MSI3&x>~-TmB}d4 z5nsA`j<87orQcHEf(!U{I;~KgH}C*+FuJ6vJ}Yq&8u{LNNTKU=l0rn*mzbX^UEJ+-gt(5^ecl;85t?V!6B9)F2_H&tbf{Cjz+ z^NMc}@zi)JNBTu|wdcIB6m#i)gh8|(6W#ioY|-i?rry^xZx~=PTX`oc|LjF|<~Bpg zjde|qX>s~9%zYaNHR8zMax+V(z`rH`dYt$BO9T{=qT-2nfT*yu`g>2xg50-{&m>|{&mHK4ILM2^?St@2&5$m z%q_C#1eqibuOHyHpo1F73Nsg%unnc2&va$coPs!P}qG!UBj6qSJUR>St} z2D8EPu>T^zM<_XH6$%*@c4#1MNHp^H3aL>tI7+f!EF6G=RN@#t8{&kXX2oCx_d*4p zmhuhx4{*hV0`F-*KS1tO_fpuuIM7j4*B6NTR0%{V? zV*vg4%yKU*L-`j02NOX3i{|@h{&$Y0cqINW?zIc#e~74M5%S+S(&+F1rD8&VA+Uda zyVX~-Rqmi-dp@H;ge~q$;Q}YCkX2%`2Ot31XMtzp}0JS>XxXLZm zEIQ7%0|H37Y9CbP;9>KYQa3qZ^X|`}iaarmzJ4Tp$n6B8CM(W#b#|~jZBT96&fTGF zw_;nZlbQ7L0|c;TwVB-*xsKi)+bJ?|KJ=T)3mtw(QrR#I7z%@Sq_SHHZ~8n3p-YjG z{+Rhm2E@ppqARdaquXEGfNDM4W=;M^8{rDxLY%vo2~A%y?iAvZ6)dFGi;uVA#LnQ& zA0X_|XY)a1VbzGnC&tPtq4i@z^0v;9)l!v_o{dMFW!MS__N^rcX?z)?Kr;}YWFzJ5{Rb0T_c`j4fCM&%Mx@ zSQ{=l{l>4MRyY>#IsGjBBEKc}G5EFN^`K?{7_VoTS?eGfwuAl1A&f%RfYSU`l}gC7 z0(HVYP;wQ&7Yh?4mT)#Ia|%p<2JU=D*0J-wyV>eY<^RoGkV87HyXJb7OI+L1BjP*3 zfXs;GlfPu`m-j8&2asf*9F0FZNIydT`yh?>hNj_7LDszNfP<{EO;#$B$lESTK_MVv!;jSqi)}FxBj1`Sg1i0vspOh$Sk!Z^ZSTX+X|JSJa zADXuI)w`zuL(@u#B&1CGCcU`!|519&E)n>@M8>nlXqfsZVk3z-Q3znKxS)jg%VA|s znFq^J^%h(V4%g8C~psOFUqh_%H;JjjwE-3!?j*! zPt@0n4In}QUjGK^aP55j8;Oz{s)?YwAR6` zJG3}7E{XxGn>|+Qo;`qt%;n7=qQQ*t9L^toaNaX326XGK8b7@lyL^>>YI6ne_l;=D zsXx13=;U^T<*LS*1DWs{b^I)yYRR#ST-t~ftD)t2>=#ANGQG(;7 zG0ZFF56R)|8nxHX8ah zKC#A2QGqc;svR5$+x-a!50N=IzhL$@2HY!WN9e(!)ev6Y8nkKAyq73$K+H~SaolAe zGWm*9e(+n)uuI?T;BfD>*U4-;Y&df=drWUz|9S-Hoh-tzoNC@e=%Tb@Yuv1&2&Zmj z(c;?geyXBu(@+8S@ZFE5M40XVzcQ{pD5@%qpS{xfV3ByF%LQ^-z(84$00S2UwB)IP zm@)#!L&Q~L6NOR21kte4S=SU6NqB4buL4e6C#1&XznILhn{t!nDQyUo5rf<*P zVfX5tx&NH=`+nc=eBU|uo^v0k{c|^Y^`^Rnku0+wUyUqP*)Fy|JQ~otJ7Lf_^Vr!> zChC96`SDDaaAizI`8=hGGn-g(iZ+ zXMg<>o^#MaRZQ}e{Y}0NS`;((qO4O@Tdgj2*dh4aR2K5&od$P@N8LGC0rVv9d_rb-|3XYdFRJc{&f1{)W^$4 zt~}ng;O2_P(5Rk-bLpFopZ0P&|Mdnt^Zd(6^2j&+o>qjZ4hW}22ivyS&Pnc$+tpno zIP<|trxv>*tImI?soVcXnw5(BuBynYhs=UUx;fT`-nfQy>dCN zi0l7Q(Yks@U0x&n)9ULq=%w3(V&<%%Fs%qb>LnIa>C&!vHC0{&-yJWC)30cSkTbWYu z>eW^t?c%`vEj5hMftD03v=M`=ngeeWZ#!BmAY}avLVS&}L|~(5HiPI05e(hr40QiN zn`0<~a82ROGcdJ91A9p9aOPqj(8V$t_LL;E%at~|?4oFD0Nb(D{MANBN+@uR&rIm; zw5H&R+>IwD(2I69AyS^FBv%TK60poa4c*0uVV zX%^tC?*b+laz_$ScML{^AC1e22$pd%{{~~eE{S?ncP-?~B5$$Gl2nkUIm3}#LOMx; zGAvW3Lj5g!vF;ueqS-R8Lh@1!ikGnv(wqt!O)zJ)y_;puZh;*Ij*yUR360%CYHWD; z@oMKRmcu=T3j_^tK*N&|LRRs-Y*!#_CEUfF&U=)GW!~5co+n%( zvDgBZ_v^9I11!U2L&iWThZCUfU=9>yE(UWg=h(0YCPc<_ymUAGoMwY7VkaG#Be>Vc zG81{!xwwB3SGJ~~Wu*C7CV|8K+;kPHdRXQ+6~#U1aiO6AB0H6bqv-|4(vRopc?pc( zkWDLERX1xg0Eb)`_HDkR+ohDTBZq}}eSo2?@vTN=6k~SWc&w>s!2PNOT$+T_L5|;@ zdGZ*}uz$r%B>z$&FNfxSZ>n?cirMy+$T*2((Vxc{d=Wf*!?{MQG2bhzshsG&wmLan zc>xBfsD_sR(#Lh6>A5ya4B!#ot;LjiKysfQXS}bMWxB4>G$ttL>C#9QjX9b<3Bkxk zpej!uK~n?f(hi2zjubdO8A9pr@qn?7l+{C{_cV-sJo`H4*UOOG1zMINEg;iM#dvE8 cH@=Vla|bExNk@0VU(W!vFvP diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3c093feb..a3200f62 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jun 12 09:17:06 IDT 2017 +#Tue Jul 18 12:33:44 IDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.1-all.zip diff --git a/gradlew b/gradlew index 4453ccea..cccdd3d5 100755 --- a/gradlew +++ b/gradlew @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -155,7 +155,7 @@ if $cygwin ; then fi # Escape application args -save ( ) { +save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } From 920cfbb6b96127e19cb86f37d1f6e793bb9e68f7 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 18 Jul 2017 13:46:34 +0300 Subject: [PATCH 321/592] Set javadoc encoding to UTF-8. --- java_shared.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java_shared.gradle b/java_shared.gradle index bbb305e4..4194cca7 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -6,6 +6,10 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } +javadoc { + options.encoding = 'UTF-8' +} + test { testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' From ff93e787a8714eee0a7e648a7e6bc37573ad6bb9 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Wed, 19 Jul 2017 18:49:22 +0300 Subject: [PATCH 322/592] Make restore test run in parallel --- .../java/com/cloudinary/test/AbstractApiTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 a3c5e920..7fda665d 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 @@ -45,6 +45,7 @@ abstract public class AbstractApiTest extends MockableTest { public static final String DELETE_TRANSFORMATION_NAME = "c_scale,l_text:Arial_60:" + SUFFIX + "_delete,w_100"; public static final Transformation DELETE_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX + "_delete").fontFamily("Arial").fontSize(60)); public static final String TEST_KEY = "test-key" + SUFFIX; + public static final String API_TEST_RESTORE = "api_test_restore" + SUFFIX; protected Api api; @@ -708,18 +709,18 @@ public void testFolderApi() throws Exception { public void testRestore() throws Exception { // should support restoring resources 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()); + 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); - api.deleteResources(Collections.singletonList("api_test_restore"), ObjectUtils.emptyMap()); - resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + api.deleteResources(Collections.singletonList(API_TEST_RESTORE), ObjectUtils.emptyMap()); + resource = api.resource(API_TEST_RESTORE, ObjectUtils.emptyMap()); assertEquals(resource.get("bytes"), 0); assertTrue((Boolean) resource.get("placeholder")); - Map response = api.restore(Collections.singletonList("api_test_restore"), ObjectUtils.emptyMap()); - Map info = (Map) response.get("api_test_restore"); + Map response = api.restore(Collections.singletonList(API_TEST_RESTORE), ObjectUtils.emptyMap()); + Map info = (Map) response.get(API_TEST_RESTORE); assertNotNull(info); assertEquals(info.get("bytes"), 3381); - resource = api.resource("api_test_restore", ObjectUtils.emptyMap()); + resource = api.resource(API_TEST_RESTORE, ObjectUtils.emptyMap()); assertEquals(resource.get("bytes"), 3381); } From 62a53416bc7611e97d9de4865e95abd826373c72 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Jun 2017 14:46:02 +0300 Subject: [PATCH 323/592] Add generics to `Transformation` class, Streaming profile support. Allows for chaining commands when working with derived classes and calling a super method. Eager transformation generation logic moved from `Utils`into 'EagerTransformation'. --- .../com/cloudinary/EagerTransformation.java | 17 +- .../java/com/cloudinary/Transformation.java | 191 +++++++++--------- .../src/main/java/com/cloudinary/Util.java | 12 +- .../com/cloudinary/test/CloudinaryTest.java | 1 - .../cloudinary/test/AbstractUploaderTest.java | 6 + 5 files changed, 123 insertions(+), 104 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index 1f7ebc0b..dd6cedbd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -1,9 +1,12 @@ package com.cloudinary; +import com.cloudinary.utils.StringUtils; + +import java.util.ArrayList; import java.util.List; import java.util.Map; -public class EagerTransformation extends Transformation { +public class EagerTransformation extends Transformation { protected String format; @SuppressWarnings("rawtypes") @@ -23,4 +26,16 @@ public EagerTransformation format(String format) { public String getFormat() { return format; } + + @Override + public String generate(Map options) { + List eager = new ArrayList<>(); + eager.add(super.generate(options)); + + if (StringUtils.isNotBlank(format)){ + eager.add(format); + } + + return StringUtils.join(eager, "/"); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 37dbe42f..e6bc5757 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -12,7 +12,7 @@ import com.cloudinary.utils.StringUtils; @SuppressWarnings({"rawtypes", "unchecked"}) -public class Transformation implements Serializable{ +public class Transformation implements Serializable{ public static final String VAR_NAME_RE = "^\\$[a-zA-Z][a-zA-Z0-9]+$"; protected Map transformation; protected List transformations; @@ -49,305 +49,305 @@ public Transformation() { chain(); } - public Transformation width(Object value) { + public T width(Object value) { return param("width", value); } - public Transformation height(Object value) { + public T height(Object value) { return param("height", value); } - public Transformation named(String... value) { + public T named(String... value) { return param("transformation", value); } - public Transformation crop(String value) { + public T crop(String value) { return param("crop", value); } - public Transformation background(String value) { + public T background(String value) { return param("background", value); } - public Transformation color(String value) { + public T color(String value) { return param("color", value); } - public Transformation effect(String value) { + public T effect(String value) { return param("effect", value); } - public Transformation effect(String effect, Object param) { + public T effect(String effect, Object param) { return param("effect", effect + ":" + param); } - public Transformation angle(int value) { + public T angle(int value) { return param("angle", value); } - public Transformation angle(String... value) { + public T angle(String... value) { return param("angle", value); } - public Transformation border(String value) { + public T border(String value) { return param("border", value); } - public Transformation border(int width, String color) { + public T border(int width, String color) { return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); } - public Transformation x(Object value) { + public T x(Object value) { return param("x", value); } - public Transformation y(Object value) { + public T y(Object value) { return param("y", value); } - public Transformation radius(Object value) { + public T radius(Object value) { return param("radius", value); } - public Transformation quality(Object value) { + public T quality(Object value) { return param("quality", value); } - public Transformation defaultImage(String value) { + public T defaultImage(String value) { return param("default_image", value); } - public Transformation gravity(String value) { + public T gravity(String value) { return param("gravity", value); } - public Transformation colorSpace(String value) { + public T colorSpace(String value) { return param("color_space", value); } - public Transformation prefix(String value) { + public T prefix(String value) { return param("prefix", value); } - public Transformation overlay(String value) { + public T overlay(String value) { return param("overlay", value); } - public Transformation overlay(AbstractLayer value) { + public T overlay(AbstractLayer value) { return param("overlay", value); } - public Transformation underlay(String value) { + public T underlay(String value) { return param("underlay", value); } - public Transformation underlay(AbstractLayer value) { + public T underlay(AbstractLayer value) { return param("underlay", value); } - public Transformation fetchFormat(String value) { + public T fetchFormat(String value) { return param("fetch_format", value); } - public Transformation density(Object value) { + public T density(Object value) { return param("density", value); } - public Transformation page(Object value) { + public T page(Object value) { return param("page", value); } - public Transformation delay(Object value) { + public T delay(Object value) { return param("delay", value); } - public Transformation opacity(int value) { + public T opacity(int value) { return param("opacity", value); } - public Transformation rawTransformation(String value) { + public T rawTransformation(String value) { return param("raw_transformation", value); } - public Transformation flags(String... value) { + public T flags(String... value) { return param("flags", value); } - public Transformation dpr(float value) { + public T dpr(float value) { return param("dpr", value); } - public Transformation dpr(int value) { + public T dpr(int value) { return param("dpr", value); } - public Transformation dpr(String value) { + public T dpr(String value) { return param("dpr", value); } - public Transformation duration(String value) { + public T duration(String value) { return param("duration", value); } - public Transformation duration(float value) { + public T duration(float value) { return param("duration", new Float(value)); } - public Transformation duration(double value) { + public T duration(double value) { return param("duration", new Double(value)); } - public Transformation durationPercent(float value) { + public T durationPercent(float value) { return param("duration", new Float(value).toString() + "p"); } - public Transformation durationPercent(double value) { + public T durationPercent(double value) { return param("duration", new Double(value).toString() + "p"); } - public Transformation startOffset(String value) { + public T startOffset(String value) { return param("start_offset", value); } - public Transformation startOffset(float value) { + public T startOffset(float value) { return param("start_offset", new Float(value)); } - public Transformation startOffset(double value) { + public T startOffset(double value) { return param("start_offset", new Double(value)); } - public Transformation startOffsetPercent(float value) { + public T startOffsetPercent(float value) { return param("start_offset", new Float(value).toString() + "p"); } - public Transformation startOffsetPercent(double value) { + public T startOffsetPercent(double value) { return param("start_offset", new Double(value).toString() + "p"); } - public Transformation endOffset(String value) { + public T endOffset(String value) { return param("end_offset", value); } - public Transformation endOffset(float value) { + public T endOffset(float value) { return param("end_offset", new Float(value)); } - public Transformation endOffset(double value) { + public T endOffset(double value) { return param("end_offset", new Double(value)); } - public Transformation endOffsetPercent(float value) { + public T endOffsetPercent(float value) { return param("end_offset", new Float(value).toString() + "p"); } - public Transformation endOffsetPercent(double value) { + public T endOffsetPercent(double value) { return param("end_offset", new Double(value).toString() + "p"); } - public Transformation offset(String value) { + public T offset(String value) { return param("offset", value); } - public Transformation offset(String[] value) { + public T offset(String[] value) { if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); return param("offset", value); } - public Transformation offset(float[] value) { + public T offset(float[] value) { if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); Number[] numberArray = new Number[]{value[0], value[1]}; return offset(numberArray); } - public Transformation offset(double[] value) { + public T offset(double[] value) { if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); Number[] numberArray = new Number[]{value[0], value[1]}; return offset(numberArray); } - public Transformation offset(Number[] value) { + public T offset(Number[] value) { if (value.length < 2) throw new IllegalArgumentException("Offset range must include at least 2 items"); return param("offset", value); } - public Transformation videoCodec(String value) { + public T videoCodec(String value) { return param("video_codec", value); } - public Transformation videoCodec(Map value) { + public T videoCodec(Map value) { return param("video_codec", value); } - public Transformation audioCodec(String value) { + public T audioCodec(String value) { return param("audio_codec", value); } - public Transformation audioFrequency(String value) { + public T audioFrequency(String value) { return param("audio_frequency", value); } - public Transformation audioFrequency(int value) { + public T audioFrequency(int value) { return param("audio_frequency", value); } - public Transformation bitRate(String value) { + public T bitRate(String value) { return param("bit_rate", value); } - public Transformation bitRate(int value) { + public T bitRate(int value) { return param("bit_rate", new Integer(value)); } - public Transformation videoSampling(String value) { + public T videoSampling(String value) { return param("video_sampling", value); } - public Transformation videoSamplingFrames(int value) { + public T videoSamplingFrames(int value) { return param("video_sampling", value); } - public Transformation videoSamplingSeconds(Number value) { + public T videoSamplingSeconds(Number value) { return param("video_sampling", value.toString() + "s"); } - public Transformation videoSamplingSeconds(int value) { + public T videoSamplingSeconds(int value) { return videoSamplingSeconds(new Integer(value)); } - public Transformation videoSamplingSeconds(float value) { + public T videoSamplingSeconds(float value) { return videoSamplingSeconds(new Float(value)); } - public Transformation videoSamplingSeconds(double value) { + public T videoSamplingSeconds(double value) { return videoSamplingSeconds(new Double(value)); } - public Transformation zoom(String value) { + public T zoom(String value) { return param("zoom", value); } - public Transformation zoom(float value) { + public T zoom(float value) { return param("zoom", new Float(value)); } - public Transformation zoom(double value) { + public T zoom(double value) { return param("zoom", new Double(value)); } - public Transformation aspectRatio(double value) { + public T aspectRatio(double value) { return param("aspect_ratio", new Double(value)); } - public Transformation aspectRatio(String value) { + public T aspectRatio(String value) { return param("aspect_ratio", value); } - public Transformation aspectRatio(int nom, int denom) { + public T aspectRatio(int nom, int denom) { return aspectRatio(Integer.toString(nom) + ":" + Integer.toString(denom)); } - public Transformation responsiveWidth(boolean value) { + public T responsiveWidth(boolean value) { return param("responsive_width", value); } @@ -364,7 +364,7 @@ public Condition ifCondition() { * @param condition a condition string * @return the transformation for chaining */ - public Transformation ifCondition(String condition) { + public T ifCondition(String condition) { return param("if", condition); } @@ -374,7 +374,7 @@ public Transformation ifCondition(String condition) { * @param expression a condition * @return the transformation for chaining */ - public Transformation ifCondition(Expression expression) { + public T ifCondition(Expression expression) { return ifCondition(expression.toString()); } @@ -383,16 +383,16 @@ public Transformation ifCondition(Expression expression) { * @param condition a condition * @return the transformation for chaining */ - public Transformation ifCondition(Condition condition) { + public T ifCondition(Condition condition) { return ifCondition(condition.toString()); } - public Transformation ifElse() { + public T ifElse() { chain(); return param("if", "else"); } - public Transformation endIf() { + public T endIf() { chain(); int transSize = this.transformations.size(); for (int i = transSize - 1; i >= 0; i--) { @@ -420,7 +420,7 @@ public Transformation endIf() { * For example, 23-29.7 * @return the transformation for chaining */ - public Transformation fps(String value) { + public T fps(String value) { return param("fps", value); } @@ -429,7 +429,7 @@ public Transformation fps(String value) { * @param value the desired fps * @return the transformation for chaining */ - public Transformation fps(double value) { + public T fps(double value) { return param("fps", new Float(value)); } @@ -438,10 +438,14 @@ public Transformation fps(double value) { * @param value the desired fps * @return the transformation for chaining */ - public Transformation fps(int value) { + public T fps(int value) { return param("fps", new Integer(value)); } + public T streamingProfile(String value){ + return param("streaming_profile", value); + } + public boolean isResponsive() { return this.isResponsive; } @@ -451,25 +455,25 @@ public boolean isHiDPI() { } // Warning: options will destructively updated! - public Transformation params(Map transformation) { + public T params(Map transformation) { this.transformation = transformation; transformations.add(transformation); - return this; + return (T) this; } - public Transformation chain() { + public T chain() { return params(new HashMap()); } - public Transformation chainWith(Transformation transformation) { + public T chainWith(Transformation transformation) { List transformations = dup(this.transformations); transformations.addAll(dup(transformation.transformations)); - return new Transformation(transformations); + return (T) new Transformation(transformations); } - public Transformation param(String key, Object value) { + public T param(String key, Object value) { transformation.put(key, value); - return this; + return (T) this; } /** @@ -618,7 +622,8 @@ public String generate(Map options) { "p", "prefix", "pg", "page", "u", "underlay", - "vs", "video_sampling" + "vs", "video_sampling", + "sp", "streaming_profile" }; for (int i = 0; i < simple_params.length; i += 2) { @@ -797,7 +802,7 @@ private static String processVideoCodecParam(Object param) { * @param value the value to assign to the variable * @return this for chaining */ - public Transformation variable(String name, Object value) { + public T variable(String name, Object value) { return param(name, value); } @@ -806,7 +811,7 @@ public Transformation variable(String name, Object value) { * @param variables variable expressions * @return this for chaining */ - public Transformation variables(Expression...variables) { + public T variables(Expression...variables) { return param("variables", variables); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index e3c76a74..927f2beb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -68,21 +68,15 @@ protected static final String buildEager(List transfor if (transformations == null) { return null; } + List eager = new ArrayList(); for (Transformation transformation : transformations) { - List single_eager = new ArrayList(); String transformationString = transformation.generate(); if (StringUtils.isNotBlank(transformationString)) { - single_eager.add(transformationString); - } - if (transformation instanceof EagerTransformation) { - EagerTransformation eagerTransformation = (EagerTransformation) transformation; - if (StringUtils.isNotBlank(eagerTransformation.getFormat())) { - single_eager.add(eagerTransformation.getFormat()); - } + eager.add(transformationString); } - eager.add(StringUtils.join(single_eager, "/")); } + return StringUtils.join(eager, "|"); } 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 8690e1a0..261729de 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1006,7 +1006,6 @@ public void testOverlayOptions() { } } - @Test @SuppressWarnings("deprecation") public void testBackwardCampatibleOverlayOptions() { 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 ca1efe97..166a4178 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 @@ -179,6 +179,12 @@ public void testUniqueFilename() throws Exception { assertEquals(result.get("public_id"), "old_logo"); } + @Test + public void testEagerWithStreamingProfile() throws IOException { + Transformation transformation = new EagerTransformation().format("m3u8").streamingProfile("full_hd"); + assertEquals("sp_full_hd/m3u8", transformation.generate()); + } + @Test public void testExplicit() throws IOException { Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload", "moderation", "manual")); From c89c40466fa1bf37dffca441b99a03c29c0f7f72 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Jun 2017 15:04:56 +0300 Subject: [PATCH 324/592] Add test for listing transformations with cursor. --- .../java/com/cloudinary/test/AbstractApiTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 7fda665d..6c28b0ae 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 @@ -240,6 +240,20 @@ public void testResourcesListingStartAt() throws Exception { assertEquals(response.get("public_id"), resources.get(0).get("public_id")); } + @Test + public void testTransformationsWithCursor() throws Exception { + String name = "testTransformation" + SDK_TEST_TAG; + api.createTransformation(name, "c_scale,w_100", null); + final List transformations = new ArrayList<>(); + String next_cursor = null; + do { + Map result = api.transformations(ObjectUtils.asMap("max_results", 500, "next_cursor", next_cursor)); + transformations.addAll((List) result.get("transformations")); + next_cursor = (String) result.get("next_cursor"); + } while (next_cursor != null ); + assertThat(transformations, hasItem(allOf(hasEntry("name", "t_" + name)))); + } + @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids From 9206a89b6abff2bb3d7e593cc5f3dec9d3359b93 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Jun 2017 15:35:08 +0300 Subject: [PATCH 325/592] Fix test to run in parallel. --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6c28b0ae..63bf414d 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 @@ -242,7 +242,7 @@ public void testResourcesListingStartAt() throws Exception { @Test public void testTransformationsWithCursor() throws Exception { - String name = "testTransformation" + SDK_TEST_TAG; + String name = "testTransformation" + SDK_TEST_TAG + System.currentTimeMillis(); api.createTransformation(name, "c_scale,w_100", null); final List transformations = new ArrayList<>(); String next_cursor = null; From 544d7e689e5e7218204b46c45fb4ec8f9dd8fe68 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 13 Jul 2017 12:54:20 +0300 Subject: [PATCH 326/592] Allow deleteByToken to pass through when there's no api secret in config. --- .../main/java/com/cloudinary/android/UploaderStrategy.java | 6 +++--- .../cloudinary/strategies/AbstractUploaderStrategy.java | 7 +++++++ .../main/java/com/cloudinary/http42/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/http43/UploaderStrategy.java | 2 +- .../main/java/com/cloudinary/http44/UploaderStrategy.java | 2 +- .../java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 8c367560..93d24b31 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -25,9 +25,7 @@ public Map callApi(String action, Map params, Map options, Objec } boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - if (Boolean.TRUE.equals(options.get("unsigned"))) { - // Nothing to do - } else { + if (requiresSigning(action, options)) { String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); @@ -43,6 +41,8 @@ public Map callApi(String action, Map params, Map options, Objec params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); params.put("api_key", apiKey); } + } else { + // Nothing to do } String apiUrl = buildUploadUrl(action, options); diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index e12947a4..cd634f13 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -42,4 +42,11 @@ protected String buildUploadUrl(String action, Map options) { return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, resource_type, action}, "/"); } } + + protected boolean requiresSigning(String action, Map options) { + boolean unsigned = Boolean.TRUE.equals(options.get("unsigned")); + boolean deleteByToken = "delete_by_token".equals(action); + + return !unsigned && !deleteByToken; + } } \ No newline at end of file diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index dfed6846..014a1d5c 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -44,7 +44,7 @@ public Map callApi(String action, Map params, Map options, Objec boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + if (requiresSigning(action, options)) { uploader.signRequestParams(params, options); } else { Util.clearEmpty(params); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index cd8732d6..09d6b4cb 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -67,7 +67,7 @@ public Map callApi(String action, Map params, Map options, Objec boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + if (requiresSigning(action, options)) { uploader.signRequestParams(params, options); } else { Util.clearEmpty(params); diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index e509e321..f8877fb6 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -67,7 +67,7 @@ public Map callApi(String action, Map params, Map options, Objec boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - if (options.get("unsigned") == null || Boolean.FALSE.equals(options.get("unsigned"))) { + if (requiresSigning(action, options)) { uploader.signRequestParams(params, options); } else { Util.clearEmpty(params); 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 166a4178..0b76921c 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 @@ -88,7 +88,7 @@ public void testDeleteByToken() throws Exception { Map options = ObjectUtils.asMap("return_delete_token", true, "tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG}); Map res = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); String token = (String) res.get("delete_token"); - res = cloudinary.uploader().deleteByToken(token); + res = new Cloudinary(ObjectUtils.asMap("cloud_name", cloudinary.config.cloudName)).uploader().deleteByToken(token); assertNotNull(res); assertEquals("ok", res.get("result")); } From e1fbd34b18decf9c390ffbc8bbce7514cb7bd720 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 18 Jul 2017 11:11:26 +0300 Subject: [PATCH 327/592] Better comment. --- .../com/cloudinary/strategies/AbstractUploaderStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index cd634f13..abc7a41c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -35,7 +35,7 @@ protected String buildUploadUrl(String action, Map options) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); if (action.equals("delete_by_token")) { - // the only method (so far) that doesn't need resource_type + // delete_by_token doesn't need resource_type return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, action}, "/"); } else { String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); From 01ea9408c2e4d07a4c917dddbb28d29a0e0f7764 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 20 Jul 2017 15:06:49 +0300 Subject: [PATCH 328/592] Update TravisCI to explicitly set distribution (cherry picked from commit 9d76a98) --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1785a2e7..b665e3c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: android +dist: trusty +sudo: required +group: edge android: components: From 3f22d0115d547e5091319c8c16bb99e74d4cef6d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 13 Jul 2017 11:54:34 +0300 Subject: [PATCH 329/592] Add offset to `uploadLargeParts`to support upload resume. --- .../src/main/java/com/cloudinary/Uploader.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 7b329455..32804016 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -110,6 +110,10 @@ public Map uploadLarge(Object file, Map options, int bufferSize) throws IOExcept } public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallback progressCallback) throws IOException { + return uploadLarge(file, options, bufferSize, 0, null, progressCallback); + } + + public Map uploadLarge(Object file, Map options, int bufferSize, long offset, String uniqueUploadId, ProgressCallback progressCallback) throws IOException { InputStream input; long length = -1; boolean remote = false; @@ -136,7 +140,7 @@ public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallbac if (remote) { result = upload(file, options); } else { - result = uploadLargeParts(input, options, bufferSize, length, progressCallback); + result = uploadLargeParts(input, options, bufferSize, length, offset, uniqueUploadId, progressCallback); } return result; } finally { @@ -146,13 +150,13 @@ public Map uploadLarge(Object file, Map options, int bufferSize, ProgressCallbac } } - private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length, final ProgressCallback progressCallback) throws IOException { + private Map uploadLargeParts(InputStream input, Map options, int bufferSize, long length, long offset, String uniqueUploadId, final ProgressCallback progressCallback) throws IOException { Map params = buildUploadParams(options); Map sentOptions = new HashMap(); sentOptions.putAll(options); Map extraHeaders = new HashMap(); - extraHeaders.put("X-Unique-Upload-Id", cloudinary().randomPublicId()); + extraHeaders.put("X-Unique-Upload-Id", StringUtils.isBlank(uniqueUploadId) ? cloudinary().randomPublicId() : uniqueUploadId); sentOptions.put("extra_headers", extraHeaders); byte[] buffer = new byte[bufferSize]; @@ -160,10 +164,11 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon int bytesRead = 0; int currentBufferSize = 0; int partNumber = 0; - long totalBytes = 0; + long totalBytes = offset; Map response = null; final long knownLengthBeforeUpload = length; - long totalBytesUploaded = 0; + long totalBytesUploaded = offset; + input.skip(offset); while (true) { bytesRead = input.read(buffer, currentBufferSize, bufferSize - currentBufferSize); boolean atEnd = bytesRead == -1; @@ -172,7 +177,7 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon if (atEnd || fullBuffer) { totalBytes += currentBufferSize; - int currentLoc = bufferSize * partNumber; + long currentLoc = offset + bufferSize * partNumber; if (!atEnd) { //verify not on end - try read another byte bytesRead = input.read(nibbleBuffer, 0, 1); From 89b86be063985252bf561c9e028f93d5141f6038 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 20 Jul 2017 18:31:53 +0300 Subject: [PATCH 330/592] Fix javadoc errors --- .../src/main/java/com/cloudinary/utils/StringUtils.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 e9d56f8f..3d45f24a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -121,7 +121,11 @@ public static byte[] hexStringToByteArray(String s) { } /** - * {@see HtmlEscape.escapeHtml} + * Method for html escaping a String + * + * @param input The String to escape + * @return The escaped String + * @see HtmlEscape#escapeTextArea(String) */ public static String escapeHtml(String input) { return HtmlEscape.escapeTextArea(input); @@ -157,7 +161,7 @@ public static boolean isEmpty(String input) { /** * Verify that the input is an empty string or contains only whitespace characters.
- * {@see Character.isWhitespace} + * see {@link Character#isWhitespace(char)} * @param input a string * @return true if input is an empty string or contains only whitespace characters */ From 17ac9c3ec5ce7e7f26435e6bd310df9b8ffda194 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Thu, 20 Jul 2017 18:32:04 +0300 Subject: [PATCH 331/592] Version 1.14.0 --- CHANGELOG.md | 28 +++++++++++++++++++ README.md | 4 +-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c03f182..dcf897b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,32 @@ +1.14.0 / 2017-07-20 +=================== + +New functionality +----------------- + + * Add support for uploading remote urls through `Uploader.uploadLarge()` + * Support resuming `uploadLarge` + * Streaming profile support. + * Update TravisCI to explicitly set distribution + * Allow deleteByToken to pass through when there's no api secret in config. + +Other changes +------------- + + * Add test for listing transformations with cursor. + * Merge branch 'master' into patch-1 + * Make restore test run in parallel + * Set javadoc encoding to UTF-8. + * Update gradle to 4.0.1. + * Fix test to run in parallel. + * Remove use of `DatatypeConverter` which is not supported in Android + * Update Cloudinary dependencies version for java sample project. + * Merge pull request #84 from elevenfive/master + * Close responsestream + * Merge pull request #83 from theel0ja/patch-1 + * Improved formatting of markdown + 1.13.0 / 2017-06-12 =================== diff --git a/README.md b/README.md index a24e71bd..bc231c5e 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.13.0 + 1.14.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.13.0/cloudinary-core-1.13.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.13.0/cloudinary-http44-1.13.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.14.0/cloudinary-core-1.14.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.14.0/cloudinary-http44-1.14.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index b68f37ca..c6108978 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.13.0"; + public final static String VERSION = "1.14.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 80e3b4d6..9740cf05 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.13.0 +version=1.14.0 From fb5f034bae62258ff2f06d97e7e2294c495d9331 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 10 Aug 2017 15:53:46 +0300 Subject: [PATCH 332/592] Add format field to `ResponsiveBreakpoint`. --- .../com/cloudinary/ResponsiveBreakpoint.java | 33 ++++++++++++++++--- .../cloudinary/test/AbstractUploaderTest.java | 6 +++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java index 35b063ae..c0c0c02f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java @@ -1,10 +1,13 @@ package com.cloudinary; -import org.cloudinary.json.JSONObject; +import com.cloudinary.utils.StringUtils; -import java.util.Collections; +import org.cloudinary.json.JSONObject; public class ResponsiveBreakpoint extends JSONObject { + private Transformation transformation = null; + private String format = ""; + public ResponsiveBreakpoint() { put("create_derived", true); } @@ -19,14 +22,35 @@ public ResponsiveBreakpoint createDerived(boolean createDerived) { } public Transformation transformation() { - return (Transformation) opt("transformation"); + return transformation; } public ResponsiveBreakpoint transformation(Transformation transformation) { - put("transformation", Util.buildEager(Collections.singletonList(transformation))); + this.transformation = transformation; + updateTransformationKey(); + return this; + } + + + public ResponsiveBreakpoint format(String format) { + this.format = format; + updateTransformationKey(); return this; } + public String format() { + return format; + } + + private synchronized void updateTransformationKey() { + String transformationStr = transformation == null ? "" : transformation.generate(); + if (StringUtils.isNotBlank(format)){ + transformationStr += "/" + format; + } + + put("transformation", transformationStr); + } + public int maxWidth() { return optInt("max_width"); } @@ -62,5 +86,4 @@ public ResponsiveBreakpoint maxImages(Integer maxImages) { put("max_images", maxImages); return this; } - } 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 0b76921c..1b606207 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 @@ -493,7 +493,7 @@ public void testFilenameOption() throws Exception { @Test public void testResponsiveBreakpoints() throws Exception { - ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false).transformation(new EagerTransformation().format("gif").effect("sepia")); + ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false).format("gif"); // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", @@ -504,6 +504,9 @@ public void testResponsiveBreakpoints() throws Exception { assertEquals(2, breakpoints.size()); assertTrue(((Map) breakpoints.get(0)).get("url").toString().endsWith("gif")); + // check again with transformation + format + breakpoint.transformation(new Transformation().effect("sepia")); + // an array of breakpoints result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", new ResponsiveBreakpoint[]{breakpoint}, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) @@ -511,6 +514,7 @@ public void testResponsiveBreakpoints() throws Exception { breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); assertEquals(2, breakpoints.size()); + assertTrue(((Map) breakpoints.get(0)).get("url").toString().endsWith("gif")); // a JSONArray of breakpoints JSONArray array = new JSONArray(); From 4bc48c2889ac853a70c7945be06caad2d29d2a1a Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 17 Aug 2017 09:57:15 +0300 Subject: [PATCH 333/592] Fix project for java8, update cloudinary dependencies. --- samples/photo_album/pom.xml | 16 ++++++++-------- .../src/main/resources/META-INF/persistence.xml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index bf111dee..d38291c5 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -8,7 +8,7 @@ photo_album - 3.2.0.RELEASE + 4.3.10.RELEASE @@ -23,12 +23,12 @@ com.cloudinary cloudinary-taglib - 1.13.0 + 1.14.0 com.cloudinary cloudinary-http44 - 1.13.0 + 1.14.0 org.springframework @@ -90,13 +90,13 @@ org.hibernate.javax.persistence hibernate-jpa-2.0-api - 1.0.0.Final + 1.0.1.Final org.hibernate hibernate-entitymanager - 3.6.10.Final + 5.2.10.Final @@ -114,13 +114,13 @@ javax.validation validation-api - 1.1.0.Final + 2.0.0.Final org.hibernate - hibernate-validator-annotation-processor - 4.1.0.Final + hibernate-validator-annotation-processor + 6.0.1.Final diff --git a/samples/photo_album/src/main/resources/META-INF/persistence.xml b/samples/photo_album/src/main/resources/META-INF/persistence.xml index 898749ed..8a7baf27 100644 --- a/samples/photo_album/src/main/resources/META-INF/persistence.xml +++ b/samples/photo_album/src/main/resources/META-INF/persistence.xml @@ -1,7 +1,7 @@ - org.hibernate.ejb.HibernatePersistence + org.hibernate.jpa.HibernatePersistenceProvider From fe17b050e467702e59eaabc273d8f671991e71b5 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 22 Aug 2017 11:02:34 +0300 Subject: [PATCH 334/592] Fix boolean config values in tag generation --- .../taglib/CloudinaryJsConfigTag.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java index e0b3652e..53155db4 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java @@ -9,8 +9,8 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Singleton; -public class CloudinaryJsConfigTag extends SimpleTagSupport { - @SuppressWarnings("unused") +public class CloudinaryJsConfigTag extends SimpleTagSupport { + @SuppressWarnings("unused") public void doTag() throws JspException, IOException { Cloudinary cloudinary = Singleton.getCloudinary(); if (cloudinary == null) { @@ -18,23 +18,20 @@ public void doTag() throws JspException, IOException { } JspWriter out = getJspContext().getOut(); out.println(""); } - private void print(JspWriter out, String key,Object value) throws IOException { - out.println(key + ": \""+value + "\","); - - } + private void print(JspWriter out, String key, Object value) throws IOException { + if (value instanceof Boolean) { + out.println(key + ": " + ((Boolean) value ? "true" : "false") + ","); + } else { + out.println(key + ": \"" + value + "\","); + } + } } From 6f631ea5bdbb1ec6408ed9a6a13810de7b7ffd31 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 6 Jun 2017 12:09:55 +0300 Subject: [PATCH 335/592] Centralize response handling and respect `returnError` param. --- .../cloudinary/android/UploaderStrategy.java | 20 +------ .../strategies/AbstractUploaderStrategy.java | 56 ++++++++++++++++++- .../cloudinary/http42/UploaderStrategy.java | 27 +-------- .../cloudinary/http43/UploaderStrategy.java | 23 +------- .../cloudinary/http44/UploaderStrategy.java | 23 +------- 5 files changed, 59 insertions(+), 90 deletions(-) diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java index 93d24b31..9a3f1253 100644 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java @@ -119,25 +119,7 @@ public void totalBytesLoaded(long bytes) { responseStream.close(); } catch (Exception e) {} - if (code != 200 && code != 400 && code != 404 && code != 500) { - throw new RuntimeException("Server returned unexpected status code - " + code + " - " + responseData); - } - - JSONObject result; - try { - result = new JSONObject(responseData); - if (result.has("error")) { - JSONObject error = result.getJSONObject("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException(error.getString("message")); - } - } - return ObjectUtils.toMap(result); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } + return processResponse(returnError, code, responseData); } private long determineLength(Object file) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java index abc7a41c..d259e099 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java @@ -1,15 +1,20 @@ package com.cloudinary.strategies; -import java.io.IOException; -import java.util.Map; - import com.cloudinary.Cloudinary; import com.cloudinary.ProgressCallback; import com.cloudinary.Uploader; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; public abstract class AbstractUploaderStrategy { + private final static int[] ERROR_CODES = new int[]{400, 401, 403, 404, 420, 500}; protected Uploader uploader; public void init(Uploader uploader) { @@ -43,6 +48,51 @@ protected String buildUploadUrl(String action, Map options) { } } + protected Map processResponse(boolean returnError, int code, String responseData) { + String errorMessage = null; + Map result = null; + if (code == 200 || includesServerResponse(code)) { + try { + JSONObject responseJSON = new JSONObject(responseData); + result = ObjectUtils.toMap(responseJSON); + + if (result.containsKey("error")) { + Map error = (Map) result.get("error"); + error.put("http_code", code); + errorMessage = (String) error.get("message"); + } + } catch (JSONException e) { + errorMessage = "Invalid JSON response from server " + e.getMessage(); + } + } else { + errorMessage = "Server returned unexpected status code - " + code; + if (StringUtils.isNotBlank(responseData)) { + errorMessage += (" - " + responseData); + } + } + + if (StringUtils.isNotBlank(errorMessage)) { + if (returnError) { + // return a result containing the error instead of throwing an exception: + if (result == null) { + Map error = new HashMap(); + error.put("http_code", code); + error.put("message", errorMessage); + result = new HashMap(); + result.put("error", error); + } // else - Result is already built, with the error inside. Nothing to do. + } else { + throw new RuntimeException(errorMessage); + } + } + + return result; + } + + private boolean includesServerResponse(int code) { + return Arrays.binarySearch(ERROR_CODES, code) >= 0; + } + protected boolean requiresSigning(String action, Map options) { boolean unsigned = Boolean.TRUE.equals(options.get("unsigned")); boolean deleteByToken = "delete_by_token".equals(action); diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 014a1d5c..c6b3871d 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -1,9 +1,9 @@ package com.cloudinary.http42; import com.cloudinary.Cloudinary; +import com.cloudinary.ProgressCallback; import com.cloudinary.Util; import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.ProgressCallback; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.apache.http.HttpHost; @@ -18,8 +18,6 @@ import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; import java.io.File; import java.io.IOException; @@ -115,28 +113,7 @@ public Map callApi(String action, Map params, Map options, Objec InputStream responseStream = response.getEntity().getContent(); String responseData = StringUtils.read(responseStream); - if (code != 200 && code != 400 && code != 404 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } - return result; + return processResponse(returnError, code, responseData); } } diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 09d6b4cb..2199c573 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -136,28 +136,7 @@ public Map callApi(String action, Map params, Map options, Objec response.close(); } - if (code != 200 && code != 400 && code != 404 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } - return result; + return processResponse(returnError, code, responseData); } } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index f8877fb6..20e18258 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.Collection; import java.util.Map; @@ -136,27 +137,7 @@ public Map callApi(String action, Map params, Map options, Objec response.close(); } - if (code != 200 && code != 400 && code != 404 && code != 500) { - throw new RuntimeException("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 (result.containsKey("error")) { - Map error = (Map) result.get("error"); - if (returnError) { - error.put("http_code", code); - } else { - throw new RuntimeException((String) error.get("message")); - } - } + Map result = processResponse(returnError, code, responseData); return result; } } From 33f0cf618f0b76d5efd1a5a691d4ad12816973b1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 22 Aug 2017 11:36:01 +0300 Subject: [PATCH 336/592] Remove Android from project. --- cloudinary-android/README.md | 218 -------- cloudinary-android/build.gradle | 144 ----- cloudinary-android/project.properties | 15 - .../src/androidTest/assets/docx.docx | Bin 20453 -> 0 bytes .../src/androidTest/assets/images/favicon.ico | Bin 1150 -> 0 bytes .../androidTest/assets/images/old_logo.png | Bin 3381 -> 0 bytes .../cloudinary/android/test/UploaderTest.java | 504 ------------------ .../template/AndroidManifest.xml.sample | 20 - .../src/main/AndroidManifest.xml | 5 - .../com/cloudinary/android/ApiStrategy.java | 17 - .../cloudinary/android/MultipartUtility.java | 162 ------ .../cloudinary/android/UploaderStrategy.java | 151 ------ .../java/com/cloudinary/android/Utils.java | 22 - settings.gradle | 1 - 14 files changed, 1259 deletions(-) delete mode 100644 cloudinary-android/README.md delete mode 100644 cloudinary-android/build.gradle delete mode 100644 cloudinary-android/project.properties delete mode 100755 cloudinary-android/src/androidTest/assets/docx.docx delete mode 100755 cloudinary-android/src/androidTest/assets/images/favicon.ico delete mode 100644 cloudinary-android/src/androidTest/assets/images/old_logo.png delete mode 100644 cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java delete mode 100644 cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample delete mode 100644 cloudinary-android/src/main/AndroidManifest.xml delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java delete mode 100644 cloudinary-android/src/main/java/com/cloudinary/android/Utils.java diff --git a/cloudinary-android/README.md b/cloudinary-android/README.md deleted file mode 100644 index 303d762e..00000000 --- a/cloudinary-android/README.md +++ /dev/null @@ -1,218 +0,0 @@ -Cloudinary -========== - -Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. - -Easily 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. - -Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. - -Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. - -For Android, Cloudinary provides a library for simplifying the integration even further. The library requires Android 2.3 or higher. - -## Manual Setup ###################################################################### -Download cloudinary-core-1.13.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-core/1.13.0/cloudinary-core-1.13.0.jar) and cloudinary-android-1.13.0.jar from [here](http://search.maven.org/remotecontent?filepath=com/cloudinary/cloudinary-android/1.13.0/cloudinary-android-1.13.0.jar) and put them in your libs folder. - -## Maven Integration ###################################################################### -The cloudinary_java library is available in [Maven Central](http://repo1.maven.org/maven/). To use it, add the following dependency to your pom.xml: - - - com.cloudinary - cloudinary-android - 1.13.0 - - - -## Try it right away - -Sign up for a [free account](https://cloudinary.com/users/register/free) so you can try out image transformations and seamless image delivery through CDN. - -*Note: Replace `demo` in all the following examples with your Cloudinary's `cloud name`.* - -Accessing an uploaded image with the `sample` public ID through a CDN: - - http://res.cloudinary.com/demo/image/upload/sample.jpg - -![Sample](https://res.cloudinary.com/demo/image/upload/w_0.4/sample.jpg "Sample") - -Generating a 150x100 version of the `sample` image and downloading it through a CDN: - - http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg - -![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") - -Converting to a 150x100 PNG with rounded corners of 20 pixels: - - http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png - -![Sample 150x150 Rounded PNG](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png "Sample 150x150 Rounded PNG") - -For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). - -Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: - - http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg - -![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") - -For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. - - -## Usage - -### Configuration - -Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. -Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. -See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. - -Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, -by when initializing the Cloudinary object, or by using the CLOUDINARY_URL meta-data property. - -The entry point of the library is the Cloudinary object. - -Here's an example of setting the configuration parameters programatically: - - Map config = new HashMap(); - config.put("cloud_name", "n07t21i7"); - config.put("api_key", "123456789012345"); - config.put("api_secret", "abcdeghijklmnopqrstuvwxyz12"); - Cloudinary cloudinary = new Cloudinary(config); - -Another example of setting the configuration parameters by providing the CLOUDINARY_URL value to the constructor: - - Cloudinary cloudinary = new Cloudinary("cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7"); - -Giving the context will allow Cloudinary to configure from the application's meta-data. - - Cloudinary cloudinary = new Cloudinary(Utils.cloudinaryUrlFromContext(getContext())); - -Then add a meta-data property to your application section in the AndroidManifest.xml - - - ... - - ... - - - - -### Embedding and transforming images - -Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: - -The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: - - cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg") - -Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: - - cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg") - -You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. - -Embedding a Facebook profile to match your graphic design is very simple: - - cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg") - -Same goes for Twitter: - - cloudinary.url().type("twitter_name").generate("billclinton.jpg") - -### Upload - -Assuming you have your Cloudinary configuration parameters defined (`cloud_name`, `api_key`, `api_secret`), uploading to Cloudinary is very simple. - -The following example uploads a local JPG available as an InputStream to the cloud: - - cloudinary.uploader().upload(inputStream, ObjectUtils.emptyMap()) - -The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: - - cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg") - - http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg - -You can also specify your own public ID: - - cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")) - - cloudinary.url().generate("sample_remote.jpg") - - http://res.cloudinary.com/demo/image/upload/sample_remote.jpg - -### Safe mobile uploading - -Android applications might prefer to avoid keeping the sensitive `api_secret` on the mobile device. It is recommended to generate the upload authentication signature on the server side. -This way the `api_secret` is stored only on the much safer server-side. - -Cloudinary's Android SDK allows providing server-generated signature and any additional parameters that were generated on the server side (instead of signing using `api_secret` locally). - -The following example intializes Cloudinary without any authentication parameters: - - Map config = new HashMap(); - config.put("cloud_name", "n07t21i7"); - Cloudinary mobileCloudinary = new Cloudinary(config); - -Alternatively replace your CLOUDINARY_URL meta-data property as follows: - - - -Your server can use any Cloudinary libraries (Ruby on Rails, PHP, Python & Django, Java, Perl, .Net, etc.) for generating the signature. The following JSON in an example of a response of an upload authorization request to your server: - - { - "signature": "sgjfdoigfjdgfdogidf9g87df98gfdb8f7d6gfdg7gfd8", - "public_id": "abdbasdasda76asd7sa789", - "timestamp": 1346925631, - "api_key": "123456789012345" - } - -The following code uploads an image to Cloudinary with the parameters generated safely on the server side (e.g., from a JSON as in the example above): - - cloudinary.uploader().upload(inputStream, ObjectUtils.asMap("public_id", publicId, "signature", signature, "timestamp", timestamp, "api_key", api_key)) - -You might want to reference uploaded Cloudinary images and raw files using an identifier string of the following format: - - resource_type:type:identifier.format - -The following example generates a Cloudinary URL based on an idenfier of the format mentioned above: - - String imageIdentifier = "image:upload:dfhjghjkdisudgfds7iyf.jpg"; - String[] components = imageIdentifier.split(":"); - - String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); - - // http://res.cloudinary.com/n07t21i7/image/upload/dfhjghjkdisudgfds7iyf.jpg - -Same can work for raw file uploads: - - String rawIdentifier = "raw:upload:cguysfdsfuydsfyuds31.doc"; - String[] components = rawIdentifier.split(":"); - - String url = cloudinary.url().resourceType(components[0]).type(components[1]).generate(components[2]); - - // http://res.cloudinary.com/n07t21i7/raw/upload/cguysfdsfuydsfyuds31.doc - -## Additional resources ########################################################## - -Additional resources are available at: - -* [Website](http://cloudinary.com) -* [Documentation](http://cloudinary.com/documentation) -* [Image transformations documentation](http://cloudinary.com/documentation/image_transformations) -* [Upload API documentation](http://cloudinary.com/documentation/upload_images) - -## Support - -You can [open an issue through GitHub](https://github.com/cloudinary/cloudinary_android/issues). - -Contact us at [support@cloudinary.com](mailto:support@cloudinary.com) - -Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary) - -## License ####################################################################### - -Released under the MIT license. diff --git a/cloudinary-android/build.gradle b/cloudinary-android/build.gradle deleted file mode 100644 index 2353e7d4..00000000 --- a/cloudinary-android/build.gradle +++ /dev/null @@ -1,144 +0,0 @@ -import org.apache.tools.ant.filters.* - -apply plugin: 'com.android.library' - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' - } - -} - -sourceCompatibility = 1.7 -targetCompatibility = 1.7 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -configurations.all { -} - -android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - - defaultConfig { - minSdkVersion 9 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - compile project(':cloudinary-core') - testCompile 'junit:junit:4.12' - testCompile project(':cloudinary-test-common') - androidTestCompile 'com.android.support:support-annotations:25.3.1' - androidTestCompile 'com.android.support.test:runner:0.5' - androidTestCompile 'com.android.support.test:rules:0.5' - androidTestCompile 'org.hamcrest:hamcrest-library:1.3' - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) -} - -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - - pom.project { - groupId publishGroupId - artifactId 'cloudinary-android' - name 'Cloudinary Android Library' - description publishDescription - packaging 'aar' - version version - - url githubUrl - - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl - } - - licenses { - license { - name licenseName - url licenseUrl - } - } - - developers { - developer { - id developerId - name developerName - email developerEmail - } - } - } - - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } - } - } - } - } -} - -task copyFiles(type: Copy) { - from "src/androidTest/template/" - into 'src/androidTest/' - rename { String fileName -> - fileName.replace("AndroidManifest.xml.sample", "AndroidManifest.xml") - } - filter(ReplaceTokens, tokens: ['cloudinary://123456789:abcdefg@hijklmnop': System.getenv('CLOUDINARY_URL')]) -} - - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - android.libraryVariants.all{var -> classpath += var.javaCompiler.classpath} - -} -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} -artifacts { - archives javadocJar - archives sourcesJar -} -preBuild.dependsOn(copyFiles) diff --git a/cloudinary-android/project.properties b/cloudinary-android/project.properties deleted file mode 100644 index 362a0a30..00000000 --- a/cloudinary-android/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-22 -android.library=true diff --git a/cloudinary-android/src/androidTest/assets/docx.docx b/cloudinary-android/src/androidTest/assets/docx.docx deleted file mode 100755 index d179509837c5687f4f30a6c9d9b75b0665c1f1b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20453 zcmeFZ1Cyk|wy53Hwr$&XPuuQk+qP}nwr$(CZB4s-+W30*x%+de)s>M=l@{|G$u`1Z_vXBUW2^C0@nBmk%i=wQ=iNhkFfsKdXP$> ziU@rMy5bDm>82V(o$rUp8tev-9nE@aEo8s{ExLm86gvy56ER^<3VFkc{(GhAx$9lq z8xz_TsN7evLQQdknXM=Y!zq@)%SfjfqofJ68_eVLKct7I9! z#<)RN0ZewctWaB!`gkW-J|j9jL-dn7p7>M_S!X1yONB-$GHWS3RcYJfK1Z_6p|sUL zHJ~cWwGUkY*YQ*|n{9RJ<8YbSBDeKdm^b+d`W*o=k@PIqh()P(GDj~)`mlNGxkpo~ zKd8`E+wXF-L5kl`?=$w#Q5yXF;|l>&^J?IQR$#8~{_;;^Z+yw?8O1-Q$Y^^gU?;lo zmrQR9S+7BbHm?n&K0n~bH>VF|I3jKUDs)T=TnV#5%mnc9PO&}s#7zi#Gng1Ik{ZBi z$bru8LLHvQi5MVFKHiM8hJ28D`*#);syNxbz%ZkI`8lEZv3c=BPTpMfW8KE$YtA;1 z3GGvh*r+{nl)aqBo_qp@P>VY`9d~4-u>-HKm>%RUda-Yk1smx;zkT-W3kX2&|8RNS zILwy&Z#$Ox{ub)n<#p_hEFI`*|Gxf@tN%aj-~Y1o$oLHjAb#lJYmgniQ7^U)NW%HG zZLvHT!rwrFX%|0SU?&ULySobT+vPU2j~`AZWS27nG8VZ?xIt_5)RuBrGjv6FEL^Qx zBEaO-Mm5s?O<+W)Yt5`}mg%_T67#qt87lJk8-n?Xc?pe~Y6~=jM!S{TW3j~{6l3Bh zvHnC>w0Dm@r~sv>6|bwon3-JvY#LEz)metsV#D-?G`?P)2tyeK)3aHl)u*%+AGVJ& zr}#sSX-Pjrk`z_-jMFU9L3Paz{%=6LjDbn|8$xk z-|_Lg_y5~ZWn!Pq0R6Yoh;<4kaY=LO0ySEr{ai;0W;ZEYP-YfSa_!yDwp?0|wpt@e zk#JV}((&fXp7t@q@d~I*QpONJ2o%a~ou>L_cxdqAe*cp&Lh7h;8IrO7hvad??}{_5 zMd&Tr=1NSgHp+41LLtj+@}^GUG0RFUxho-4B8>J~Bi)xE70*AVTAaQfJY0;W-4ud_v-@MdyUvpmYDR;xjdPk(qL-(qFnd(Uy; zwfI1pIKp5bu?2`$0wS}8YLGr-+zzf63- z4eZ}-wO@L3|KS_oXxIS&5Wai-!&d**N-xwWtT)(^x8Uf%ewyO-T#|}h=G$c!N5}w; zG(gGCB3jiYi@}JiIjMX^z4*P3P}rFcl0e z%#5mZE-)hKx4e>T##Hl}rjwPSAx1HVePkyhvXCtNWBmSRQ&W&Pa5+mRMF8J-NDp}t zcSMN-ogr=3F*s7cr>J2&mQt?g&@+}IX`Wy_7_@NFxK4Agf!``!2<(M4$*QUz#@<66 z1s3R6O@hX&T;DfTer8Yy8-Nsxt1FDQSYQrSuMwaGji;8UJ204=Fp1w~h>n%`E~XM1 zkA~uYkc1`<964N!kGtBN=4e*yaF93OE6m1X=8%GfwIg{V%-^9hpVH=;gRKdU%}?nJ zD7jDRzc(1+Nf#Y3eN9{OhPYAHktxoYFKo!VITDyZy@-gXm>lM&YI@fPB808Da}5j^ zFq!F#OLoblVN%ab4_8~sLO(juK?;W`=YJIy0i;i`gWIjcKzq{<6p`-?cNX3_JWGwb zxptoOBRqK4GIMRzylHx)hI64T$-5n265CxOMpNeX!525&v2`> zN>uj`s0WdSOp<8j@v{?B;~)}hW{p!wtGp_CswfV8VG!d*fM3q`t%GIFA51G) z%VG@r^2KM$2L?eVLO1eUy*#+opw)%(M0tpX#{Kx1ur>UJRxg-Kl6#VmREJLXkGyCy z))^;4c%6I*6G+ac_qw(elaI$kZkYE<%QU-M#!o!5OZG%ay25NwzCOVOY<`vMc?BMS z96$!!))%H-Ik%DkYbtTgcBMf~^d8hqx`V=Wn~psJp#%truOJReU zf8v2vb(53}Eh$rY|3LXD=N=vs5@aX_LYuXeo9qZbk`=N zG{q9EzxE%fv#~>@kbl}GnMW}xMwig;JV`-Z1vYPYLZSe!Q&H0=U!IU*iWu4n`0p|NGgft(WbZCk6{5v)%dJWnN($>mvSny ziJIh+8vbH&Z=_|p;&%SAlq#FrFu*oQ8K5GKwJj)HT|+|DUS9E(M0RD7MkdGbD+&? zceM(>E^Jex{>FdSE|==Gg!BmU(4j2$Qh$aB8drb+ zEfOCAwS9qz8t9a+Rv!W_i^kuD1)%l$^U@}a_g>hb=XW9O36_Zs+Eo@$n`*@^_JwLf z)m2b<0ZGtwwX_C2J&bl1@sg~;&8T(9jkK`sE#tvW4kUen&XizxT`0o7m;=GW#8JAt z_B&Y+8H+1d_{Xfl&9N1B0hWI#o~x&%SivlbOGa;X$&Rpp7!iQxm?KO)bC>PB=1J^TmCARKkApJpSz)57IZ9wOvhw|y-E zxo@0Ze6j3Dy0>aip#hfv87N6dmlzd+FhU(qcG|%$hD_vuKOWV728u+O8`9<$HY#uChzdS1xJrK`Ah252UXqRaa!Aqd_UJX(E;5Mq zqxTmn@?h!}kJQ%UP2$9oU&8OHNVVGqn{0dtfenmJ8TXZo=`2MXfOS@b2nV&y1Knqk zF(j0^0NJ=^mLA(x2d>kas@j!5a61P)`dyU+n27QwRr3)|(Cri=IQO+!n!52MJnt&Q zFQJhgRqdRaua={x1tVh7^Uw#4$*5%A zz1Q%Du0NkvAi>wiTrDDHm@$2^L^DIYfgw6PdBWqtqpM2C4Xrn4E;XErR8eKvqN=_b zs$kH6E3WlAJ35d1qf>lnCaQ)>vZId42BSg^CxdJ$#4syHQHr6u7AjUPRx(0~W8qBA zj*|U}jSyw6Uc8j%wp`=}=@^<~qJ9Mw`O4?Yk;nB2XWI4~Rl1 z4`cBNmZMD6Ek_wdD`k?s3`J2|WrA$)VlG$&pz3igse8;$775wZQF8hyzIv(BC2lJ5 z)LZoY&Ohr3ilgICg)Bp&F94(+{)ybrApZ%G3V%u*PJgGn>Tm!6@c%?4V;dVsBYTFw z9DkvrCA&e7?3L5_1^<#ost00)K;bWd04Ioo$JgtWIZRYXvtdTwl^XfoJU{n%jzA)|Qq%YA)W~k^S}c zC^uZ8fSY~FNl6^BU=khUrJEFkrWO}Dk~~k?98?IoTTD@;R0ksIxq~0QHBQN2u8Lq0 z{T^hw)Q{@bR6qf?P9YD{^5JJ`n~tuN#yHN;l+{{{i8M7p=h~hAQ1qm5QJB1}=vW@a zrNH!<$G~%=Is$92JE7S6+|Aa|?tBhgcufUmcR%)->BL{wRlI`TVb9g+b(I_;D@ z2WEEpRzM16U!GQ&A+d8oVeOT^^A3eSvV-)llxZReP;+*!ut6aV8l)h`odIrYX3G&! zQ#a{vUd2KbT%7S;u$P%T&Z^C1ExM=|9n%`J(l#Ko7F!k2lHr~HLMINi$k|7|rH~=n zU2&$syaZj8fIi~12FSn(cpiJt=uL`>rfu7Y5Yl#O8K2{hR^tS+NpG!;6PbXAfF}&; z;CiJ3-5MM+o3KRG5G|IMq+tEO3a<^A9f86iVqq^+Lp1@&G%@uEJacz+-w9~~_3tMa ze<)4;lm=$QT&^R9UgS}L7PL^7cOLxn>H@e_jfTVLwWEIp%97iSr-3cnlnn$)8(uUo z6aZ753s-J(?Qw4F9vNlCFO*OYn4;e4$hLxoqx=a0%T9C0H&nh}GAk!Q?Z zzeh6J0klT>)Tukgp8Dh|c4c8V)R!z{q(wpX*%lF@+d@HuVu?b_twCu~*&rlVyF9%G zUPs@yt$p}%$C30EE}9~VP`gvcMmGeX+?!ms<>J2lwL20^GsC)Q))dlNNnI80y3fIU zmX4LTS7$^<&hkbz?^Km{<#u6~wt@BA4sUtKnvUreN|4WR!Onk74@~`vkov!q)AVo8 zK>SzRxBfPNhriZen7Ad=Pmk<-E$tan^fWt-UH}zP3Vcw-2PfAN%~E~BY&CLtc?pVj z3NGKeCcEN&!o|atHQDWmQB~*!NkDR&8Ykx8E7iwHa%~eaM-!HL5_^ltUu;N&lO|0^ zN9q($?+9*Xcf}wmxbsS0kq6uqltNq{noxDdq zi@MfsI1+V0u(n{VL>tueN5**RuUF--8R!iLW0q9&`0gP+wz^gnAUBglKrvc3XI_!VWgeG2jm{&5C(8AxBK36lcpiZj3>X&p1PS$ zH#FU`vt&%UP^*i*u3*apFai6hO4obJ2%c|Z^`O0|Z}nAAinA5l zWqqkf9&+o9SImL6^IO@3DNU6kI7L`Vq!1#oF{Cnun}?Q6wfG@-NMs5)BMH{o=;iyn zr7{^J<6nit|DOuaF#m_bmHvN)gG8utF`))I(ms3a)*eSYvhx(@#ld?XnX)`thdT?i zwBRMB0k`jIcU367Nb5FK;092XQ4?q7j^URwh&TJU%i8FP%46SjG->9RHUjUs7b++7 z1Nl=Wt_9udsDsy?l2pZj)GZtNRI}b5jqbqT+1S_CcEXme{t#^iN{O?Ia%}4gA?@j? z{3IT}P^H%r!5qxpV?*qIyGSyv*6n@2Hh-jJQyQeWew))b0_70N67lel@moSZ#!IZL zXuf+CbnpR;lz1xI6^R#%I5Yz>eeu9RUb;0}>=a|%Vfm}@;Xr9Jn3l}~5;CKAS<^lL(ZxT%Ed1ZT6@Kuo@c$j&OpWvm|5tdcOx*b1x&Pjo0lUC~ z-Xd#`U`CJR6rQWJpaTWAdIE^798`A#BpI35h-o$;%ZkVk*;0it@_rXj?`(Z9_|=GX zTExO3SppFexYrzOoY9~Y!W-4b>C#%=5VL=BvcDG_^w71W4lYVm}}6v2t0C**U?Nmy>K-G@};EP7-M19`z4 z#fS}6Al_fr0*+w()kX1ovKNNi2A0#5%xnC!hVYB6hFQ4-a~`|o5or6)*^(1fG*YHS z!{o?jwZkd%I1A6@Q3})^;vFmTt7c0*Djklj;cSKP$+*-A=H^2P?EPJGk@*huK4wdrY| zA@zpaGTUb6pIW|ir@V=~z!A(dSIxtOl_(VD3u=p=ahCiZ{lZQnZ$+&$YVpJ;_ty;$ z%Nfed_CyB?mbnUgo(-G~O@}DWBZ8|`{G&LfnOl(L!1b+dFH;^=C2E&dt8IHxEPV@5 zyKRNZnt8zKWa^eG!#STPO8HmyLDgp%iV`qEGA_As1{p^UiI@mq44PR%Lsmt7LQnl0 zkY-*Ye0@r}m?U`%OJ(LcjrnLk-{$b|zYO$Sf)u6SzZr#C001cej0KLSMpj02|9Jf` z7*dn9* z)ha|!!w+?}=#6=e(-LR4rdCuTen?e2o^>_6ikBGi<>2iL7q6^hTO_S7e(B5Q`FN=NBGJwA$@;cFTxeOjfg~vm6YT{ zxB*q|V+wIy>q+5Al+JKl3ixO6(_u@qHf0U1a0Y4a#9j2!ZOoW5*;%=S2Uh!i9?+Vw zc=uit{akS*lb}9OFsG99R1}-U)kQ6;KS}a_yL7Jp^b{PoR&sACx{x8h*b63 z=EiCp@3nSIe%o40*Kk`U`l=_aDk=>SFCblW+?C&d+PmEOX8>xi1B#WC8K*>is zl7BwhWjnVlSF&h&hJB7_PFBg3ELa-VtLB3m;$b;FH?5l+IIQ(y#SXEZ+(b?;ms@299{r;0`M8)F!7Jd)_P)QHLtAoH8~%D7AnSR5 zcVmH{HzA#lpv!rGycmn9;r;mN5JkC>_7gRFjvW@t&o~6Xyhlf7Qnn)@BeLs88313n z3!frRWbaFt1o_Ag$9|ZiFivy9=HMJ`+Qs>CQ~>GfWj}-93Lh4hkCN$P2xpd<(ts}(3_c(I((V_?&XINSsml$J3~Kuu zEJ<{SYz=6O*=Z;<6g&?T0!nwMOe<1P00owTz61=iQ;G5j^Giqn6uFZ1#NIw6TjDsH z4qtWbn-$GkMy@+OS*wzhe_ z#xRJQUP8Z^Lv10YJ+>Bv1k&PQNhI0=2l6qh3#)EtpQ$_+Ezbo*tFRF(v~XAVPS}tv?Ri$^?(F zgVacFiRz>F8zs~P!n`RatQo#cW>jf1utA*L{?WVSEvZ!mQy^GEO^$a0`;$b|B{e)(XV+RrV?(NKh(ogdE-JNe&2DlY+~i2P#58*h)f!TPAi#|(46Oav zxOE~|4<+dlomi4R`qzxo+#AFuZzb?L5U(Z-RHS2ZBM+_8U>;-Cnc zhv`+Bg(x-&2tL3b`6xY&dz?>UoJODWnhBp>^TPO7e=6zJM}f_Q%)>_K+SLcJrZQ7l ztXDeCD^IZfhPrZu>L!byHa!+)U>ZE!sJuf3@MeyUv&B849Y3;?hS z^)JxtVC3j%W^MA1ET>Uik3EXi%XP>9G> zWJ83@u;}j0ua}V%(xpLk_yr(ec+R6E!gWl=qJlWumOvseLMlSvw&ZXkAkiiayTa^q zb5#j#0zs(p(zTdjJMI{D^tMoQl6h49QM0nbWDA2|3T@g8@r^mTdEQ6bHEt9W=}3B; zQAEP%tN20N^AcdppE`5BLskH5UWbFx4frd>x1#Y?9Z^cp!Urmo4UkFkBy*UEgJ~`) zM3maP@n=}MDRMbKOGQ7g;(`GG5Ig&19i?~8U5_=-?7OIw7kZ}W0aT)NO4YClP%6=r zmy@Q6oXE-Z6&(hUR)ta$h|5LWVMkE|Wyby9#257Kk`EQcpvjL%Anx>K^$@v=!%wSd ziewZ-9=(EJo6!DUfB4{Qcr`(%$r zWT6Y?EXMr3uxt)_1`!LzLj!LOy-HX^YPn z5W&{{2o;;=2V0FC#9$J#h&U=hZD*1(2$LYc_*-O65wifMeT}7GAQHJ;XK3(Po}8q! zawMl&jU@1u@A?43GyzL5Hu-CqvDK|lTTV0pF}{FYWewSO)Zk#UM%8Hst9uJfm4AiF zDJmW_n44!@pjkCJDeZ4sVGLlV627sZ-ksb-ib=T?-<{fBBB0RPKh9tcUwS%mk@$*& z>z^ePnns3^%4|v$Wz`_TpC4NjxkBxZ{pN$~$4j5@zU5G6p$AKb-bt07)YL5pgAFfB zj>{8jqz$qOywmL2(hU{7)5O`@)E;xi_ja9}mkVje2hVr)F%GCB2i{Q0V`tChEB;4n z@mhvnrq?6*Tp1)4MmaLg=~KoA6MkYXw?}1Kxy?i$IW+4(o#N_bV4$sZBKO)2IjwF>>31mL9JSEgW)W*ib?VIhuARpLquXzo%luYQs5yV~ z>m!**hwd3z3+FkfjbB1VZswNz>p>C@k*WzrS~m|zIRTMD(=oV^vmPX~HA|cC8oFKi zO8mwUYvEJw{qk3OTbLvDV?8synZB`Kh-QUqR1?hr!|VjkaD++_WT4E}{rCQo3C3u7ugirg?t%Ub?o&T7-`E677-9Z2VTyX;cVEz-y9UR>(jT}_W z98HCdjg7u{6aQ}0ENX1nZ?GeJ?Ua9jC2!;W-d_zfPo&rgBdZ>F<*rkFgO-mSViC7> ztJ{BhHDmq>ub%)D{|a%_gM z5-~fO{lIS8Ks7)tLOPMf96KD8()yf)r!CP{V&8p}H6t_jC30NQ^M=Dj!&VqbY?m{# zPC-Dt@&f;3!?h`j9it<04mS?o7)CI;mdm6oK2W&Kk7JU#+0GA+F6?^8Uf#%5>_$k916F%&8U?_&$) zAtM5xZ5#8)Ja=#1vftEg(IQpAI=V>+hY&3-5ooyVl5!Q9^ z4;BSD6BGu?yZ{$S5oko&xF4+>S%aGDB2+((U*@)6z=31r16~*AiJ9EX|L4eLlqs!Tw>^=B{yqxXm%Bdet0Xx~Ot?=#Yk)0t z>=jZR_?d0{CklgvIx2RT3(oABZTX|%E;7F%iHX)w{7tIK*YhN7af54RPj8|n zPlA~~?r58^?j9yO5^Wkv@rH~-QO6tcjOc4#2{eJEiev=ZbVq&$!wQj$RHenUd3 zqvWxu66G}`FbQRni&~yAgP>CQKBExS5PVX;>vyMrYY0i9ctYQO!es{-GfI*BQSw3i zNhyPDhfK)i?RreF0l;{LSDGTnJD~Z)B#yL{P30%QhiZkM6JQW3BHtD&g8%PLTZm*I z4B@-a{zC2K-=Ts^{c6AGuX(#BuEZ_x{X@R2a{@lGRB78N2z3Zro+5k#o(B2vJpV4{ zzc*neLT~u*K2*Fg6PEc3;3}b*;QyFsPL|M+3Ns!4_c}5B5tIIkFF%#wTPzs)LHeN* z^8bg({wcoytLPNF)g#<=dww)vaNhF$xd=#y^+_v2`;%4U1`DH*nRx^oQa+>qR(F%; zJ?nN61Rvw)=3tg#7CeJB@H5{C8-hFEd)s51XP{-~;mFmeqxS^XeJl?P<~UvIwikH0FyvplQ7xpl3q>s*>tZk9R_pAJAnsJs5M0==~cV+Z|Eq$3inA@Zf~F0BcT$HG984n6Pp(b=cn%l*7WMp6#vP z!$D9Vm|e;yipjcU2K?_tWE!vwPg-!8g-g0-`5Uys&M^N7fH}8`b;Sp;_mG29eAd&7D#3w|mTX5+%1q3l-ub zr^^3;9q3$ssOp>XeoCsRTBwWmaNALX1H#U4=XNFy3h#*grS33AV4W2Pl$Qlr^X6)40%S1W9oSa7pSmD!yHI_-Cj>ixR%yuiDZ^9B)ZQlf z+mF)~RD(dSXX=rHf1;pBxVA@#^G|8Qxy5e;Wstz_tWDu&m&DOH_XaA1&FL`L1p zFb${sBD)AI_T9wgfdr$se54G)4nZWF582@(3*r1i(#3=3*SjjAm3 zBbSHkX9T(lKqN~rG-$oPU=Ny7tfxac?Sl$q#DPqzg%Aw-)gzVj5uR-!qcU}c>1Cp< z8Wi~Y2ex=TnzKewkT7PpxhR7aNP@$Op z@{<2NAm?5tFM?Z7FKt+G=8;qBSUlsxx=;x&oPJ%D;{--tTN|}H`GU1Lay*h7H$(QS zedgTYdJ~h%D*EZqDypv)0wNeQ*e-aADg)+$=mFXSC^mfKyObssEHbw=@{}+Eg#(Y? zF@>acduH<=Nm0{Dl->H96hAls0KU($|EtdLzew?a;~^`&*R|A_-@ud5%4%4##EA3$ zFjLuYk?DstLV^eu@zPEqyPoa{5O97?L_)*1g>F^EFrHrRX^#*x4_Tq}Kuc zDi!R8c&@5N)Kv1`u?@iRZpxJh(((mXZ@E@(xK{SK_4xor3qt!v{F!E{bBO>&cB%LN zHRCI}lRv9X@pFHAG7s9A?R%K9cestZ;!-2gZ#y&p*$Frz#P!y#$dwouc%Qts|D*WP zA7hc76x5OhB8)!b@Wm_Bx7uR)+iTAZz+z)sRW1=t7`t&>Q^!!aLseDI&o7k`q8`lm z1C`hz$Ec>8_K|EZCD{&ml>gJ*ZZBXLfTffVMw(Tfj-xz}l^uGsB~~6-M6Y6o+tY9^ zKM>0Sj}U>*yQ^)&AX~8TqMAk((*&^W|UFHr`z1nKHHtKNN*D`l|nija#xT0a@2d743Pa;^(0nIrG zaXO<}PNhz#HxZn;P`Go@%tbxEFlp{29`YVL2%ThYo_wT=*r%*Qqik5;&P@31Pu+bC zEC~q;%BNZAKBEvn;pU>Is9a<`ag!D2JlV*OPn~_DHjEg6tM$2C2i)64ZGoLZ#LEp&)p(Chx@BDf>`0sPkNxtW{9V-3XIZ}OSqof9PJ$0I;#=@t_KGE~it*{B z`P1-fgoTrh&wR5~%Rg9ZqjEMkm6TGr4kO<$|9hU9-;4RT_RUf;-xyjqRAQVat^}zE zB@d(bcb@wVNu|%>#~6RtDJYk(@K=1u3*Ta46=arU{zkcf71=-E|KCLyu3`akKgp&` zDkyqYlC33o@JZLOnBQHCGLfef7}v1*DfdUsk`0x2;fXV*>+pSU!G>0vN&3dE08gw_ zN{Mw7k+5_QlTltImN z$1oKmM3amA79m;uZ30B92=3%5%JKuzFH|qyyO2fA&o0WF`?#ZZaHOO@$jh8E ziR)sY!%~x1lg%@{rr?BE6UIz!P4)MLUuBWsWLEI;m&_Kw$!vH*rbLh5ji3YrR^dJa z7PDX>`z%+yBtR+3y*OaqRDcLCU~ed@HNu1FpJYY~HAdUhOkH@l-J>t$xEg^mN3S;1 z3bi5^&>w%4f9jZszr<6zgAk!9N~i#H_dpI8qNlyll^L9~bwwpCSZsvzwBOx?>&`CZ z>~;7gl?tidZ%M|QiHp>fgX%$lc%VuSNz$*X+YzUm=rf^8?3%4)A z(b*gbw-+_`vT4nmcmlm@{RbJJo^bLo6ZQ^>J&LtnmasMp$_Hsp&6-#~yju6wKeSH1 z6yH2zJX=yhIQNyeJ*_wQL1Ed89&8QQ@=gbjXCG$=HNM7LxqlHunhWq)m(yjZ?cNbA z!SO;T^&x)s{owV>f9@qwe>sHJ9oTy@)cQBhCO9T&s>D-hwjwSl zm-G%5(1n}{gfC)Xux3QC66;^I5J{8l?HXctUi6zOKUtIDv|ypPjYgh0wf5M1&;`(D zCN~bL5`4IL0JPY3o`5v;YU{cjkO%T zs?2h11&*M6SX9}DE~c2lLp4ZZQ!nCWXVuv!u}Yr6t6j%hqB@)1WUw)G+*Dk);WwY;l zb^mik=o?uVmHYk{qIAKn@}YOPL|-mi0SXVz>QCrt01aBj1dvjN(Q6^sepQd~PDchHiGVdmx zu0mdJ;xnQ{;K5BftrVU>Z$_Ukorc|GDSJozf9Ch|dZ206eit2e27MCKT*0tsnr)GJ zK!%i8Z~6Uj0j@J#y1M^#?c2h>dXIuG8Cdx7zFyH!3p(=gSswWHAM>m_Kp-QqZ)a}! zE|mF~f=O#fB|UvhqrdLlnW$y6PLCXXO?JsA)JBz>zX@8n`a5N-moS-NanHW=#5)=q#Sw5RcCu)_^>S#p9>pl=C^Hh=B z4eb-(;I1S$PQZkv~V-%pk zyF)pj*PXYapR>dwkx3mRi4ot2T~jnk1-@Y8WLI4~RjXu#l1rsb#22Z^2wl4DP${4j^G{l%z#NdJk4oh)?{y1$Pbwn6L|W*YA|^Tmz8 z?42H3=+-tS&p`qhGw*EjL4e?5RUb^IKb9zjOc$%#f;eVm^OR=c(vDdz@%P&hj*u&|avu?k*G4+-I~r(KUT zaX%np3Pe|$1^Dh%ZVQe&ynLGKX*PFW_Uh>f z5aw+7Pc_)XePd+S09tAIap}WzlE_ZW96s>dj zIZ@0siUyaR=G{7eD#>i8lzE2180WPCDPp5%*B#Ws+cDmx)p`AQR5ns_2lKdz(H)np2};ypwDJI&+ld~hIFK6L^PcYy?PVZ4IQtJ zUXmcp3dNYdXAGCJTCcS*hTeH#v$jfh%i!@DkyLMYLuninup>z50UN}Qs zL{b~j;(Bceaj_IF#v4c}G5*|=W5irGj%?WIC0U#4lRlfK!Ypwrwp6k%*WQ1LhBeYB z?jKw`M)WbG<70yQJahMAja+X;#Qc3vvX|1>a7~W1^KvT)P8gRO1gW?2ruR{|NOeC6 zLXuei2P(*T(cV5iIRpWZ8X*=n>^yQ_pJtmrNq5&?wFXwZE_>6Yt+=_FZtL1?YK00m77h!VvMy)!JO-#{$4uXX#%m|x2Zn>;^>#Su zkJ)50G`Sj)v{csepyo)jACV4C#FbGP%5Y!ygmX9xCwhCQT9A-2^2VUi7rccMnr|<6BD!H}Z5!_wl+d z+&y88vV-$J>SUa;QkYapnL31Q1s%_35kk3J5a{Hk=64=T~wicTps zxwBs)8gTmqTUIK-)4axAv#myk)0jsXnwcJ0LWomcMYxfyRjMD9P|giw!Bs}6(u{m+ zt34EvnP{auU7>nbh$-9CF2nhADJ&8CWT|6RTn)!ZyQ@K_yd^(hIn5Zba6>Z+@S}8E2;EGS4Chv4m~NjIo-eK z4<0JjSdRq@`(U8gn|4!he}51*ru4*Pp^|jPv{g$Wi662y=AwoHfmNI5<*}xjp9elJ zOt1K6;FGgLhmQd986;@(b1iZQ84FO6OyrrX z=zpRKPceYctoYt^CG&{l;};NsCpCGbcrc0rY|%36Bu|f(58t^^dWLC>l`i2bx4vp zR+^2KNWMLD)35b0q(nQvN@%BGOWU8BrfG|)>{0%z1uzyx^M_lm@E5MMO$MZv(teD= zm;KMtWogjA4gR~P_itT4@;^6!U5xY<|E0nya(2obkO3a-#v|{#_ON;k6+uliNFgxW z3J*UcODjN}R7#|P*Q=m8Xg;FFbMig)g>HrHx|~R^9$j5tHmMXJ{lW@ept};2j6P+tiUn!9)6E z2s^`Rf$@f#$qThkVtRy4Fs{5UdZiy5GZ>to%}0DM3WDJJTB>*BduVdptC%T%QGJYy z%Zvq|mB zl1T_L?HsiQ@J<$B_~sN!HqlGQh%CzwnQf7i z-@;O7E0>UgEO1yrbEk>~*a1@CNsJ#$rMO;ZPD9jL!o;tyG9`kgM8z$>RHz|HuVHEZ z7>hsj7qd-N6cvY$d?*bC6Q33l|FAxn@nr&J5tEyaXOhRp88zg_x7s#pMM|M%Tft>(Z#rq>s*m~4DZz>U7b(vzxSLd z|MB%>~ zCl5Z{@2YNGRb2CDdgy&%C_@^Mj7++~?2P>|E^uTZ5m=7q3h+iX0QG1eB)trunHd<6 z4*EgYhfCs+QPtZ+5KaB!m)?wh_2*%kI=-SbDY9h2h1NOzh+L88cqU%QA z>xR(%2)Nw=t{c7)3SB??&MAccE5JeptRHzF6}ooxeK-j1Mqg(4fzbU9 zxLpHmIr>%&bR*Cg!6J+}sRT6wc}Xm~cJ#Gq2XSE@UIiOw< zx@Od|G=x?L1~+R42K13NblvEkcZ7and#KfEz!UD^U3ql<=)E|EelJI;ew02Px_^#Qn`Tm~o@Ar9r&mMRl5eq-7 zRXDcG%|;Qsh@1p1I+Z>}ggM*civ4pAhGEpz!ng=r^W)NYCX|Vt2;~xrJM!b4W3HS< z3Au}3idV0HE3DqgY<&$Rv#(#y_*=}ZjT(L$81a((0|URDZwfwghT?o*j+2eRP#!KO zXUW{g0ZijEnKtZHPfe%txBa9`)C8$;>hwW_?A zOb6mvM=QCr(EI=PJl;G-?z4FfA6Y>Z57d~91K+WVF|vX|sQ=$jPN1%F*UYNC2Tn6T zKrWA7?7G@Xr2~6O8jWufRbLjih*@N_6U=q4ltEzq0kV6#Dpq|qb^fRfI)%|AH-opw$ y;T9U8=l|7uzkj^`y#FFWi~b^*)h%*-m~Th7NGK?hUlJ)j5&3yUWZPBAsN7$TB`?AN diff --git a/cloudinary-android/src/androidTest/assets/images/old_logo.png b/cloudinary-android/src/androidTest/assets/images/old_logo.png deleted file mode 100644 index 10d58c4b80281d2bacb3e8c5439111a242c43972..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3381 zcmV-54a)L~P)nAibqOU||W@$8x`b=@e-#2UnyM{SU-6uG->Y0%@+w zauXm$0^DYv_T%n&G`~2cWSu<^AS{dIa5OXDd*6HWX1K-fL4JPpw+{c)Rf-u%v1Axc;&viopW}xA>yxd;sz92zXY>7G0$olqI_z1GdN$AEP0mSzYU9069pI`v5Tq@CpAjw1D=Uy^lI)hH_&5XCKNSGL+X10k0qd#LWSifH&8iCZJ7x z0Efu-vBpA^72gTLt{?%#&4Bn;0ki_Nch*4pV}MzfgqJ@ET^b~SxE{dBTAnGW^Ffoz z8nE^k3W~iGl0t$!Hfef_=~w~aZqvX59q+Qc!7!$=&_RL(5Ib7Oo+(%8M=S=+I-y&GJSJ%@u*EIl zJABMJuy)q@Blxq4#^09DXYc2rWg$O03)4_R9-2H^L79_#0Pqngd<4H-72rC?^<=d# zLzY*NM{kL-GN~ZiEF^f)|J!QE*kf+<$F+gX2i+KhGIDI6Q+eQ`oVkMW>^!6a41mfx_6h%u z1Dq!Xb}hAK#?S8t*gAl?;dQ`7Ri_P6=(&%j2czgSdqx+ns_J?S$Ov`wncgDhG2nwU z6Uuw1xbA>oGkZWF9xM7I=f80W=ZCPuvh0d&6+|&l>X-iB+$6$i!K#X<3 zeM=CCKt$PCiA-U?|K@r0_s@?gLOsKZA_5IfIURut`wD6737+fSi2(C1euws1u-vY4 z(Ez|Ajn#R~GDG~}&Ue8lHh>uEibI3;BY$yV$;3Jn2O|Lv7Lb5*SP>ke+%!3_3k8!{ zl!l8H?lXO~zJLNFUzQlkHrR_!3H9Lz2H}D@!7kdc3~C*OmRS}t+2f>6z=vDmaT!43 zpKqSMd_5H{AeDh&lMm>UrhhmAnb(JDm`9(Vb4eXNHYq*Pcf+7eI8Zat??^)@y(|qc zw+!Tog?GeVf^43xsxB^m|!vDE_ieN!d5T_jj(>)T{i>i}v4w71jk2TmjqztrVrpft+r# z27|AC&D6Vv7z1*c9=Xt*?m7TLwfo67b)F`$HR0#R8aANY@kH#$k*ZFb-=PemY3fbo zP89>qYO3od9KB7$9u-aQ96|4Z@s=-3?yH6DTq@wk{ z(nO%oMLL@AI*tH5QCMKzShkgQ=2_qJ{>aftsJ@F@niIL>+^+4d4jtwgb>*NP3WG9Y z`px!MA*U4^o*4pKm0Sp)Of1kf6J0Fns!E2S#f^(Ypl_4ht5~w2eM`MlL%bq(m?C5b zrLUU*?>UaSSxYlett*klK$cDW^J|9|0LKcjcVL+-$-iFzvBQr!Kb#vCS!G2<5bHp> zj(N;ugD+0W5M5+8>0E`Q71c}Uf>2i4HvHLb&7f^kNP+FbzwR0onvHQJ@J+iocHncf z8e=6Fl)?xM?9!mD%g-H6eJ@+8=$kDXj6hbE{j5&-J+TJBZ9{;l&(HTO0Jx+!F{?iB znrf)dMhQBzqX~SaGG%A#skB}V%b}}Rq_Gqfj2cNw&NS6l8^BOxnZR@=4s2>Fft>)R zic?(Gs2jOZqEaXo!ptG=qH0@;$j@3;>7XHyW%ZSCuL$6X*;gZ-RS0y6^uEtQU1gc# z4f|8&F1PydU4N_fuSR7@BhJx-WytEUuP{Qg?*$)YB>GClxJm-PeoI7+ZdyY0ZM*ZtVRdaPA=5Hr~(#4nW;zn z?=|Qv?`@Ex>1pa&`l=L&DferHXQiG^Zpc+Jk|uVY+?#@y>Os5VuKeC-L^fj_ zX9ntDxFqM}8WjMKWvdH?(boi7a)aV%qwbMlspg&vCKl5@sZ9L8u`YnFKhPelepQgm z9QvJc_XMfi>*)GB ztHc`4HG_$Af>ZSAK69J)xzZ@ZMcsLMRq3EgN%o-#%3xr`HeX1QT?lQ}VfD7Fs?vtT zDnM@`TS*+yJxk8;y$S+Zbr#Ss1-g(werL6e%Ns0UjSpD;NNXam1hPDLZ1C4(3^pBr zQz=iovFs_7n;P0Z-cSI~43vM)J_aF8b&nfr!_4C9EX`uHG!FG9zt8B9ecUzDM>2pe zHu!C$ij8hc?Q;R}z_H$<_&hU%(h=*QS6~4yfIo1oS5eVVuXC-j4D?}A;U3kqzx4RM zPIOt4rTOHRKnul(Z0svSISn51qstsu3n^rAKR;%$!m$MCsl4rw3y_6eW!J%#mVpxu zO_v8is$c2qOTG?L?BB5PIZhUNRTzkF*8TF zwLbL{h_TIVS8(1-AP*lxOa0;7-;@9*`b?YQb;HZlf^2}8rmu)0PMtw4fSG*GU0ABc zo_@p_pcK^hWWE`9-DSZiopUu{Zs`Vi*6^QWh;Sp2i^ zd@8!w#uho$Z#?cVKK=TK#ruzuwdJ!wJ9+y2)gX+SARFix)0o~Dtnwm7*|tR0>m}pg z9{;xY>nH!X@VGStkP8mvG4qQqCmhHF09h4=cM(dv3_fs>2O(R&H)7AM6AuvOptI>0 zFW!>oaeWo2C={hT|MBy$vLAo(Rn_+=mqJwfvYc)T66B{U{}*5YYcwb29g!=G00000 LNkvXXu0mjfH5q86 diff --git a/cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java b/cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java deleted file mode 100644 index 2ec636c7..00000000 --- a/cloudinary-android/src/androidTest/java/com/cloudinary/android/test/UploaderTest.java +++ /dev/null @@ -1,504 +0,0 @@ -package com.cloudinary.android.test; - -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.util.Log; -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.ProgressCallback; -import com.cloudinary.Transformation; -import com.cloudinary.android.Utils; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.Rectangle; -import org.cloudinary.json.JSONArray; -import org.cloudinary.json.JSONObject; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static junit.framework.Assert.*; - -@RunWith(AndroidJUnit4.class) -public class UploaderTest { - - - public static final String TEST_IMAGE = "images/old_logo.png"; - public static final String TEST_PRESET = "cloudinary_java_test"; - private static Cloudinary cloudinary; - - @BeforeClass - public static void setUp() throws Exception { - String url = Utils.cloudinaryUrlFromContext(InstrumentationRegistry.getContext()); - cloudinary = new Cloudinary(url); - - if (cloudinary.config.apiSecret == null) { - Log.e("UploaderTest", "Please CLOUDINARY_URL in AndroidManifest for Upload test to run"); - } - } - - - protected InputStream getAssetStream(String filename) throws IOException { - return InstrumentationRegistry.getContext().getAssets().open(filename); - } - - private long getAssetFileSize(String filename) { - try { - return InstrumentationRegistry.getContext().getAssets().openFd(filename).getLength(); - } catch (IOException e) { - return -1; - } - } - - private File getLargeFile() throws IOException { - File temp = File.createTempFile("cldupload.test.", ""); - FileOutputStream out = new FileOutputStream(temp); - int[] header = new int[]{0x42, 0x4D, 0x4A, 0xB9, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB8, 0x59, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xB8, 0x1E, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF5, 0x28, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - byte[] byteHeader = new byte[138]; - for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; - byte[] piece = new byte[10]; - Arrays.fill(piece, (byte) 0xff); - out.write(byteHeader); - for (int i = 1; i <= 588000; i++) { - out.write(piece); - } - out.close(); - assertEquals(5880138, temp.length()); - return temp; - } - - @Test - public void testUpload() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true))); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - assertNotNull(result.get("colors")); - assertNotNull(result.get("predominant")); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadProgressCallback() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - - final long totalLength = getAssetFileSize(TEST_IMAGE); - final long[] totalUploaded = new long[]{0}; - - ProgressCallback progressCallback = new ProgressCallback() { - @Override - public void onProgress(long bytesUploaded, long totalBytes) { - totalUploaded[0] += bytesUploaded; - } - }; - - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("colors", true), progressCallback)); - - assertTrue("ProgressCallback was never called", totalUploaded[0] > 0); - assertEquals("ProgressCallback calls do not sum up to actual file length", totalLength, totalUploaded[0]); - - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - assertNotNull(result.get("colors")); - assertNotNull(result.get("predominant")); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUnsignedUpload() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().unsignedUpload(getAssetStream(TEST_IMAGE), TEST_PRESET, - ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - Log.d("TestRunner", cloudinary.config.apiSecret); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadUrl() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload("http://cloudinary.com/images/old_logo.png", ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadDataUri() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject( - cloudinary - .uploader() - .upload("data:image/png;base64,iVBORw0KGgoAA\nAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l\nEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\nP9/AFGGFyjOXZtQAAAAAElFTkSuQmCC", - ObjectUtils.emptyMap())); - assertEquals(result.getLong("width"), 16L); - assertEquals(result.getLong("height"), 16L); - Map to_sign = new HashMap(); - to_sign.put("public_id", (String) result.get("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testUploadExternalSignature() throws Exception { - String apiSecret = cloudinary.config.apiSecret; - if (apiSecret == null) - return; - Map config = new HashMap(); - config.put("api_key", cloudinary.config.apiKey); - config.put("cloud_name", cloudinary.config.cloudName); - - Map params = new HashMap(); - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary.apiSignRequest(params, apiSecret)); - Cloudinary emptyCloudinary = new Cloudinary(config); - JSONObject result = new JSONObject(emptyCloudinary.uploader().upload(getAssetStream(TEST_IMAGE), params)); - assertEquals(result.getLong("width"), 241L); - assertEquals(result.getLong("height"), 51L); - Map to_sign = new HashMap(); - to_sign.put("public_id", result.getString("public_id")); - to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, apiSecret); - assertEquals(result.get("signature"), expected_signature); - } - - @Test - public void testRename() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.emptyMap())); - - cloudinary.uploader().rename(result.getString("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); - - JSONObject result2 = new JSONObject(cloudinary.uploader().upload(getAssetStream("images/favicon.ico"), ObjectUtils.emptyMap())); - boolean error_found = false; - try { - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.emptyMap()); - } catch (Exception e) { - error_found = true; - } - assertTrue(error_found); - cloudinary.uploader().rename((String) result2.get("public_id"), result.get("public_id") + "2", ObjectUtils.asMap("overwrite", Boolean.TRUE)); - } - - @Test - public void testExplicit() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().explicit("sample", - ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload"))); - String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg") - .version(result.get("version")).generate("sample"); - assertEquals(url, result.getJSONArray("eager").getJSONObject(0).get("url")); - } - - @Test - public void testEager() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), - ObjectUtils.asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)))); - } - - @Test - public void testUploadAsync() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), - ObjectUtils.asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true))); - assertEquals(result.getString("status"), "pending"); - } - - @Test - public void testHeaders() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", new String[]{"Link: 1"})); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("headers", ObjectUtils.asMap("Link", "1"))); - } - - @Test - public void testText() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().text("hello world", ObjectUtils.emptyMap())); - assertTrue(result.getInt("width") > 1); - assertTrue(result.getInt("height") > 1); - } - - @Test - public void testSprite() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_1")); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", sprite_test_tag, "public_id", "sprite_test_tag_2")); - JSONObject result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.emptyMap())); - assertEquals(2, result.getJSONObject("image_infos").length()); - result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, ObjectUtils.asMap("transformation", "w_100"))); - assertTrue((result.getString("css_url")).contains("w_100")); - result = new JSONObject(cloudinary.uploader().generateSprite(sprite_test_tag, - ObjectUtils.asMap("transformation", new Transformation().width(100), "format", "jpg"))); - assertTrue((result.getString("css_url")).contains("f_jpg,w_100")); - } - - @Test - public void testMulti() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_1")); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("tags", "multi_test_tag", "public_id", "multi_test_tag_2")); - JSONObject result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.emptyMap())); - assertTrue((result.getString("url")).endsWith(".gif")); - result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", "w_100"))); - assertTrue((result.getString("url")).contains("w_100")); - result = new JSONObject(cloudinary.uploader().multi("multi_test_tag", ObjectUtils.asMap("transformation", new Transformation().width(111), "format", "pdf"))); - assertTrue((result.getString("url")).contains("w_111")); - assertTrue((result.getString("url")).endsWith(".pdf")); - } - - @Test - public void testUniqueFilename() throws Exception { - if (cloudinary.config.apiSecret == null) - return; - - File f = new File(InstrumentationRegistry.getContext().getCacheDir() + "/old_logo.png"); - - InputStream is = getAssetStream(TEST_IMAGE); - int size = is.available(); - byte[] buffer = new byte[size]; - is.read(buffer); - is.close(); - - FileOutputStream fos = new FileOutputStream(f); - fos.write(buffer); - fos.close(); - - JSONObject result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true))); - assertTrue(result.getString("public_id").matches("old_logo_[a-z0-9]{6}")); - result = new JSONObject(cloudinary.uploader().upload(f, ObjectUtils.asMap("use_filename", true, "unique_filename", false))); - assertEquals(result.getString("public_id"), "old_logo"); - } - - @Test - public void testFaceCoordinates() throws Exception { - // should allow sending face coordinates - if (cloudinary.config.apiSecret == null) - return; - Coordinates coordinates = new Coordinates(); - Rectangle rect1 = new Rectangle(121, 31, 110, 51); - Rectangle rect2 = new Rectangle(120, 30, 109, 51); - coordinates.addRect(rect1); - coordinates.addRect(rect2); - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("face_coordinates", coordinates, "faces", true))); - JSONArray resultFaces = result.getJSONArray("faces"); - assertEquals(2, resultFaces.length()); - - JSONArray resultCoordinates = resultFaces.getJSONArray(0); - - assertEquals(rect1.x, resultCoordinates.getInt(0)); - assertEquals(rect1.y, resultCoordinates.getInt(1)); - assertEquals(rect1.width, resultCoordinates.getInt(2)); - assertEquals(rect1.height, resultCoordinates.getInt(3)); - - resultCoordinates = resultFaces.getJSONArray(1); - - assertEquals(rect2.x, resultCoordinates.getInt(0)); - assertEquals(rect2.y, resultCoordinates.getInt(1)); - assertEquals(rect2.width, resultCoordinates.getInt(2)); - assertEquals(rect2.height, resultCoordinates.getInt(3)); - - } - - @Test - public void testContext() throws Exception { - // should allow sending context - if (cloudinary.config.apiSecret == null) - return; - @SuppressWarnings("rawtypes") - Map context = ObjectUtils.asMap("caption", "some caption", "alt", "alternative"); - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("context", context)); - } - - @Test - public void testModerationRequest() throws Exception { - // should support requesting manual moderation - if (cloudinary.config.apiSecret == null) - return; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("moderation", "manual"))); - assertEquals("manual", result.getJSONArray("moderation").getJSONObject(0).getString("kind")); - assertEquals("pending", result.getJSONArray("moderation").getJSONObject(0).getString("status")); - } - - @Test - public void testRawConvertRequest() { - // should support requesting raw conversion - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream("docx.docx"), ObjectUtils.asMap("raw_convert", "illegal", "resource_type", "raw")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(".*illegal is not a valid.*")); - } - } - - @Test - public void testCategorizationRequest() { - // should support requesting categorization - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("categorization", "illegal")); - } catch (Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); - } - } - - @Test - public void testDetectionRequest() { - // should support requesting detection - if (cloudinary.config.apiSecret == null) - return; - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("detection", "illegal")); - } catch (Exception e) { - assertTrue(e.getMessage().matches(".*(Illegal value|not a valid|invalid).*")); - } - } - - @Test - public void testAutoTaggingRequest() { - // should support requesting auto tagging - if (cloudinary.config.apiSecret == null) - return; - - try { - cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("auto_tagging", 0.5f)); - } catch (Exception e) { - for (int i = 0; i < e.getStackTrace().length; i++) { - StackTraceElement x = e.getStackTrace()[i]; - } - assertTrue(e.getMessage().matches("^Must use(.*)")); - } - } - - @Test - public void testFilenameOption() throws Exception { - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", "emanelif"))); - assertEquals("emanelif", result.getString("original_filename")); - } - - @Test - public void testComplexFilenameOption() throws Exception { - String complexFilename = "Universal Image Loader @#&=+-_.,!()~'%20.png"; - JSONObject result = new JSONObject(cloudinary.uploader().upload(getAssetStream(TEST_IMAGE), ObjectUtils.asMap("filename", complexFilename))); - complexFilename = complexFilename.replace(".png", ""); - - assertEquals(complexFilename, result.getString("original_filename")); - } - - @Test - @SuppressWarnings("unchecked") - public void testUploadLarge() throws Exception { - // support uploading large files - if (cloudinary.config.apiSecret == null) - return; - - File temp = File.createTempFile("cldupload.test.", ""); - FileOutputStream out = new FileOutputStream(temp); - int[] header = new int[]{0x42, 0x4D, 0x4A, 0xB9, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x78, 0x05, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xB8, 0x59, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xB8, 0x1E, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF5, 0x28, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - byte[] byteHeader = new byte[138]; - for (int i = 0; i <= 137; i++) byteHeader[i] = (byte) header[i]; - byte[] piece = new byte[10]; - Arrays.fill(piece, (byte) 0xff); - out.write(byteHeader); - for (int i = 1; i <= 588000; i++) { - out.write(piece); - } - out.close(); - assertEquals(5880138, temp.length()); - - JSONObject resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000))); - assertEquals("raw", resource.getString("resource_type")); - - resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5243000))); - assertEquals("image", resource.getString("resource_type")); - assertEquals(1400L, resource.getLong("width")); - assertEquals(1400L, resource.getLong("height")); - - resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138))); - assertEquals("image", resource.getString("resource_type")); - assertEquals(1400L, resource.getLong("width")); - assertEquals(1400L, resource.getLong("height")); - } - - @Test - public void testUploadLargeProgressCallback() throws Exception { - // support uploading large files - if (cloudinary.config.apiSecret == null) - return; - - - File temp = getLargeFile(); - final CountDownLatch signal = new CountDownLatch(1); - final long totalLength = temp.length(); - - ProgressCallback progressCallback = new ProgressCallback() { - @Override - public void onProgress(long bytesUploaded, long totalBytes) { - if (bytesUploaded == totalLength) { - signal.countDown(); - } - } - }; - JSONObject resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("resource_type", "raw", "chunk_size", 5243000), progressCallback)); - - signal.await(120, TimeUnit.SECONDS); - assertEquals(signal.getCount(), 0); - - assertEquals("raw", resource.getString("resource_type")); - - resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5243000))); - assertEquals("image", resource.getString("resource_type")); - assertEquals(1400L, resource.getLong("width")); - assertEquals(1400L, resource.getLong("height")); - - resource = new JSONObject(cloudinary.uploader().uploadLarge(temp, ObjectUtils.asMap("chunk_size", 5880138))); - assertEquals("image", resource.getString("resource_type")); - assertEquals(1400L, resource.getLong("width")); - assertEquals(1400L, resource.getLong("height")); - } -} diff --git a/cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample b/cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample deleted file mode 100644 index 3669bff2..00000000 --- a/cloudinary-android/src/androidTest/template/AndroidManifest.xml.sample +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/cloudinary-android/src/main/AndroidManifest.xml b/cloudinary-android/src/main/AndroidManifest.xml deleted file mode 100644 index 03acd910..00000000 --- a/cloudinary-android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java deleted file mode 100644 index a3c3420f..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/android/ApiStrategy.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.cloudinary.android; - -import java.util.Map; - -import com.cloudinary.Api.HttpMethod; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.strategies.AbstractApiStrategy; - -public class ApiStrategy extends AbstractApiStrategy { - - @SuppressWarnings("rawtypes") - @Override - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - throw new Exception("Administration API is not supported for mobile applications."); - } - -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java b/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java deleted file mode 100644 index 79bebbcb..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/android/MultipartUtility.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.cloudinary.android; - -import com.cloudinary.Cloudinary; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Map; - -/** - * This utility class provides an abstraction layer for sending multipart HTTP - * POST requests to a web server. - * - * @author www.codejava.net - * @author Cloudinary - */ -public class MultipartUtility { - private final String boundary; - private static final String LINE_FEED = "\r\n"; - private static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; - private final MultipartCallback multipartCallback; - private HttpURLConnection httpConn; - private String charset; - private OutputStream outputStream; - private PrintWriter writer; - - public final static String USER_AGENT = "CloudinaryAndroid/" + Cloudinary.VERSION; - - /** - * This constructor initializes a new HTTP POST request with content type is - * set to multipart/form-data - * - * @param requestURL - * @param charset - * @throws IOException - */ - public MultipartUtility(String requestURL, String charset, String boundary, Map headers) throws IOException { - this(requestURL, charset, boundary, headers, null); - } - - public MultipartUtility(String requestURL, String charset, String boundary, Map headers, MultipartCallback multipartCallback) throws IOException { - this.charset = charset; - this.boundary = boundary; - this.multipartCallback = multipartCallback; - - URL url = new URL(requestURL); - httpConn = (HttpURLConnection) url.openConnection(); - httpConn.setDoOutput(true); // indicates POST method - httpConn.setChunkedStreamingMode(0); - httpConn.setDoInput(true); - if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - httpConn.setRequestProperty(header.getKey(), header.getValue()); - } - } - httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); - httpConn.setRequestProperty("User-Agent", USER_AGENT); - outputStream = httpConn.getOutputStream(); - writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); - } - - public MultipartUtility(String requestURL, String charset, String boundary) throws IOException { - this(requestURL, charset, boundary, null, null); - } - - /** - * Adds a form field to the request - * - * @param name field name - * @param value field value - */ - public void addFormField(String name, String value) { - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED); - writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED); - writer.append(LINE_FEED); - writer.append(value).append(LINE_FEED); - writer.flush(); - } - - /** - * Adds a upload file section to the request - * - * @param fieldName name attribute in {@code } - * @param uploadFile a File to be uploaded - * @throws IOException - */ - public void addFilePart(String fieldName, File uploadFile, String fileName) throws IOException { - if (fileName == null) fileName = uploadFile.getName(); - FileInputStream inputStream = new FileInputStream(uploadFile); - addFilePart(fieldName, inputStream, fileName); - } - - public void addFilePart(String fieldName, File uploadFile) throws IOException { - addFilePart(fieldName, uploadFile, "file"); - } - - public void addFilePart(String fieldName, InputStream inputStream, String fileName) throws IOException { - if (fileName == null) fileName = "file"; - writer.append("--" + boundary).append(LINE_FEED); - writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED); - writer.append("Content-Type: ").append(APPLICATION_OCTET_STREAM).append(LINE_FEED); - writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); - writer.append(LINE_FEED); - writer.flush(); - - byte[] buffer = new byte[4096]; - int bytesRead; - long totalRead = 0; - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - notifyCallback(totalRead += bytesRead); - } - outputStream.flush(); - inputStream.close(); - - writer.append(LINE_FEED); - writer.flush(); - } - - private void notifyCallback(long bytes) { - if (multipartCallback != null) { - multipartCallback.totalBytesLoaded(bytes); - } - } - - public void addFilePart(String fieldName, InputStream inputStream) throws IOException { - addFilePart(fieldName, inputStream, "file"); - } - - /** - * Completes the request and receives response from the server. - * - * @return a list of Strings as response in case the server returned status - * OK, otherwise an exception is thrown. - * @throws IOException - */ - public HttpURLConnection execute() throws IOException { - writer.append("--" + boundary + "--").append(LINE_FEED); - writer.close(); - - return httpConn; - } - - /** - * Closes the internal connection's output stream. - * Closing a previously closed stream has no effect. - */ - public void close(){ - if (writer != null){ - writer.close(); - } - } - - /** - * For internal use only - callback to monitor multipart upload progress - */ - interface MultipartCallback { - void totalBytesLoaded(long bytes); - } - -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java b/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java deleted file mode 100644 index 9a3f1253..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/android/UploaderStrategy.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.cloudinary.android; - -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.ProgressCallback; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import java.io.*; -import java.net.HttpURLConnection; -import java.util.Collection; -import java.util.Map; - -import static com.cloudinary.android.MultipartUtility.*; - -public class UploaderStrategy extends AbstractUploaderStrategy { - - @SuppressWarnings("rawtypes") - @Override - public Map callApi(String action, Map params, Map options, Object file, final ProgressCallback progressCallback) throws IOException { - // 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)) { - String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary().config.apiKey); - if (apiKey == null) - throw new IllegalArgumentException("Must supply api_key"); - if (options.containsKey("signature") && options.containsKey("timestamp")) { - params.put("timestamp", options.get("timestamp")); - params.put("signature", options.get("signature")); - params.put("api_key", apiKey); - } else { - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary().config.apiSecret); - if (apiSecret == null) - throw new IllegalArgumentException("Must supply api_secret"); - params.put("timestamp", Long.valueOf(System.currentTimeMillis() / 1000L).toString()); - params.put("signature", this.cloudinary().apiSignRequest(params, apiSecret)); - params.put("api_key", apiKey); - } - } else { - // Nothing to do - } - - String apiUrl = buildUploadUrl(action, options); - MultipartCallback multipartCallback; - if (progressCallback == null) { - multipartCallback = null; - } else { - final long totalBytes = determineLength(file); - multipartCallback = new MultipartCallback() { - @Override - public void totalBytesLoaded(long bytes) { - progressCallback.onProgress(bytes, totalBytes); - } - }; - } - - MultipartUtility multipart = null; - HttpURLConnection connection; - - try { - multipart = new MultipartUtility(apiUrl, "UTF-8", this.cloudinary().randomPublicId(), (Map) options.get("extra_headers"), multipartCallback); - - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addFormField(param.getKey() + "[]", ObjectUtils.asString(value)); - } - } else { - if (StringUtils.isNotBlank(param.getValue())) { - multipart.addFormField(param.getKey(), param.getValue().toString()); - } - } - } - - if (file instanceof String && !((String) file).matches("(?s)ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { - file = new File((String) file); - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - multipart.addFilePart("file", (File) file, filename); - } else if (file instanceof String) { - multipart.addFormField("file", (String) file); - } else if (file instanceof InputStream) { - multipart.addFilePart("file", (InputStream) file, filename); - } else if (file instanceof byte[]) { - multipart.addFilePart("file", new ByteArrayInputStream((byte[]) file), filename); - } - - connection = multipart.execute(); - } finally { - if (multipart != null){ - // Closing more than once has no effect so we can call it safely without having to check state - multipart.close(); - } - } - - int code; - try { - code = connection.getResponseCode(); - } catch (IOException e) { - if (e.getMessage().equals("No authentication challenges found")) { - // Android trying to be clever... - code = 401; - } else { - throw e; - } - } - InputStream responseStream = code >= 400 ? connection.getErrorStream() : connection.getInputStream(); - String responseData = readFully(responseStream); - connection.disconnect(); - - try { - responseStream.close(); - } catch (Exception e) {} - - return processResponse(returnError, code, responseData); - } - - private long determineLength(Object file) { - long actualLength = -1; - - if (file != null) { - if (file instanceof File) { - actualLength = ((File) file).length(); - } else if (file instanceof byte[]) { - actualLength = ((byte[]) file).length; - } else if (!(file instanceof InputStream)) { - File f = new File(file.toString()); - actualLength = f.length(); - } - } - - return actualLength; - } - - protected static String readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length = 0; - while ((length = in.read(buffer)) != -1) { - baos.write(buffer, 0, length); - } - return new String(baos.toByteArray()); - } -} diff --git a/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java b/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java deleted file mode 100644 index 45ec07a8..00000000 --- a/cloudinary-android/src/main/java/com/cloudinary/android/Utils.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.cloudinary.android; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; - -public class Utils { - public static String cloudinaryUrlFromContext(Context context) { - String url = ""; - try { - PackageManager packageManager = context.getPackageManager(); - ApplicationInfo info = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - if (info != null && info.metaData != null) { - url = (String) info.metaData.get("CLOUDINARY_URL"); - } - } catch (NameNotFoundException e) { - // No metadata found - } - return url; - } -} diff --git a/settings.gradle b/settings.gradle index f192e7f7..c1342b86 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ rootProject.name = 'cloudinary-parent' include ':cloudinary-core' -include ':cloudinary-android' include ':cloudinary-taglib' include ':cloudinary-test-common' include ':cloudinary-http42' From bdfb635f44e2d49a90bb10bf71cb198d22d3d822 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 22 Aug 2017 12:32:35 +0300 Subject: [PATCH 337/592] Remove preset creation script (only used by Android). --- .../scripts/create_unsigned_preset.sh | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 cloudinary-test-common/scripts/create_unsigned_preset.sh diff --git a/cloudinary-test-common/scripts/create_unsigned_preset.sh b/cloudinary-test-common/scripts/create_unsigned_preset.sh deleted file mode 100644 index 30e0c238..00000000 --- a/cloudinary-test-common/scripts/create_unsigned_preset.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -# Create the unsigned upload preset required for tests -# Currently only required for the Android test since Android API cannot create the preset - -UNSIGNED_PRESET="cloudinary_java_test" -SDK_TEST_TAG="cloudinary_java_test" - -if [ -z ${CLOUDINARY_URL+x} ] - then echo "The variable CLOUDINARY_URL must be set!" -else - - API_CRED=${CLOUDINARY_URL%@*} - API_CRED=${API_CRED#*//} - if curl -s "https://${API_CRED#*//}@api.cloudinary.com/v1_1/${CLOUDINARY_URL#*@}/upload_presets/${UNSIGNED_PRESET}" | \ - grep --quiet "Can't find upload preset named" - then curl --data "name=${UNSIGNED_PRESET}&unsigned=true&tags=${TAG}" \ - "https://${API_CRED#*//}@api.cloudinary.com/v1_1/${CLOUDINARY_URL#*@}/upload_presets" - echo - else - echo "Preset already exists" - fi -fi \ No newline at end of file From 63115fc3d46bdb3fe030e71d54f5735ac580e0d3 Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 5 Sep 2017 12:07:09 +0300 Subject: [PATCH 338/592] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8cace2e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Cloudinary + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From fe423f2297b28c2e206855d4c589dce832cf91de Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 5 Sep 2017 12:09:40 +0300 Subject: [PATCH 339/592] Add badges to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bc231c5e..a934e82a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![Build Status](https://travis-ci.org/cloudinary/cloudinary_java.svg?branch=master)](https://travis-ci.org/cloudinary/cloudinary_java) +[![Maven Central](https://img.shields.io/maven-central/v/com.cloudinary/cloudinary-core.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Acom.cloudinary) +[![license](https://img.shields.io/github/license/cloudinary/cloudinary_js.svg?maxAge=2592000)]() + Cloudinary ========== From 00c99ed36f0e28a744970df9d089e3384c23c17a Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 5 Sep 2017 12:36:23 +0300 Subject: [PATCH 340/592] Version 1.15.0 --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf897b0..02a2b417 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ +1.15.0 / 2017-09-05 +=================== + +New functionality +----------------- + + * Add format field to `ResponsiveBreakpoint`. + * The Android SDK has been moved to https://github.com/cloudinary/cloudinary_android + +Other changes +------------- + + * Add badges to README + * Create LICENSE + * Centralize response handling and respect `returnError` param. + * Fix boolean config values in tag generation + * Fix project for java8, update cloudinary dependencies. + 1.14.0 / 2017-07-20 =================== diff --git a/README.md b/README.md index a934e82a..591e23cd 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.14.0 + 1.15.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.14.0/cloudinary-core-1.14.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.14.0/cloudinary-http44-1.14.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.15.0/cloudinary-core-1.15.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.15.0/cloudinary-http44-1.15.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c6108978..65f4ce74 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.14.0"; + public final static String VERSION = "1.15.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 9740cf05..2cb79dc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.14.0 +version=1.15.0 From 2fca8d746972d0f9d226cd06f78443d82f078be1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 18 Sep 2017 12:57:34 +0300 Subject: [PATCH 341/592] Change url suffix and root path limitations * Allow rootPath/urlSuffix without a private cdn. * Allow rootPath/urlSuffix for videos. * Allow rootPath/urlSuffix for authenticated images. --- .../src/main/java/com/cloudinary/Url.java | 17 +++++++-------- .../com/cloudinary/test/CloudinaryTest.java | 21 +++++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 96d3583e..de10d283 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -328,15 +328,6 @@ public String generate(String source) { throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); } - if (!this.config.privateCdn) { - if (StringUtils.isNotBlank(urlSuffix)) { - throw new IllegalArgumentException("URL Suffix only supported in private CDN"); - } - if (useRootPath) { - throw new IllegalArgumentException("Root path only supported in private CDN"); - } - } - if (source == null) { if (publicId == null) { if (this.source == null) { @@ -457,11 +448,17 @@ public String finalizeResourceType(String resourceType, String type, String urlS } else if (resourceType.equals("image") && type.equals("private")) { resourceType = "private_images"; type = null; + } else if (resourceType.equals("image") && type.equals("authenticated")){ + resourceType = "authenticated_images"; + type = null; } else if (resourceType.equals("raw") && type.equals("upload")) { resourceType = "files"; type = null; + } else if (resourceType.equals("video") && type.equals("upload")){ + resourceType = "videos"; + type = null; } else { - throw new IllegalArgumentException("URL Suffix only supported for image/upload, image/private and raw/upload"); + throw new IllegalArgumentException("URL Suffix only supported for image/upload, image/private, raw/upload, image/authenticated and video/upload"); } } if (useRootPath) { 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 261729de..4ee95237 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -163,11 +163,6 @@ public void testCnameSubdomain() { assertEquals("http://a2.hello.com/test123/image/upload/test", result); } - @Test(expected = IllegalArgumentException.class) - public void testDisallowUrlSuffixInSharedDistribution() { - cloudinary.url().suffix("hello").generate("test"); - } - @Test(expected = IllegalArgumentException.class) public void testDisallowUrlSuffixInNonUploadTypes() { cloudinary.url().suffix("hello").privateCdn(true).type("facebook").generate("test"); @@ -228,15 +223,23 @@ public void testSupportUrlSuffixForRawUploads() { assertEquals("http://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); + } + + @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); + } + @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); } - @Test(expected = IllegalArgumentException.class) - public void testDisllowUseRootPathInSharedDistribution() { - cloudinary.url().useRootPath(true).generate("test"); - } @Test public void testSupportUseRootPathForPrivateCdn() { From f86b678ceae3f8293a84b80b7a44eefc213e7913 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 19 Sep 2017 13:47:34 +0300 Subject: [PATCH 342/592] Fix android repository link --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 591e23cd..901bbf45 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ Cloudinary provides URL and HTTP based APIs that can be easily integrated with a For Java, Cloudinary provides a library for simplifying the integration even further. -**Note:** Starting from version 1.1.0, you should depend on cloudinary-http42 for Java and cloudinary-android for Android. The artifact cloudinary is deprecated. From version 1.2.1 cloudinary-http44 is available. From version 1.2.2 cloudinary-http43 is available. +**Notes:** -**Note:** This readme intended mainly for Web applications. For **Android** specific instructions, see: https://github.com/cloudinary/cloudinary_java/tree/master/cloudinary-android +* There are three flavors of the library to support different HttpClient versions: cloudinary-http42, cloudinary-http43 and cloudinary-http44. +* For Android there's a separate library available at https://github.com/cloudinary/cloudinary_android ## Getting started guide ![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](http://cloudinary.com/documentation/java_integration#getting_started_guide)**. From 93f737470ebeafd661dd07019cd8b5443d237531 Mon Sep 17 00:00:00 2001 From: Nadav Soferman Date: Tue, 19 Sep 2017 14:10:23 +0300 Subject: [PATCH 343/592] Update Readme to point to HTTPS URLs of cloudinary.com And adding demo link and fixing the support forums link. --- README.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 901bbf45..cdf73684 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ For Java, Cloudinary provides a library for simplifying the integration even fur * For Android there's a separate library available at https://github.com/cloudinary/cloudinary_android ## Getting started guide -![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](http://cloudinary.com/documentation/java_integration#getting_started_guide)**. +![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](https://cloudinary.com/documentation/java_integration#getting_started_guide)**. ## Setup ###################################################################### @@ -73,7 +73,7 @@ Generating a 120x90 thumbnail based on automatic face detection of the Facebook ![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") -For more details, see our documentation for embedding [Facebook](http://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](http://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. +For more details, see our documentation for embedding [Facebook](https://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](https://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. ## Usage @@ -81,7 +81,7 @@ For more details, see our documentation for embedding [Facebook](http://cloudina Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. -See [API, URLs and access identifiers](http://cloudinary.com/documentation/api_and_access_identifiers) for more details. +See [API, URLs and access identifiers](https://cloudinary.com/documentation/solution_overview#account_and_api_setup) for more details. Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, by when initializing the Cloudinary object, or by using the CLOUDINARY_URL environment variable / system property. @@ -135,7 +135,7 @@ Same goes for Twitter: cloudinary.url().type("twitter_name").generate("billclinton.jpg"); ``` -![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_manipulation) for more information about displaying and transforming images in Java**. +![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_manipulation) for more information about displaying and transforming images in Java**. ### Upload @@ -165,7 +165,7 @@ cloudinary.url().generate("sample_remote.jpg"); # http://res.cloudinary.com/demo/image/upload/sample_remote.jpg ``` -![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. +![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. ### imageTag @@ -191,24 +191,28 @@ Map htmlOptions = ObjectUtils.asMap("alt", "sample"); String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); ``` -![](http://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](http://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. +![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. ## Additional resources ########################################################## Additional resources are available at: -* [Website](http://cloudinary.com) -* [Documentation](http://cloudinary.com/documentation) -* [Image transformations documentation](http://cloudinary.com/documentation/image_transformations) -* [Upload API documentation](http://cloudinary.com/documentation/upload_images) +* [Website](https://cloudinary.com) +* [Interactive demo](https://demo.cloudinary.com/default) +* [Knowledge Base](https://support.cloudinary.com/hc/en-us) +* [Documentation](https://cloudinary.com/documentation) +* [Documentation for Java integration](https://cloudinary.com/documentation/java_integration) +* [Image transformations documentation](https://cloudinary.com/documentation/image_transformations) +* [Upload API documentation](https://cloudinary.com/documentation/upload_images) ## Support You can [open an issue through GitHub](https://github.com/cloudinary/cloudinary_java/issues). -Contact us at [info@cloudinary.com](mailto:info@cloudinary.com) +Contact us [https://cloudinary.com/contact](https://cloudinary.com/contact) + +Stay tuned for updates, tips and tutorials: [Blog](https://cloudinary.com/blog), [Twitter](https://twitter.com/cloudinary), [Facebook](https://www.facebook.com/Cloudinary). -Or via Twitter: [@cloudinary](https://twitter.com/#!/cloudinary) ## License ####################################################################### From b0fa16081c8491dd98434c9796d324b9b1d1c60e Mon Sep 17 00:00:00 2001 From: Amir Tocker Date: Tue, 26 Sep 2017 12:03:07 +0300 Subject: [PATCH 344/592] Add update_version.sh --- tools/update_version.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 tools/update_version.sh diff --git a/tools/update_version.sh b/tools/update_version.sh new file mode 100755 index 00000000..229b3ddd --- /dev/null +++ b/tools/update_version.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +new_version=$1 + +current_version=`grep -oP "(?<=VERSION \= \")([0-9.]+)(?=\")" cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java` +current_version_re=${current_version//./\\.} +echo "Current version is $current_version" +if [ -n "$new_version" ]; then + echo "New version will be $new_version" + echo "Pattern used: $current_version_re" + sed -e "s/${current_version_re}/${new_version}/g" -i "" cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java + sed -e "s/${current_version_re}/${new_version}/g" -i "" README.md + sed -e "s/${current_version_re}/${new_version}/g" -i "" gradle.properties + git changelog -t $new_version +else + echo "Usage: $0 " + echo "For example: $0 1.9.2" +fi From b2666720484fcb6b4f7869eaba4aa43327df44df Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 26 Sep 2017 12:49:16 +0300 Subject: [PATCH 345/592] Version 1.16.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 02a2b417..b3b4afc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +1.16.0 / 2017-09-26 +=================== + + * Change url suffix and root path limitations + * Add update_version.sh + * Update Readme to point to HTTPS URLs of cloudinary.com + * Fix android repository link + 1.15.0 / 2017-09-05 =================== diff --git a/README.md b/README.md index cdf73684..45781b95 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.15.0 + 1.16.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.15.0/cloudinary-core-1.15.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.15.0/cloudinary-http44-1.15.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.16.0/cloudinary-core-1.16.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.16.0/cloudinary-http44-1.16.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 65f4ce74..0398ca92 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.15.0"; + public final static String VERSION = "1.16.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 2cb79dc1..0b2db6ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.15.0 +version=1.16.0 From c7c6d6e89271e67951554f4ea79dcf0c3b084b02 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 22 Nov 2017 13:47:57 +0200 Subject: [PATCH 346/592] Feature/travis add jdk versions (#111) * Remove Android and add more jdk versions to tests. --- .travis.yml | 22 +++------------------- java_shared.gradle | 6 ++++++ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index b665e3c9..bfea8c7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,6 @@ -language: android -dist: trusty +language: java +dist: precise sudo: required -group: edge - -android: - components: - - tools - - platform-tools - - tools - - build-tools-25.0.2 - - android-25 - - extra-android-m2repository before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock @@ -22,13 +12,7 @@ cache: jdk: - oraclejdk8 - -# Android build tools 25 require java 8 - until project separation java 7 tests are disabled -# - oraclejdk7 -# - openjdk7 - -# Temporarily disabled, test fail because Hamcrest needs java 1.7 -# - openjdk6 + - oraclejdk7 # ciTest is configured to skip the various timeout tests that don't work in travis script: ./gradlew clean ciTest diff --git a/java_shared.gradle b/java_shared.gradle index 4194cca7..bfbf0e8f 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -28,4 +28,10 @@ task javadocJar(type: Jar, dependsOn: javadoc) { artifacts { archives sourcesJar archives javadocJar +} + +tasks.each { task -> + if (!project.hasProperty("ossrhPassword") && "signArchives" == task.name) { + task.enabled = false + } } \ No newline at end of file From 97d43d5928a2187e556191b2c1f35fff8982b598 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 26 Nov 2017 12:14:55 +0200 Subject: [PATCH 347/592] Add missing params to `explicit` and `upload` api (#113) Add missing params to `explicit` --- .../main/java/com/cloudinary/Uploader.java | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 32804016..3108ff0b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -245,32 +245,11 @@ public Map rename(String fromPublicId, String toPublicId, Map options) throws IO } public Map explicit(String publicId, Map options) throws IOException { - if (options == null) + if (options == null) { options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - params.put("public_id", publicId); - params.put("callback", (String) options.get("callback")); - params.put("type", (String) options.get("type")); - params.put("eager", Util.buildEager((List) options.get("eager"))); - params.put("eager_async", ObjectUtils.asBoolean(options.get("eager_async"), false).toString()); - params.put("eager_notification_url", (String) options.get("eager_notification_url")); - params.put("headers", Util.buildCustomHeaders(options.get("headers"))); - params.put("tags", StringUtils.join(ObjectUtils.asArray(options.get("tags")), ",")); - params.put("moderation", (String) options.get("moderation")); - params.put("ocr", (String) options.get("ocr")); - if (options.get("face_coordinates") != null) { - params.put("face_coordinates", Coordinates.parseCoordinates(options.get("face_coordinates")).toString()); - } - if (options.get("custom_coordinates") != null) { - params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); } - if (options.get("context") != null) { - params.put("context", Util.encodeContext(options.get("context"))); - } - if (options.get("responsive_breakpoints") != null) { - params.put("responsive_breakpoints", JSONObject.wrap(options.get("responsive_breakpoints"))); - } - params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + Map params = buildUploadParams(options); + params.put("public_id", publicId); return callApi("explicit", params, options, null); } From a3612ab6e29ce01748985bacc6cd6d9977a90f23 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 26 Nov 2017 12:27:43 +0200 Subject: [PATCH 348/592] Version 1.17.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b4afc4..129e1900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ +1.17.0 / 2017-11-26 +=================== + + * Add missing params to `explicit` and `upload` api + * Remove Android from Travis configuration and add JDK versions + 1.16.0 / 2017-09-26 =================== diff --git a/README.md b/README.md index 45781b95..a599ffd7 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.16.0 + 1.17.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.16.0/cloudinary-core-1.16.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.16.0/cloudinary-http44-1.16.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.17.0/cloudinary-core-1.17.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.17.0/cloudinary-http44-1.17.0.jar) and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 0398ca92..4d118795 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -40,7 +40,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.16.0"; + public final static String VERSION = "1.17.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 0b2db6ae..867fce6d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.16.0 +version=1.17.0 From 2c4a2358836f63d6c92bfd3718879d1f327fc383 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 17 Jan 2018 11:21:52 +0200 Subject: [PATCH 349/592] Replace `pom.xml` link with `build.gradle` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a599ffd7..c1dfd2b6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. ``` Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.17.0/cloudinary-core-1.17.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.17.0/cloudinary-http44-1.17.0.jar) -and see [pom.xml](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/pom.xml) for library dependencies. +and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away From 4d3dda700a3e0cfb05a2e632a85cac383af534df Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 17 Jan 2018 15:13:04 +0200 Subject: [PATCH 350/592] Verify `testDeleteByToken` takes all original config into account (#116) --- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 5 ++++- 1 file changed, 4 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 1b606207..1538f48b 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 @@ -88,7 +88,10 @@ public void testDeleteByToken() throws Exception { Map options = ObjectUtils.asMap("return_delete_token", true, "tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG}); Map res = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); String token = (String) res.get("delete_token"); - res = new Cloudinary(ObjectUtils.asMap("cloud_name", cloudinary.config.cloudName)).uploader().deleteByToken(token); + Map baseConfig = cloudinary.config.asMap(); + baseConfig.remove("api_key"); + baseConfig.remove("api_secret"); + res = new Cloudinary(baseConfig).uploader().deleteByToken(token); assertNotNull(res); assertEquals("ok", res.get("result")); } From b0397d19d423cc1213387088bd175dcb617a2f57 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 8 Mar 2018 10:08:44 +0200 Subject: [PATCH 351/592] Add access control parameter to upload and update calls. --- .../com/cloudinary/AccessControlRule.java | 66 +++++++++++++++++++ .../src/main/java/com/cloudinary/Util.java | 18 ++++- .../com/cloudinary/utils/ObjectUtils.java | 12 ++++ .../test/java/com/cloudinary/UtilTest.java | 37 +++++++++++ .../com/cloudinary/test/AbstractApiTest.java | 24 +++++-- .../cloudinary/test/AbstractUploaderTest.java | 60 ++++++++++++++++- 6 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/AccessControlRule.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/AccessControlRule.java b/cloudinary-core/src/main/java/com/cloudinary/AccessControlRule.java new file mode 100644 index 00000000..e1870d7c --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/AccessControlRule.java @@ -0,0 +1,66 @@ +package com.cloudinary; + +import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONObject; + +import java.util.Date; + +/** + * A class representing a single access control rule for a resource. Used as a parameter for {@link Api#update} and {@link Uploader#upload} + */ +public class AccessControlRule extends JSONObject { + + /** + * Construct a new token access rule + * @return The access rule instance + */ + public static AccessControlRule token(){ + return new AccessControlRule(AccessType.token, null, null); + } + + /** + * Construct a new anonymous access rule + * @param start The start date for the rule + * @return The access rule instance + */ + public static AccessControlRule anonymousFrom(Date start){ + return new AccessControlRule(AccessType.anonymous, start, null); + } + + /** + * Construct a new anonymous access rule + * @param end The end date for the rule + * @return The access rule instance + */ + public static AccessControlRule anonymousUntil(Date end){ + return new AccessControlRule(AccessType.anonymous, null, end); + } + + /** + * Construct a new anonymous access rule + * @param start The start date for the rule + * @param end The end date for the rule + * @return The access rule instance + */ + public static AccessControlRule anonymous(Date start, Date end){ + return new AccessControlRule(AccessType.anonymous, start, end); + } + + private AccessControlRule(AccessType accessType, Date start, Date end) { + put("access_type", accessType.name()); + if (start != null) { + put("start", ObjectUtils.toISO8601(start)); + } + + if (end != null) { + put("end", ObjectUtils.toISO8601(end)); + } + } + + /** + * Access type for an access rule + */ + public enum AccessType { + anonymous, token + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 927f2beb..436c01e6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -1,11 +1,11 @@ package com.cloudinary; -import java.util.*; - import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONObject; +import java.util.*; + 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"}; @@ -48,6 +48,8 @@ public static final Map buildUploadParams(Map options) { } processWriteParameters(options, params); } else { + // if there's a signature, it means all the params are already serialized so + // we don't need to construct them, just pass the value as is: params.put("eager", (String) options.get("eager")); params.put("transformation", (String) options.get("transformation")); params.put("headers", (String) options.get("headers")); @@ -60,6 +62,7 @@ public static final Map buildUploadParams(Map options) { params.put("detection", (String) options.get("detection")); params.put("similarity_search", (String) options.get("similarity_search")); params.put("auto_tagging", (String) options.get("auto_tagging")); + params.put("access_control", (String) options.get("access_control")); } return params; } @@ -92,6 +95,9 @@ public static final void processWriteParameters(Map options, Map params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); if (options.get("context") != null) params.put("context", encodeContext(options.get("context"))); + if (options.get("access_control") != null) { + params.put("access_control", encodeAccessControl(options.get("access_control"))); + } putObject("ocr", options, params); putObject("raw_convert", options, params); putObject("categorization", options, params); @@ -102,6 +108,14 @@ public static final void processWriteParameters(Map options, Map params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); } + protected static String encodeAccessControl(Object accessControl) { + if (accessControl instanceof AccessControlRule) { + accessControl = Arrays.asList(accessControl); + } + + return JSONObject.wrap(accessControl).toString(); + } + protected static String encodeContext(Object context) { if (context != null && context instanceof Map) { Map mapArg = (Map) context; 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 0984ba6e..1725ad55 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -5,10 +5,22 @@ import org.cloudinary.json.JSONObject; import java.io.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.*; public class ObjectUtils { + /** + * Formats a Date as an ISO-8601 string representation. + * @param date Date to format + * @return The date formatted as ISO-8601 string + */ + public static String toISO8601(Date date){ + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat.format(date); + } public static String asString(Object value) { if (value == null) { diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java index bdae440a..f15e17c4 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -1,8 +1,12 @@ package com.cloudinary; import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONObject; import org.junit.Test; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Map; import static org.junit.Assert.*; @@ -11,6 +15,12 @@ * Created by amir on 17/11/2016. */ public class UtilTest { + + public static final String START = "2019-02-22 16:20:57 +0200"; + public static final String END = "2019-03-22 00:00:00 +0200"; + public static final String START_REFORMATTED = "2019-02-22T14:20:57Z"; + public static final String END_REFORMATTED = "2019-03-21T22:00:00Z"; + @Test public void encodeContext() throws Exception { Map context = ObjectUtils.asMap("caption", "different = caption", "alt2", "alt|alternative"); @@ -19,4 +29,31 @@ public void encodeContext() throws Exception { "alt2=alt\\|alternative|caption=different \\= caption".equals(result)); } + @Test + public void testAccessControlRule() throws ParseException { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + final Date start = simpleDateFormat.parse(START); + final Date end = simpleDateFormat.parse(END); + AccessControlRule acl = AccessControlRule.anonymous(start, end); + + JSONObject deserializedAcl = new JSONObject(acl.toString()); + assertEquals(deserializedAcl.get("access_type"), "anonymous"); + assertEquals(deserializedAcl.get("start"), START_REFORMATTED); + assertEquals(deserializedAcl.get("end"), END_REFORMATTED); + + acl = AccessControlRule.anonymousFrom(start); + assertEquals(2, acl.length()); + assertEquals(deserializedAcl.get("access_type"), "anonymous"); + assertEquals(deserializedAcl.get("start"), START_REFORMATTED); + + acl = AccessControlRule.anonymousUntil(end); + assertEquals(2, acl.length()); + assertEquals(deserializedAcl.get("access_type"), "anonymous"); + assertEquals(deserializedAcl.get("end"), END_REFORMATTED); + + AccessControlRule token = AccessControlRule.token(); + assertEquals(1, token.length()); + assertEquals("{\"access_type\":\"token\"}", token.toString()); + + } } \ 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 63bf414d..8942e7f5 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 @@ -1,9 +1,6 @@ package com.cloudinary.test; -import com.cloudinary.Api; -import com.cloudinary.Cloudinary; -import com.cloudinary.Coordinates; -import com.cloudinary.Transformation; +import com.cloudinary.*; import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; @@ -13,6 +10,7 @@ import org.junit.rules.TestName; import java.io.IOException; +import java.text.SimpleDateFormat; import java.util.*; import static org.hamcrest.Matchers.*; @@ -594,6 +592,24 @@ public void testUpdateCustomCoordinates() throws IOException, Exception { } } + @Test + public void testUpdateAccessControl() throws Exception { + // should update access control + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + final Date start = simpleDateFormat.parse("2019-02-22 16:20:57 +0200"); + final Date end = simpleDateFormat.parse("2019-03-22 00:00:00 +0200"); + AccessControlRule acl = AccessControlRule.anonymous(start, end); + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); + ApiResponse res = cloudinary.api().update(uploadResult.get("public_id").toString(), ObjectUtils.asMap("access_control", acl)); + Map result = cloudinary.api().resource(uploadResult.get("public_id").toString(), ObjectUtils.asMap("access_control", true)); + + Map accessControlResult = (Map) ((List) result.get("access_control")).get(0); + + assertEquals("anonymous", accessControlResult.get("access_type")); + assertEquals("2019-02-22T14:20:57Z", accessControlResult.get("start")); + assertEquals("2019-03-21T22:00:00Z", accessControlResult.get("end")); + } + @Test public void testListUploadPresets() throws Exception { // should allow creating and listing upload_presets 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 1538f48b..8c277816 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 @@ -4,12 +4,15 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; import org.junit.*; import org.junit.rules.TestName; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.*; import java.util.zip.ZipInputStream; @@ -408,11 +411,15 @@ public void testRawConvertRequest() { @Test public void testCategorizationRequest() { //should support requesting categorization + String errorMessage = ""; + try { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("categorization", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid|invalid)(.*)")); + errorMessage = e.getMessage(); } + + assertTrue(errorMessage.contains("Categorization item illegal is not valid")); } @Test @@ -571,4 +578,55 @@ public void testUploadInvalidUrl() { } } + @Test + public void testAccessControl() throws ParseException, IOException { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + final Date start = simpleDateFormat.parse("2019-02-22 16:20:57 +0200"); + final Date end = simpleDateFormat.parse("2019-03-22 00:00:00 +0200"); + AccessControlRule acl; + AccessControlRule token = AccessControlRule.token(); + + acl = AccessControlRule.anonymous(start, null); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("access_control", + Arrays.asList(acl, token), "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + + assertNotNull(result); + List> accessControlResponse = (List>) result.get("access_control"); + assertNotNull(accessControlResponse); + assertEquals(2, accessControlResponse.size()); + + Map acr = accessControlResponse.get(0); + assertEquals("anonymous", acr.get("access_type")); + assertEquals("2019-02-22T14:20:57Z", acr.get("start")); + assertThat(acr, not(hasKey("end"))); + + acr = accessControlResponse.get(1); + assertEquals("token", acr.get("access_type")); + assertThat(acr, not(hasKey("start"))); + assertThat(acr, not(hasKey("end"))); + + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("access_control", + acl, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + + assertNotNull(result); + accessControlResponse = (List>) result.get("access_control"); + assertNotNull(accessControlResponse); + acr = accessControlResponse.get(0); + assertEquals(1, accessControlResponse.size()); + assertEquals("anonymous", acr.get("access_type")); + assertEquals("2019-02-22T14:20:57Z", acr.get("start")); + assertThat(acr, not(hasKey("end"))); + + String aclString = "[{\"access_type\":\"anonymous\",\"start\":\"2019-02-22 16:20:57 +0200\",\"end\":\"2019-03-22 00:00 +0200\"}]"; + result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("access_control", + aclString, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + + assertNotNull(result); + accessControlResponse = (List>) result.get("access_control"); + assertNotNull(accessControlResponse); + assertTrue(accessControlResponse.size() == 1); + assertEquals("anonymous", accessControlResponse.get(0).get("access_type")); + assertEquals("2019-02-22T14:20:57Z", accessControlResponse.get(0).get("start")); + assertEquals("2019-03-21T22:00:00Z", accessControlResponse.get(0).get("end")); + } } From 99689f8f7781de977a31b6397c4aeb69cf747475 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 8 Mar 2018 11:49:38 +0200 Subject: [PATCH 352/592] Configure .travis.yml to show more test information --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bfea8c7d..428f96aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ jdk: - oraclejdk7 # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean ciTest +script: ./gradlew clean ciTest -i From 0557b7c4bc52a2a400d96439db7eae432602e3d8 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Mar 2018 11:53:10 +0200 Subject: [PATCH 353/592] Fix `testOcrUpdate()` test case (#119) --- .../main/java/com/cloudinary/test/AbstractApiTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 8942e7f5..e29a4cd7 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 @@ -520,14 +520,18 @@ public void testManualModeration() throws Exception { @Test public void testOcrUpdate() { + Exception expected = null; // should support requesting ocr info try { Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("ocr", "illegal")); } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); + expected = e; } + + assertNotNull(expected); + assertTrue(expected instanceof BadRequest); + assertTrue(expected.getMessage().matches("^Illegal value(.*)")); } @Test From 8860812d0d822b1510792c6d6a9137d2377dcf51 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Mar 2018 13:13:38 +0200 Subject: [PATCH 354/592] Fix authToken generation when using acl. --- cloudinary-core/src/main/java/com/cloudinary/AuthToken.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index a47dc67c..9bef9e95 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -157,7 +157,7 @@ public String generate(String url) { tokenParts.add("acl=" + escapeToLower(acl)); } ArrayList toSign = new ArrayList(tokenParts); - if (url != null) { + if (url != null && acl == null) { toSign.add("url=" + escapeToLower(url)); } String auth = digest(StringUtils.join(toSign, "~")); From 2ab4ea8fb1f364d14871729b39359e2e587cee81 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Mar 2018 13:53:07 +0200 Subject: [PATCH 355/592] Version 1.18.0 --- CHANGELOG.md | 17 +++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 24 +++++++------------ gradle.properties | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 129e1900..19dcbdd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,21 @@ +1.18.0 / 2018-03-15 +=================== + +New functionality +----------------- + + * Add access control parameter to upload and update calls + +Other changes +------------- + + * Fix authToken generation when using acl + * Fix `testOcrUpdate()` test case (#119) + * Configure .travis.yml to show more test information. + * Verify `testDeleteByToken` takes all original config into account (#116) + * Replace `pom.xml` link with `build.gradle` in README.md + 1.17.0 / 2017-11-26 =================== diff --git a/README.md b/README.md index c1dfd2b6..04c8841a 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.17.0 + 1.18.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.17.0/cloudinary-core-1.17.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.17.0/cloudinary-http44-1.17.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.18.0/cloudinary-core-1.18.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.18.0/cloudinary-http44-1.18.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 4d118795..e922ac61 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -1,26 +1,18 @@ package com.cloudinary; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.StrategyLoader; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.*; + @SuppressWarnings({"rawtypes", "unchecked"}) public class Cloudinary { @@ -40,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.17.0"; + public final static String VERSION = "1.18.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 867fce6d..4e347032 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.17.0 +version=1.18.0 From 090788607f1b92f895b7b79c67e1e628719666c6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 23 Apr 2018 15:51:51 +0300 Subject: [PATCH 356/592] Fix raw convert error message test --- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 8c277816..bbce143a 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 @@ -4,7 +4,6 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; -import org.cloudinary.json.JSONObject; import org.junit.*; import org.junit.rules.TestName; @@ -404,7 +403,7 @@ public void testRawConvertRequest() { try { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("raw_convert", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + assertTrue(e.getMessage().contains("Raw convert is invalid")); } } From e4f9dfe502c878f43bbd78c4ee6156706a98b33d Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 8 May 2018 12:03:57 +0300 Subject: [PATCH 357/592] Remove `test02Resources` test (broken and unnecessary). --- .../java/com/cloudinary/test/AbstractApiTest.java | 15 --------------- 1 file changed, 15 deletions(-) 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 e29a4cd7..ea534e65 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 @@ -150,21 +150,6 @@ public void test01ResourceTypes() throws Exception { assertThat(resource_types, hasItem("image")); } - @Test - public void test02Resources() throws Exception { - // should allow listing resources - Map resource = preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS)); - - final List resources = new ArrayList(); - String next_cursor = null; - do { - Map result = api.resources(ObjectUtils.asMap("max_results", 500, "next_cursor", next_cursor)); - resources.addAll((List) result.get("resources")); - next_cursor = (String) result.get("next_cursor"); - } while (next_cursor != null); - assertThat(resources, hasItem(allOf(hasEntry("public_id", (String) resource.get("public_id")), hasEntry("type", "upload")))); - } - @Test public void test03ResourcesCursor() throws Exception { // should allow listing resources with cursor From 5cb9416c02988acb616cf8713de3ce23d902b227 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 7 May 2018 16:27:34 +0300 Subject: [PATCH 358/592] Separate modules to run on different travis jobs. --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 428f96aa..2329f76c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,14 @@ cache: - $HOME/.gradle/wrapper/ jdk: - - oraclejdk8 - oraclejdk7 + - oraclejdk8 +env: + - MODULE=core + - MODULE=http42 + - MODULE=http43 + - MODULE=http44 # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean ciTest -i +script: ./gradlew clean ciTest -p cloudinary-${MODULE} -i + From 4f0c11e0ba396d0d5e06541faf9c313d83f7fa8a Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 8 May 2018 14:51:12 +0300 Subject: [PATCH 359/592] Feature/keyframe interval (#118) * Add keyframe interval transformation parameter --- .../java/com/cloudinary/Transformation.java | 21 ++++++++++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 11 ++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index e6bc5757..fd0aa0b0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -121,6 +121,24 @@ public T gravity(String value) { return param("gravity", value); } + /** + * Set the keyframe interval parameter + * @param value Interval in seconds + * @return The transformation for chaining + */ + public T keyframeInterval(float value) { + return param("keyframe_interval", value); + } + + /** + * Set the keyframe interval parameter + * @param value Interval in seconds. + * @return The transformation for chaining + */ + public T keyframeInterval(String value) { + return param("keyframe_interval", value); + } + public T colorSpace(String value) { return param("color_space", value); } @@ -623,7 +641,8 @@ public String generate(Map options) { "pg", "page", "u", "underlay", "vs", "video_sampling", - "sp", "streaming_profile" + "sp", "streaming_profile", + "ki", "keyframe_interval" }; for (int i = 0; i < simple_params.length; i += 2) { 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 4ee95237..bb6fc117 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1084,6 +1084,17 @@ public void testFps() { } + @Test + public void testKeyframeInterval(){ + assertEquals("ki_10.0", new Transformation().keyframeInterval(10).generate()); + assertEquals("ki_0.05", new Transformation().keyframeInterval(0.05f).generate()); + assertEquals("ki_3.45", new Transformation().keyframeInterval(3.45f).generate()); + assertEquals("ki_300.0", new Transformation().keyframeInterval(300).generate()); + assertEquals("ki_10", new Transformation().keyframeInterval("10").generate()); + assertEquals("", new Transformation().keyframeInterval("").generate()); + assertEquals("", new Transformation().keyframeInterval(null).generate()); + } + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { From 751c2e4be8e47a0428232e113101e906825cdd28 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 8 May 2018 16:06:19 +0300 Subject: [PATCH 360/592] Fix responsive breakpoint format field implementation, add tests. --- .../com/cloudinary/ResponsiveBreakpoint.java | 25 +++---------------- .../cloudinary/test/AbstractUploaderTest.java | 16 ++++++++---- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java index c0c0c02f..d377f5b7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java @@ -1,13 +1,8 @@ package com.cloudinary; -import com.cloudinary.utils.StringUtils; - import org.cloudinary.json.JSONObject; public class ResponsiveBreakpoint extends JSONObject { - private Transformation transformation = null; - private String format = ""; - public ResponsiveBreakpoint() { put("create_derived", true); } @@ -22,33 +17,21 @@ public ResponsiveBreakpoint createDerived(boolean createDerived) { } public Transformation transformation() { - return transformation; + return (Transformation) opt("transformation"); } public ResponsiveBreakpoint transformation(Transformation transformation) { - this.transformation = transformation; - updateTransformationKey(); + put("transformation", transformation); return this; } - public ResponsiveBreakpoint format(String format) { - this.format = format; - updateTransformationKey(); + put("format", format); return this; } public String format() { - return format; - } - - private synchronized void updateTransformationKey() { - String transformationStr = transformation == null ? "" : transformation.generate(); - if (StringUtils.isNotBlank(format)){ - transformationStr += "/" + format; - } - - put("transformation", transformationStr); + return optString("format"); } public int maxWidth() { 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 bbce143a..8d595a25 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 @@ -502,16 +502,22 @@ public void testFilenameOption() throws Exception { @Test public void testResponsiveBreakpoints() throws Exception { - ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint().maxImages(2).createDerived(false).format("gif"); + ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint() + .createDerived(true) + .maxImages(2) + .transformation(new Transformation().angle(90)) + .format("gif"); // A single breakpoint Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("responsive_breakpoints", - breakpoint, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) - )); + breakpoint, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + java.util.ArrayList breakpointsResponse = (java.util.ArrayList) result.get("responsive_breakpoints"); - java.util.ArrayList breakpoints = (java.util.ArrayList) ((Map) breakpointsResponse.get(0)).get("breakpoints"); - assertEquals(2, breakpoints.size()); + Map map = (Map) breakpointsResponse.get(0); + + java.util.ArrayList breakpoints = (java.util.ArrayList) map.get("breakpoints"); assertTrue(((Map) breakpoints.get(0)).get("url").toString().endsWith("gif")); + assertEquals("a_90", map.get("transformation")); // check again with transformation + format breakpoint.transformation(new Transformation().effect("sepia")); From 2e0441efb59d8641caaab9edbdd80e92ca4e9be6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 9 May 2018 14:55:41 +0300 Subject: [PATCH 361/592] Add int overload to `TextLayer.letterSpacing()` --- .../main/java/com/cloudinary/transformation/TextLayer.java | 5 +++++ .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index 73416b98..206f078c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -77,6 +77,11 @@ public TextLayer letterSpacing(String letterSpacing) { return getThis(); } + public TextLayer letterSpacing(int letterSpacing) { + this.letterSpacing = String.valueOf(letterSpacing); + return getThis(); + } + public TextLayer lineSpacing(Integer lineSpacing) { this.lineSpacing = lineSpacing; return getThis(); 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 bb6fc117..85403b07 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -994,6 +994,9 @@ public void testOverlayOptions() { new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing("4").lineSpacing(3), "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%252C%20Nice%20to%20meet%20you%3F", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontWeight("bold").fontStyle("italic").letterSpacing(4).lineSpacing(3), + "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt", From 252855dd1c1f10fc23671acf7df5385aa0ffd4be Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 10 May 2018 10:20:49 +0300 Subject: [PATCH 362/592] Cleanup upload preset from `testGetUploadPreset` (#129) Fix `testGetUploadPreset()` (cleanup uploaded preset) --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 ++ 1 file changed, 2 insertions(+) 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 ea534e65..a80232d7 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 @@ -637,6 +637,8 @@ public void testGetUploadPreset() throws Exception { assertArrayEquals(tags, outTags); Map outContext = (Map) settings.get("context"); assertEquals(context, outContext); + + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); } @Test From 68129080c13a47ca552afad5dacba3bca3ae571e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 13 May 2018 09:26:35 +0300 Subject: [PATCH 363/592] Ignore the staging-test branch in CI tests --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2329f76c..f27b6890 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,10 @@ env: - MODULE=http43 - MODULE=http44 +branches: + except: + - staging-test + # ciTest is configured to skip the various timeout tests that don't work in travis script: ./gradlew clean ciTest -p cloudinary-${MODULE} -i From d4e157401fe0a0317496a6a179b7b24b677dafd6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 14 May 2018 09:55:54 +0300 Subject: [PATCH 364/592] Fix Api list tags test - verify the list instead of specific tags --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 a80232d7..66bd4b99 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 @@ -354,9 +354,10 @@ public void test09aDeleteResourcesByTags() throws Exception { @Test public void test10Tags() throws Exception { // should allow listing tags - Map result = api.tags(ObjectUtils.asMap("max_results", 500)); + Map result = api.tags(ObjectUtils.asMap("max_results", 10)); List tags = (List) result.get("tags"); - assertThat(tags, hasItem(API_TAG)); + assertNotNull(tags); + assertTrue(tags.size() > 0); } @Test From 00be4a57762dc9d78f86f0fbe289a90f1b94d70f Mon Sep 17 00:00:00 2001 From: d-mendoza <30640721+d-mendoza@users.noreply.github.com> Date: Tue, 26 Jun 2018 02:00:46 -0700 Subject: [PATCH 365/592] Add support of 'auto' value for 'start_offset' transformation parameter (#132) * Add support of `auto` value for `start_offset` transformation parameter --- .../src/main/java/com/cloudinary/Transformation.java | 11 +++++++++-- .../test/java/com/cloudinary/test/CloudinaryTest.java | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index fd0aa0b0..b213d84a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -589,11 +589,11 @@ public String generate(Map options) { String flags = StringUtils.join(ObjectUtils.asArray(options.get("flags")), "."); String duration = normRangeValue(options.get("duration")); - String startOffset = normRangeValue(options.get("start_offset")); + String startOffset = normAutoRangeValue(options.get("start_offset")); String endOffset = normRangeValue(options.get("end_offset")); String[] offset = splitRange(options.get("offset")); if (offset != null) { - startOffset = normRangeValue(offset[0]); + startOffset = normAutoRangeValue(offset[0]); endOffset = normRangeValue(offset[1]); } @@ -797,6 +797,13 @@ private static String normRangeValue(Object objectValue) { return matcher.group(1) + modifier; } + private static String normAutoRangeValue(Object objectValue) { + if ("auto".equals(objectValue)) { + return objectValue.toString(); + } + return normRangeValue(objectValue); + } + private static String processVideoCodecParam(Object param) { StringBuilder outParam = new StringBuilder(); if (param instanceof String) { 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 85403b07..b207e537 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -728,6 +728,9 @@ public void testStartOffset() { actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffsetPercent(2.63)) .generate("video_id"); assertEquals(VIDEO_UPLOAD_PATH + "so_2.63p/video_id", actual); + actual = cloudinary.url().resourceType("video").transformation(new Transformation().startOffset("auto")) + .generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "so_auto/video_id", actual); } @Test From 854496148add8abdc2faaa25cc6e4669ff61ec47 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 1 Jul 2018 13:00:40 +0300 Subject: [PATCH 366/592] Update gradle for java 7 TLS fix (https://github.com/gradle/gradle/issues/5740) --- gradle/wrapper/gradle-wrapper.jar | Bin 54712 -> 54712 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9e6e3a879ee6f094d09074132c95608a4e11fc52..6a36b274071997529d25912f7b5a845267de6e82 100644 GIT binary patch delta 28 hcmdn7nt8`+<_(_?v*blR^x6FFaFHOGH96<14*=5Q4l@7% delta 28 hcmdn7nt8`+<_(_?vzVoP^4k3EaFHOGH96<14*<__4fOy3 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a3200f62..4c050a70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jul 18 12:33:44 IDT 2017 +#Sun Jul 01 11:19:29 IDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip From 9b7c7e50c4f4751c7a19098f0f25f73f924c79db Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 10 Jul 2018 12:33:52 +0300 Subject: [PATCH 367/592] Keep original filename in `uploadLarge` before sending the InputStream --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 7 ++++++- .../java/com/cloudinary/test/AbstractUploaderTest.java | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 3108ff0b..d32cf7f6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -8,7 +8,6 @@ import java.io.*; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; @SuppressWarnings({"rawtypes", "unchecked"}) @@ -117,10 +116,12 @@ public Map uploadLarge(Object file, Map options, int bufferSize, long offset, St InputStream input; long length = -1; boolean remote = false; + String filename = null; if (file instanceof InputStream) { input = (InputStream) file; } else if (file instanceof File) { length = ((File) file).length(); + filename = ((File) file).getName(); input = new FileInputStream((File) file); } else if (file instanceof byte[]) { length = ((byte[]) file).length; @@ -132,6 +133,7 @@ public Map uploadLarge(Object file, Map options, int bufferSize, long offset, St } else { File f = new File(file.toString()); length = f.length(); + filename = f.getName(); input = new FileInputStream(f); } } @@ -140,6 +142,9 @@ public Map uploadLarge(Object file, Map options, int bufferSize, long offset, St if (remote) { result = upload(file, options); } else { + if (!options.containsKey("filename") && StringUtils.isNotBlank(filename)) { + options.put("filename", filename); + } result = uploadLargeParts(input, options, bufferSize, length, offset, uniqueUploadId, progressCallback); } return 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 8d595a25..c1c07f68 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 @@ -461,10 +461,11 @@ public void testUploadLarge() throws Exception { String[] tags = new String[]{"upload_large_tag_" + SUFFIX, SDK_TEST_TAG, UPLOADER_TAG}; - Map resource = cloudinary.uploader().uploadLarge(temp, asMap("resource_type", "raw", "chunk_size", 5243000, "tags", tags)); + Map resource = cloudinary.uploader().uploadLarge(temp, asMap("use_filename", true, "resource_type", "raw", "chunk_size", 5243000, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); 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)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); From 69e50ce2bcc4930285b4bce94bfd1f745cfed362 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 10 Jul 2018 14:01:26 +0300 Subject: [PATCH 368/592] Fix content range header in chunked upload (force US locale) --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index d32cf7f6..c6e17397 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -8,6 +8,7 @@ import java.io.*; import java.util.Arrays; import java.util.HashMap; +import java.util.Locale; import java.util.Map; @SuppressWarnings({"rawtypes", "unchecked"}) @@ -194,7 +195,7 @@ private Map uploadLargeParts(InputStream input, Map options, int bufferSize, lon System.arraycopy(buffer, 0, finalBuffer, 0, currentBufferSize); buffer = finalBuffer; } - String range = String.format("bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); + String range = String.format(Locale.US, "bytes %d-%d/%d", currentLoc, currentLoc + currentBufferSize - 1, length); extraHeaders.put("Content-Range", range); Map sentParams = new HashMap(); sentParams.putAll(params); From 8fabd29ee4f5d4a604b3318fe026b29b1f71b8bf Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 22 Jul 2018 12:45:45 +0300 Subject: [PATCH 369/592] Version 1.19.0 --- CHANGELOG.md | 23 +++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19dcbdd3..d65846a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,27 @@ +1.19.0 / 2018-07-22 +=================== + +New functionality +----------------- + + * Add support of `auto` value for `start_offset` transformation parameter + * Feature/keyframe interval support + +Other changes +------------- + + * Fix content range header in chunked upload (force US locale) + * Keep original filename in `uploadLarge` before sending the InputStream + * Update gradle for java 7 TLS fix (https://github.com/gradle/gradle/issues/5740) + * Fix Api list tags test - verify the list instead of specific tags + * Cleanup upload preset from `testGetUploadPreset` (#129) + * Add int overload to `TextLayer.letterSpacing()` + * Fix responsive breakpoint format field implementation + * Separate modules to run on different travis jobs. + * Remove `test02Resources` test (broken and unnecessary). + * Fix raw convert error message test + 1.18.0 / 2018-03-15 =================== diff --git a/README.md b/README.md index 04c8841a..4aa630a7 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.18.0 + 1.19.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.18.0/cloudinary-core-1.18.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.18.0/cloudinary-http44-1.18.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.19.0/cloudinary-core-1.19.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.19.0/cloudinary-http44-1.19.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index e922ac61..a5346a29 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.18.0"; + public final static String VERSION = "1.19.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 4e347032..187b47f9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.18.0 +version=1.19.0 From 324c2f476b4d27d1e83324500428d694cac109eb Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 16 Sep 2018 10:48:53 +0300 Subject: [PATCH 370/592] Add support for web assembly and lambda functions in transformations --- .../main/java/com/cloudinary/BaseParam.java | 22 +++++++++++++ .../java/com/cloudinary/CustomAction.java | 31 +++++++++++++++++++ .../java/com/cloudinary/Transformation.java | 9 ++++++ .../com/cloudinary/utils/Base64Coder.java | 4 +++ .../com/cloudinary/test/CloudinaryTest.java | 9 ++++++ 5 files changed, 75 insertions(+) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/BaseParam.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/CustomAction.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/BaseParam.java b/cloudinary-core/src/main/java/com/cloudinary/BaseParam.java new file mode 100644 index 00000000..a8eb0482 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/BaseParam.java @@ -0,0 +1,22 @@ +package com.cloudinary; + +import com.cloudinary.utils.StringUtils; + +import java.util.List; + +public class BaseParam { + private String param; + + protected BaseParam(List components) { + this.param = StringUtils.join(components, ":"); + } + + protected BaseParam(String... components) { + this.param = StringUtils.join(components, ":"); + } + + @Override + public String toString() { + return param; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java b/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java new file mode 100644 index 00000000..6ca5a8e4 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java @@ -0,0 +1,31 @@ +package com.cloudinary; + +import com.cloudinary.utils.Base64Coder; + +/** + * Helper class to generate a custom action params to be used in {@link Transformation#customAction(CustomAction)}. + */ +public class CustomAction extends BaseParam{ + + private CustomAction(String... components) { + super(components); + } + + /** + * Generate a web-assembly custom action param to send to {@link Transformation#customAction(CustomAction)} + * @param publicId The public id of the web-assembly file + * @return A new instance of custom action param + */ + public static CustomAction wasm(String publicId){ + return new CustomAction("wasm", publicId); + } + + /** + * Generate a remote lambda custom action param to send to {@link Transformation#customAction(CustomAction)} + * @param url The public url of the aws lambda function + * @return A new instance of custom action param + */ + public static CustomAction remote(String url){ + return new CustomAction("remote", Base64Coder.encodeURLSafeString(url)); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index b213d84a..72c12ec1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -634,6 +634,7 @@ public String generate(Map options) { "dl", "delay", "dn", "density", "f", "fetch_format", + "fn", "custom_action", "fps", "fps", "g", "gravity", "l", "overlay", @@ -841,4 +842,12 @@ public T variables(Expression...variables) { return param("variables", variables); } + /** + * Set a custom action, such as a call to a lambda function or a web-assembly function. + * @param action The custom action to perform, see {@link CustomAction}. + * @return The transformation for chaining + */ + public T customAction(CustomAction action) { + return param("custom_action", action.toString()); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java index 97c21b0f..aed228ab 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Coder.java @@ -280,6 +280,10 @@ public static byte[] decode(char[] in, int iOff, int iLen) { private Base64Coder() { } + public static String encodeURLSafeString(String s) { + return encodeURLSafeString(s.getBytes()); + } + public static String encodeURLSafeString(byte[] digest) { char[] encode = encode(digest); for (int i = 0; i < encode.length; i++) { 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 b207e537..f9d2801f 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -25,6 +25,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.cloudinary.CustomAction.remote; +import static com.cloudinary.CustomAction.wasm; import static com.cloudinary.utils.ObjectUtils.asMap; import static com.cloudinary.utils.ObjectUtils.emptyMap; import static org.junit.Assert.*; @@ -1101,6 +1103,13 @@ public void testKeyframeInterval(){ assertEquals("", new Transformation().keyframeInterval(null).generate()); } + @Test + public void testCustomAction(){ + assertEquals("fn_wasm:blur_wasm", new Transformation().customAction(wasm("blur_wasm")).generate()); + assertEquals("fn_remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlBY3Rpb24=", + new Transformation().customAction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryAction")).generate()); + } + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { From 62040de3f958f4dbe5bca4aba0587d70b6103e8e Mon Sep 17 00:00:00 2001 From: Yakir Perlin Date: Tue, 24 Jul 2018 18:50:50 +0300 Subject: [PATCH 371/592] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4aa630a7..9c7fc4e0 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,10 @@ Contact us [https://cloudinary.com/contact](https://cloudinary.com/contact) Stay tuned for updates, tips and tutorials: [Blog](https://cloudinary.com/blog), [Twitter](https://twitter.com/cloudinary), [Facebook](https://www.facebook.com/Cloudinary). +## Join the Community ########################################################## + +Impact the product, hear updates, test drive new features and more! Join [here](https://www.facebook.com/groups/CloudinaryCommunity). + ## License ####################################################################### From 3ef9eba2a2001cfffdbd37a0e369b06aab1abd16 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 26 Jul 2018 11:21:52 +0300 Subject: [PATCH 372/592] Fix url encoding for AuthToken generation --- .../main/java/com/cloudinary/AuthToken.java | 53 +++++++--------- .../com/cloudinary/utils/StringUtils.java | 60 ++++++++++++++++--- .../java/com/cloudinary/AuthTokenTest.java | 2 +- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 9bef9e95..2b8d4708 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -7,6 +7,7 @@ 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.*; @@ -31,6 +32,7 @@ public class AuthToken { private String acl; private long duration; private boolean isNullToken = false; + private static final Pattern UNSAFE_URL_CHARS_PATTERN = Pattern.compile("[ \"#%&'/:;<=>?@\\[\\\\\\]^`{|}~]"); public AuthToken() { } @@ -46,10 +48,10 @@ public AuthToken(String key) { */ public AuthToken(Map options) { if (options != null) { - this.tokenName = ObjectUtils.asString( options.get("tokenName"), this.tokenName); + this.tokenName = ObjectUtils.asString(options.get("tokenName"), this.tokenName); this.key = (String) options.get("key"); this.startTime = ObjectUtils.asLong(options.get("startTime"), 0L); - this.expiration = ObjectUtils.asLong(options.get("expiration"),0L); + this.expiration = ObjectUtils.asLong(options.get("expiration"), 0L); this.ip = (String) options.get("ip"); this.acl = (String) options.get("acl"); this.duration = ObjectUtils.asLong(options.get("duration"), 0L); @@ -59,6 +61,7 @@ public AuthToken(Map options) { /** * Create a new AuthToken configuration overriding the default token name. + * * @param tokenName the name of the token. must be supported by the server. * @return this */ @@ -91,6 +94,7 @@ public AuthToken expiration(long expiration) { /** * Set the ip of the client + * * @param ip * @return this */ @@ -101,6 +105,7 @@ public AuthToken ip(String ip) { /** * Define an ACL for a cookie token + * * @param acl * @return this */ @@ -132,6 +137,7 @@ public String generate() { /** * Generate a URL token for the given URL. + * * @param url the URL to be authorized * @return a URL token */ @@ -168,32 +174,18 @@ public String generate(String url) { /** * Escape url using lowercase hex code + * * @param url a url string * @return escaped url */ private String escapeToLower(String url) { - String escaped; - String encodedUrl = null; - try { - encodedUrl = URLEncoder.encode(url, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Cannot escape string.", e); - } - StringBuilder sb= new StringBuilder(encodedUrl); - String regex= "%.."; - Pattern p = Pattern.compile(regex); // Create the pattern. - Matcher matcher = p.matcher(sb); // Create the matcher. - while (matcher.find()) { - String buf= sb.substring(matcher.start(), matcher.end()).toLowerCase(); - sb.replace(matcher.start(), matcher.end(), buf); - } - escaped = sb.toString(); - return escaped; + String encodedUrl = StringUtils.urlEncode(url, UNSAFE_URL_CHARS_PATTERN, Charset.forName("UTF-8")); + return encodedUrl; } - /** * Create a copy of this AuthToken + * * @return a new AuthToken object */ public AuthToken copy() { @@ -209,11 +201,12 @@ public AuthToken copy() { /** * Merge this token with another, creating a new token. Other's members who are not null or 0 will override this object's members. + * * @param other the token to merge from * @return a new token */ public AuthToken merge(AuthToken other) { - if(other.equals(NULL_AUTH_TOKEN)) { + if (other.equals(NULL_AUTH_TOKEN)) { // NULL_AUTH_TOKEN can't merge return other; } @@ -250,16 +243,16 @@ private AuthToken setNull() { @Override public boolean equals(Object o) { - if(o instanceof AuthToken) { + if (o instanceof AuthToken) { AuthToken other = (AuthToken) o; - return (isNullToken && other.isNullToken) || + return (isNullToken && other.isNullToken) || (key == null ? other.key == null : key.equals(other.key)) && - tokenName.equals(other.tokenName) && - startTime == other.startTime && - expiration == other.expiration && - duration == other.duration && - (ip == null ? other.ip == null : ip.equals(other.ip)) && - (acl == null ? other.acl == null : acl.equals(other.acl)); + tokenName.equals(other.tokenName) && + startTime == other.startTime && + expiration == other.expiration && + duration == other.duration && + (ip == null ? other.ip == null : ip.equals(other.ip)) && + (acl == null ? other.acl == null : acl.equals(other.acl)); } else { return false; } @@ -267,7 +260,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - if(isNullToken) { + if (isNullToken) { return 0; } else { return Arrays.asList(tokenName, startTime, expiration, duration, ip, acl).hashCode(); 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 3d45f24a..82cdb3b1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -3,15 +3,19 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.Collection; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class StringUtils { public static final String EMPTY = ""; /** * Join a list of Strings - * @param list strings to join + * + * @param list strings to join * @param separator the separator to insert between the strings * @return a string made of the strings in list separated by separator */ @@ -25,7 +29,8 @@ public static String join(List list, String separator) { /** * Join a array of Strings - * @param array strings to join + * + * @param array strings to join * @param separator the separator to insert between the strings * @return a string made of the strings in array separated by separator */ @@ -38,8 +43,9 @@ public static String join(Object[] array, String separator) { /** * Join a collection of Strings + * * @param collection strings to join - * @param separator the separator to insert between the strings + * @param separator the separator to insert between the strings * @return a string made of the strings in collection separated by separator */ public static String join(Collection collection, String separator) { @@ -51,10 +57,11 @@ public static String join(Collection collection, String separator) { /** * Join a array of Strings from startIndex to endIndex - * @param array strings to join - * @param separator the separator to insert between the strings + * + * @param array strings to join + * @param separator the separator to insert between the strings * @param startIndex the string to start from - * @param endIndex the last string to join + * @param endIndex the last string to join * @return a string made of the strings in array separated by separator */ public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { @@ -87,6 +94,7 @@ public static String join(final Object[] array, String separator, final int star /** * Convert an array of bytes to a string of hex values + * * @param bytes bytes to convert * @return a string of hex values. */ @@ -102,6 +110,7 @@ public static String encodeHexString(byte[] bytes) { /** * Convert a string of hex values to an array of bytes + * * @param s a string of two digit Hex numbers. The length of string to parse must be even. * @return bytes representation of the string */ @@ -125,7 +134,7 @@ public static byte[] hexStringToByteArray(String s) { * * @param input The String to escape * @return The escaped String - * @see HtmlEscape#escapeTextArea(String) + * @see HtmlEscape#escapeTextArea(String) */ public static String escapeHtml(String input) { return HtmlEscape.escapeTextArea(input); @@ -133,6 +142,7 @@ public static String escapeHtml(String input) { /** * Verify that the input has non whitespace characters in it + * * @param input a String-like object * @return true if input has non whitespace characters in it */ @@ -143,6 +153,7 @@ public static boolean isNotBlank(Object input) { /** * Verify that the input has non whitespace characters in it + * * @param input a String * @return true if input has non whitespace characters in it */ @@ -152,6 +163,7 @@ public static boolean isNotBlank(String input) { /** * Verify that the input has no characters + * * @param input a string * @return true if input is null or has no characters */ @@ -161,7 +173,8 @@ public static boolean isEmpty(String input) { /** * Verify that the input is an empty string or contains only whitespace characters.
- * see {@link Character#isWhitespace(char)} + * see {@link Character#isWhitespace(char)} + * * @param input a string * @return true if input is an empty string or contains only whitespace characters */ @@ -180,6 +193,7 @@ public static boolean isBlank(String input) { /** * Read the entire input stream in 1KB chunks + * * @param in input stream to read from * @return a String generated from the input stream * @throws IOException thrown by the input stream @@ -198,4 +212,34 @@ public static boolean isRemoteUrl(String file) { return file.matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)"); } + /** + * Replaces the unsafe characters in url with url-encoded values. + * This is based on {@link java.net.URLEncoder#encode(String, String)} + * @param url The url to encode + * @param unsafe Regex pattern of unsafe caracters + * @param charset + * @return An encoded url string + */ + public static String urlEncode(String url, Pattern unsafe, Charset charset) { + StringBuffer sb = new StringBuffer(url.length()); + Matcher matcher = unsafe.matcher(url); + while (matcher.find()) { + String str = matcher.group(0); + byte[] bytes = str.getBytes(charset); + StringBuilder escaped = new StringBuilder(str.length() * 3); + + for (byte aByte : bytes) { + escaped.append('%'); + char ch = Character.forDigit((aByte >> 4) & 0xF, 16); + escaped.append(ch); + ch = Character.forDigit(aByte & 0xF, 16); + escaped.append(ch); + } + + matcher.appendReplacement(sb, Matcher.quoteReplacement(escaped.toString().toLowerCase())); + } + + matcher.appendTail(sb); + return sb.toString(); + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index b103f750..86ce6164 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -83,7 +83,7 @@ public void testAuthenticatedUrl() { 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=7d276841d70c4ecbd0708275cd6a82e1f08e47838fbb0bceb2538e06ddfa3029", url); + assertEquals(message,"http://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)) From 93107e59ed7d55f1142c46ca46895f6ac43c9084 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Aug 2018 16:53:11 +0300 Subject: [PATCH 373/592] Improve performance of `url.generate()` method. * Replace regular expression with custom string methods, where possible. * Pre-compile regular expression patterns. * Replace `SortedMap` with `HashMap`, and sort once after `put` calls. * Add tests for new string methods. --- .../java/com/cloudinary/Transformation.java | 141 +++++++++------- .../src/main/java/com/cloudinary/Url.java | 46 +++--- .../transformation/BaseExpression.java | 37 +++-- .../com/cloudinary/utils/StringUtils.java | 156 ++++++++++++++++++ .../test/java/com/cloudinary/UtilTest.java | 98 ++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 71 +++++--- gradle/wrapper/gradle-wrapper.jar | Bin 54712 -> 54417 bytes gradle/wrapper/gradle-wrapper.properties | 1 - 8 files changed, 427 insertions(+), 123 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 72c12ec1..0b04cef7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -12,7 +12,7 @@ import com.cloudinary.utils.StringUtils; @SuppressWarnings({"rawtypes", "unchecked"}) -public class Transformation implements Serializable{ +public class Transformation implements Serializable { public static final String VAR_NAME_RE = "^\\$[a-zA-Z][a-zA-Z0-9]+$"; protected Map transformation; protected List transformations; @@ -27,6 +27,27 @@ public class Transformation implements Serializable{ protected static Map responsiveWidthTransformation = null; private static final Pattern RANGE_VALUE_RE = Pattern.compile("^((?:\\d+\\.)?\\d+)([%pP])?$"); private static final Pattern RANGE_RE = Pattern.compile("^(\\d+\\.)?\\d+[%pP]?\\.\\.(\\d+\\.)?\\d+[%pP]?$"); + private static final String[] SIMPLE_PARAMS = new String[]{ + "ac", "audio_codec", + "af", "audio_frequency", + "bo", "border", + "br", "bit_rate", + "cs", "color_space", + "d", "default_image", + "dl", "delay", + "dn", "density", + "f", "fetch_format", + "fn", "custom_action", + "fps", "fps", + "g", "gravity", + "l", "overlay", + "p", "prefix", + "pg", "page", + "u", "underlay", + "vs", "video_sampling", + "sp", "streaming_profile", + "ki", "keyframe_interval" + }; public Transformation(Transformation transformation) { this(dup(transformation.transformations)); @@ -94,7 +115,7 @@ public T border(String value) { } public T border(int width, String color) { - return param("border", "" + width + "px_solid_" + color.replaceFirst("^#", "rgb:")); + return param("border", "" + width + "px_solid_" + replaceColorPrefix(color)); } public T x(Object value) { @@ -123,6 +144,7 @@ public T gravity(String value) { /** * Set the keyframe interval parameter + * * @param value Interval in seconds * @return The transformation for chaining */ @@ -132,6 +154,7 @@ public T keyframeInterval(float value) { /** * Set the keyframe interval parameter + * * @param value Interval in seconds. * @return The transformation for chaining */ @@ -371,6 +394,7 @@ public T responsiveWidth(boolean value) { /** * Start defining a condition, which will be completed with a call {@link Condition#then()} + * * @return condition */ public Condition ifCondition() { @@ -379,6 +403,7 @@ public Condition ifCondition() { /** * Define a conditional transformation defined by the condition string + * * @param condition a condition string * @return the transformation for chaining */ @@ -389,6 +414,7 @@ public T ifCondition(String condition) { /** * Define a conditional transformation + * * @param expression a condition * @return the transformation for chaining */ @@ -398,6 +424,7 @@ public T ifCondition(Expression expression) { /** * Define a conditional transformation + * * @param condition a condition * @return the transformation for chaining */ @@ -434,6 +461,7 @@ public T endIf() { /** * fps (frames per second) parameter for video + * * @param value Either a single value int or float or a range in the format <start>[-<end>].
* For example, 23-29.7 * @return the transformation for chaining @@ -444,6 +472,7 @@ public T fps(String value) { /** * fps (frames per second) parameter for video + * * @param value the desired fps * @return the transformation for chaining */ @@ -453,6 +482,7 @@ public T fps(double value) { /** * fps (frames per second) parameter for video + * * @param value the desired fps * @return the transformation for chaining */ @@ -460,7 +490,7 @@ public T fps(int value) { return param("fps", new Integer(value)); } - public T streamingProfile(String value){ + public T streamingProfile(String value) { return param("streaming_profile", value); } @@ -515,7 +545,7 @@ public String toString() { public String generate(Iterable optionsList) { List components = new ArrayList(); for (Map options : optionsList) { - if(options.size() > 0){ + if (options.size() > 0) { components.add(generate(options)); } } @@ -549,12 +579,12 @@ public String generate(Map options) { String background = (String) options.get("background"); if (background != null) { - background = background.replaceFirst("^#", "rgb:"); + background = replaceColorPrefix(background); } String color = (String) options.get("color"); if (color != null) { - color = color.replaceFirst("^#", "rgb:"); + color = replaceColorPrefix(color); } List transformations = ObjectUtils.asArray(options.get("transformation")); @@ -600,66 +630,18 @@ public String generate(Map options) { String videoCodec = processVideoCodecParam(options.get("video_codec")); String dpr = ObjectUtils.asString(options.get("dpr"), null == defaultDPR ? null : defaultDPR.toString()); - SortedMap params = new TreeMap(); - params.put("a", Expression.normalize(angle)); - params.put("ar", Expression.normalize( options.get("aspect_ratio"))); - params.put("b", background); - params.put("c", crop); - params.put("co", color); - params.put("dpr", Expression.normalize(dpr)); - params.put("du", duration); - params.put("e", Expression.normalize( options.get("effect"))); - params.put("eo", endOffset); - params.put("fl", flags); - params.put("h", Expression.normalize(height)); - params.put("o", Expression.normalize( options.get("opacity"))); - params.put("q", Expression.normalize( options.get("quality"))); - params.put("r", Expression.normalize( options.get("radius"))); - params.put("so", startOffset); - params.put("t", namedTransformation); - params.put("vc", videoCodec); - params.put("w", Expression.normalize(width)); - params.put("x", Expression.normalize( options.get("x"))); - params.put("y", Expression.normalize( options.get("y"))); - params.put("z", Expression.normalize( options.get("zoom"))); - - String[] simple_params = new String[]{ - "ac", "audio_codec", - "af", "audio_frequency", - "bo", "border", - "br", "bit_rate", - "cs", "color_space", - "d", "default_image", - "dl", "delay", - "dn", "density", - "f", "fetch_format", - "fn", "custom_action", - "fps", "fps", - "g", "gravity", - "l", "overlay", - "p", "prefix", - "pg", "page", - "u", "underlay", - "vs", "video_sampling", - "sp", "streaming_profile", - "ki", "keyframe_interval" - }; - - for (int i = 0; i < simple_params.length; i += 2) { - params.put(simple_params[i], ObjectUtils.asString(options.get(simple_params[i + 1]))); - } List components = new ArrayList(); String ifValue = (String) options.get("if"); - if(ifValue != null){ + if (ifValue != null) { components.add(0, "if_" + Expression.normalize(ifValue)); } SortedSet varParams = new TreeSet(); - for( Object k: options.keySet()) { + for (Object k : options.keySet()) { String key = (String) k; - if(key.matches(VAR_NAME_RE)) { + if (StringUtils.isVariable(key)) { varParams.add(key + "_" + ObjectUtils.asString(options.get(k))); } } @@ -673,6 +655,36 @@ public String generate(Map options) { components.add(variables); } + Map params = new HashMap<>(64); + + params.put("a", Expression.normalize(angle)); + params.put("ar", Expression.normalize(options.get("aspect_ratio"))); + params.put("b", background); + params.put("c", crop); + params.put("co", color); + params.put("dpr", Expression.normalize(dpr)); + params.put("du", duration); + params.put("e", Expression.normalize(options.get("effect"))); + params.put("eo", endOffset); + params.put("fl", flags); + params.put("h", Expression.normalize(height)); + params.put("o", Expression.normalize(options.get("opacity"))); + params.put("q", Expression.normalize(options.get("quality"))); + params.put("r", Expression.normalize(options.get("radius"))); + params.put("so", startOffset); + params.put("t", namedTransformation); + params.put("vc", videoCodec); + params.put("w", Expression.normalize(width)); + params.put("x", Expression.normalize(options.get("x"))); + params.put("y", Expression.normalize(options.get("y"))); + params.put("z", Expression.normalize(options.get("zoom"))); + + for (int i = 0; i < SIMPLE_PARAMS.length; i += 2) { + params.put(SIMPLE_PARAMS[i], ObjectUtils.asString(options.get(SIMPLE_PARAMS[i + 1]))); + } + + params = new TreeMap<>(params); + for (Map.Entry param : params.entrySet()) { if (StringUtils.isNotBlank(param.getValue())) { components.add(param.getKey() + "_" + param.getValue()); @@ -702,12 +714,16 @@ public String generate(Map options) { return StringUtils.join(transformations, "/"); } + private String replaceColorPrefix(String color) { + return StringUtils.replaceIfFirstChar(color, '#', "rgb:"); + } + private String processVar(Expression[] variables) { - if(variables == null) { + if (variables == null) { return null; } List s = new ArrayList(variables.length); - for(Expression variable: variables) { + for (Expression variable : variables) { s.add(variable.toString()); } return StringUtils.join(s, ","); @@ -715,6 +731,7 @@ private String processVar(Expression[] variables) { /** * Check if the value is a float >= 1 + * * @param value * @return true if the value is a float >= 1 */ @@ -825,7 +842,8 @@ private static String processVideoCodecParam(Object param) { /** * Add a variable assignment. Each call to this method will add a new variable assignments, but the order of the assignments may change. To enforce a particular order, use {@link #variables(Expression...)} - * @param name the name of the variable + * + * @param name the name of the variable * @param value the value to assign to the variable * @return this for chaining */ @@ -835,10 +853,11 @@ public T variable(String name, Object value) { /** * Add a sequence of variable assignments. The order of the assignments will be honored. + * * @param variables variable expressions * @return this for chaining */ - public T variables(Expression...variables) { + public T variables(Expression... variables) { return param("variables", variables); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index de10d283..deadd74a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; @@ -57,7 +56,8 @@ public Url clone() { cloned.fallbackContent = this.fallbackContent; cloned.format = this.format; cloned.posterSource = this.posterSource; - if (this.posterTransformation != null) cloned.posterTransformation = new Transformation(this.posterTransformation); + if (this.posterTransformation != null) + cloned.posterTransformation = new Transformation(this.posterTransformation); if (this.posterUrl != null) cloned.posterUrl = this.posterUrl.clone(); cloned.publicId = this.publicId; cloned.resourceType = this.resourceType; @@ -216,24 +216,23 @@ public Url signed(boolean signUrl) { /** * Set the authorization token. If authToken has already been set the parameter is merged with the current value unless the parameter value is null or NULL_AUTH_TOKEN.

- * For example, to generate an authorized URL with a different duration:
- *
+     * For example, to generate an authorized URL with a different duration:
+ *
      *  {@code
      *   cloudinary.config.authToken = new AuthToken(KEY).duration(500);
      *   // later...
      *   cloudinary.url().signed(true).authToken(new AuthToken().duration(300))
      *                   .type("authenticated").version("1486020273").generate("sample.jpg");
      *  }
-     *
+ *
+ * * @param authToken an authorization token object * @return this - * - * */ public Url authToken(AuthToken authToken) { if (this.authToken == null) { this.authToken = authToken; - } else if(authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { + } else if (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { this.authToken = authToken; } else { this.authToken = this.authToken.merge(authToken); @@ -339,26 +338,26 @@ public String generate(String source) { } } - if (source.toLowerCase(Locale.US).matches("^https?:/.*")) { + boolean httpSource = StringUtils.isHttpUrl(source); + if (httpSource) { if (StringUtils.isEmpty(type) || "asset".equals(type)) { return source; } } - if (type != null && type.equals("fetch") && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); this.format = null; } + String transformationStr = transformation().generate(); String signature = ""; - - String[] finalizedSource = finalizeSource(source, format, urlSuffix); + String[] finalizedSource = finalizeSource(source, httpSource, format, urlSuffix); source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; - if (sourceToSign.contains("/") && !sourceToSign.matches("v[0-9]+.*") && !sourceToSign.matches("https?:/.*") && StringUtils.isEmpty(version)) { + if (sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && !httpSource && StringUtils.isEmpty(version)) { version = "1"; } @@ -377,7 +376,8 @@ public String generate(String source) { } String toSign = StringUtils.join(new String[]{transformationStr, sourceToSign}, "/"); - toSign = toSign.replaceAll("^/+", "").replaceAll("([^:])\\/+", "$1/"); + toSign = StringUtils.removeStartingChars(toSign, '/'); + toSign = StringUtils.mergeSlashesInUrl(toSign); byte[] digest = md.digest(cloudinary.getUTF8Bytes(toSign + this.config.apiSecret)); signature = Base64Coder.encodeURLSafeString(digest); @@ -389,7 +389,8 @@ public String generate(String source) { String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); String prefix = unsignedDownloadUrlPrefix(source, config.cloudName, config.privateCdn, config.cdnSubdomain, config.secureCdnSubdomain, config.cname, config.secure, config.secureDistribution); - String url = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/").replaceAll("([^:])\\/+", "$1/"); + String join = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/"); + String url = StringUtils.mergeSlashesInUrl(join); if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { try { @@ -403,12 +404,11 @@ public String generate(String source) { return url; } - private String[] finalizeSource(String source, String format, String urlSuffix) { + private String[] finalizeSource(String source, boolean isHttpSource, String format, String urlSuffix) { + source = StringUtils.mergeSlashesInUrl(source); String[] result = new String[2]; - source = source.replaceAll("([^:])//", "\1/"); - String sourceToSign; - if (source.toLowerCase().matches("^https?:/.*")) { + if (isHttpSource) { source = SmartUrlEncoder.encode(source); sourceToSign = source; } else { @@ -419,9 +419,7 @@ private String[] finalizeSource(String source, String format, String urlSuffix) } sourceToSign = source; if (StringUtils.isNotBlank(urlSuffix)) { - Pattern pattern = Pattern.compile("[\\./]"); - Matcher matcher = pattern.matcher(urlSuffix); - if (matcher.find()) { + if (urlSuffix.contains(".") || urlSuffix.contains("/")) { throw new IllegalArgumentException("url_suffix should not include . or /"); } source = source + "/" + urlSuffix; @@ -448,13 +446,13 @@ public String finalizeResourceType(String resourceType, String type, String urlS } else if (resourceType.equals("image") && type.equals("private")) { resourceType = "private_images"; type = null; - } else if (resourceType.equals("image") && type.equals("authenticated")){ + } else if (resourceType.equals("image") && type.equals("authenticated")) { resourceType = "authenticated_images"; type = null; } else if (resourceType.equals("raw") && type.equals("upload")) { resourceType = "files"; type = null; - } else if (resourceType.equals("video") && type.equals("upload")){ + } else if (resourceType.equals("video") && type.equals("upload")) { resourceType = "videos"; type = null; } else { diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index cf0c98d7..b9f6a54f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -10,10 +10,11 @@ /** * Defines an expression used in transformation parameter values + * * @param Children must define themselves as T */ public abstract class BaseExpression { - public static final Map OPERATORS = ObjectUtils.asMap( + public static final Map OPERATORS = ObjectUtils.asMap( "=", "eq", "!=", "ne", "<", "lt", @@ -27,7 +28,7 @@ public abstract class BaseExpression { "+", "add", "-", "sub" ); - public static final Map PREDEFINED_VARS = ObjectUtils.asMap( + public static final Map PREDEFINED_VARS = ObjectUtils.asMap( "width", "w", "height", "h", "initialWidth", "iw", @@ -47,7 +48,7 @@ public abstract class BaseExpression { "pageY", "py" ); - private static final String PATTERN = getpattern(); + private static final Pattern PATTERN = getPattern(); protected List expressions = null; protected Transformation parent = null; @@ -58,19 +59,23 @@ protected BaseExpression() { /** * Normalize an expression string, replace "nice names" with their coded values and spaces with "_". - * @param expresion an expression + * + * @param expression an expression * @return a parsed expression */ - public static String normalize(Object expresion) { - - String replacement; - if (expresion == null) { + public static String normalize(Object expression) { + if (expression == null) { return null; } - String conditionStr = String.valueOf(expresion); - conditionStr = conditionStr.replaceAll("[ _]+", "_"); - Pattern replaceRE = Pattern.compile(PATTERN); - Matcher matcher = replaceRE.matcher(conditionStr); + + // If it's a number it's not an expression + if (expression instanceof Number){ + return String.valueOf(expression); + } + + String replacement; + String conditionStr = StringUtils.mergeToSingleUnderscore(String.valueOf(expression)); + Matcher matcher = PATTERN.matcher(conditionStr); StringBuffer result = new StringBuffer(conditionStr.length()); while (matcher.find()) { if (OPERATORS.containsKey(matcher.group())) { @@ -89,18 +94,18 @@ public static String normalize(Object expresion) { /** * @return a regex pattern for operators and predefined vars as /((operators)(?=[ _])|variables)/ */ - private static String getpattern() { + private static Pattern getPattern() { String pattern; final ArrayList operators = new ArrayList(OPERATORS.keySet()); Collections.sort(operators, Collections.reverseOrder()); - StringBuffer sb = new StringBuffer("(("); - for(String op: operators) { + StringBuilder sb = new StringBuilder("(("); + for (String op : operators) { sb.append(Pattern.quote(op)).append("|"); } sb.deleteCharAt(sb.length() - 1); sb.append(")(?=[ _])|").append(StringUtils.join(PREDEFINED_VARS.keySet(), "|")).append(")"); pattern = sb.toString(); - return pattern; + return Pattern.compile(pattern); } public Transformation getParent() { 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 82cdb3b1..b846009e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -242,4 +242,160 @@ public static String urlEncode(String url, Pattern unsafe, Charset charset) { matcher.appendTail(sb); return sb.toString(); } + + /** + * Merge all consecutive underscores and spaces into a single underscore, e.g. "ab___c_ _d" -> "ab_c_d" + * + * @param s String to process + * @return The resulting string. + */ + public static String mergeToSingleUnderscore(String s) { + StringBuffer buffer = new StringBuffer(); + boolean inMerge = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == ' ' || c == '_') { + if (!inMerge) { + buffer.append('_'); + } + inMerge = true; + + } else { + inMerge = false; + buffer.append(c); + } + } + + return buffer.toString(); + } + + /** + * Checks whether the String fits the template for a transformation variable - $[a-zA-Z][a-zA-Z0-9]+ + * e.g. $a4, $Bd, $abcdef, etc + * + * @param s The string to test + * @return Whether it's a variable or not + */ + public static boolean isVariable(String s) { + if (s == null || + s.length() < 3 || + !s.startsWith("$") || + !Character.isLetter(s.charAt(1))) { + return false; + } + + // check that the rest of the string is comprised of letters and digits only: + for (int i = 2; i < s.length(); i++) { + char c = s.charAt(i); + if (!Character.isLetterOrDigit(c)) { + return false; + } + } + + return true; + } + + /** + * Replaces the char c in the string S, if it's the first character in the string. + * @param s The string to search + * @param c The character to replace + * @param replacement The string to replace the character in S + * @return The string with the character replaced (or the original string if the char is not found) + */ + public static String replaceIfFirstChar(String s, char c, String replacement) { + return s.charAt(0) == c ? replacement + s.substring(1) : s; + } + + /** + * Check if the given string starts with http:// or https:// + * @param s The string to check + * @return Whether it's an http url or not + */ + public static boolean isHttpUrl(String s) { + String lowerCaseSource = s.toLowerCase(); + return lowerCaseSource.startsWith("https:/") || lowerCaseSource.startsWith("http:/"); + } + + /** + * Remove all consecutive chars c from the beginning of the string + * @param s String to process + * @param c Char to search for + * @return The string stripped from the starting chars. + */ + public static String removeStartingChars(String s, char c) { + int lastToRemove = -1; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == c) { + lastToRemove = i; + continue; + } + + if (s.charAt(i) != c) { + break; + } + } + + if (lastToRemove < 0) return s; + return s.substring(lastToRemove + 1); + } + + /** + * Checks whether the url contains a versioning string (v + number, e.g. v12345) + * @param url The url to check + * @return Whether a version string is contained within the url + */ + public static boolean hasVersionString(String url) { + boolean inVersion = false; + for (int i = 0; i < url.length(); i++) { + char c = url.charAt(i); + if (c == 'v') { + inVersion = true; + } else if (Character.isDigit(c) && inVersion) { + return true; + } else { + inVersion = false; + } + + + } + + return false; + } + + /** + * Merges all occurrences of multiple slashes into a single slash (e.g. "a///b//c/d" -> "a/b/c/d") + * @param url The string to process + * @return The resulting string with merged slashes. + */ + public static String mergeSlashesInUrl(String url) { + StringBuilder builder = new StringBuilder(); + boolean prevIsColon = false; + boolean inMerge = false; + for (int i = 0; i < url.length(); i++) { + char c = url.charAt(i); + if (c == ':') { + prevIsColon = true; + builder.append(c); + } else { + if (c == '/') { + if (prevIsColon) { + builder.append(c); + inMerge = false; + } else { + if (!inMerge) { + builder.append(c); + } + inMerge = true; + } + } else { + inMerge = false; + builder.append(c); + } + + prevIsColon = false; + } + } + + return builder.toString(); + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java index f15e17c4..a8c7b0ba 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -1,6 +1,7 @@ package com.cloudinary; import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONObject; import org.junit.Test; @@ -45,15 +46,108 @@ public void testAccessControlRule() throws ParseException { assertEquals(2, acl.length()); assertEquals(deserializedAcl.get("access_type"), "anonymous"); assertEquals(deserializedAcl.get("start"), START_REFORMATTED); - + acl = AccessControlRule.anonymousUntil(end); assertEquals(2, acl.length()); assertEquals(deserializedAcl.get("access_type"), "anonymous"); assertEquals(deserializedAcl.get("end"), END_REFORMATTED); - + AccessControlRule token = AccessControlRule.token(); assertEquals(1, token.length()); assertEquals("{\"access_type\":\"token\"}", token.toString()); } + + @Test + public void testMergeToSingleUnderscore() { + assertEquals("a_b_c_d", StringUtils.mergeToSingleUnderscore("a_b_c_d")); + assertEquals("a_b_c_d", StringUtils.mergeToSingleUnderscore("a_b_c_ d")); + assertEquals("a_b_c_d", StringUtils.mergeToSingleUnderscore("a_b_c_ _ d")); + assertEquals("_a_b_c_d_", StringUtils.mergeToSingleUnderscore("___ _ a____ b_c_ d _ _")); + assertEquals("a", StringUtils.mergeToSingleUnderscore("a")); + assertEquals("a_", StringUtils.mergeToSingleUnderscore("a___________")); + assertEquals("_a", StringUtils.mergeToSingleUnderscore(" a")); + } + + @Test + public void testIsVariable(){ + assertTrue(StringUtils.isVariable("$a6")); + assertTrue(StringUtils.isVariable("$a64534534")); + assertTrue(StringUtils.isVariable("$ab")); + assertTrue(StringUtils.isVariable("$asdasda")); + assertTrue(StringUtils.isVariable("$a34asd12e")); + + assertFalse(StringUtils.isVariable("$a")); + assertFalse(StringUtils.isVariable("sda")); + assertFalse(StringUtils.isVariable(" ")); + assertFalse(StringUtils.isVariable("... . /")); + assertFalse(StringUtils.isVariable("$")); + assertFalse(StringUtils.isVariable("$4")); + assertFalse(StringUtils.isVariable("$4dfds")); + assertFalse(StringUtils.isVariable("$612s")); + assertFalse(StringUtils.isVariable("$6 12s")); + assertFalse(StringUtils.isVariable("$6 1.2s")); + } + + @Test + public void testReplaceIfFirstChar(){ + assertEquals("abcdef", StringUtils.replaceIfFirstChar("abcdef", 'b', "*")); + assertEquals("abcdef", StringUtils.replaceIfFirstChar("abcdef", 'f', "*")); + assertEquals("abcdef", StringUtils.replaceIfFirstChar("abcdef", 'z', "*")); + assertEquals("abcdef", StringUtils.replaceIfFirstChar("abcdef", '4', "*")); + assertEquals("abcdef", StringUtils.replaceIfFirstChar("abcdef", '$', "*")); + assertEquals("abc#def", StringUtils.replaceIfFirstChar("abc#def", 'b', "*")); + assertEquals("$%^bcdef", StringUtils.replaceIfFirstChar("$%^bcdef", 'b', "*")); + + assertEquals("*bcdef", StringUtils.replaceIfFirstChar("abcdef", 'a', "*")); + assertEquals("***bcdef", StringUtils.replaceIfFirstChar("abcdef", 'a', "***")); + assertEquals("aaabcdef", StringUtils.replaceIfFirstChar("abcdef", 'a', "aaa")); + assertEquals("---%^bcdef", StringUtils.replaceIfFirstChar("$%^bcdef", '$', "---")); + + } + + @Test + public void testIsHttpUrl(){ + assertTrue(StringUtils.isHttpUrl("http://earsadasdsad")); + assertTrue(StringUtils.isHttpUrl("https://earsadasdsad")); + assertTrue(StringUtils.isHttpUrl("http://")); + assertTrue(StringUtils.isHttpUrl("https://")); + + assertFalse(StringUtils.isHttpUrl("dafadfasd")); + assertFalse(StringUtils.isHttpUrl("dafadfasd#$@")); + assertFalse(StringUtils.isHttpUrl("htt://")); + } + + @Test + public void testMergeSlashes(){ + assertEquals("a/b/c/d/e", StringUtils.mergeSlashesInUrl("a////b///c//d/e")); + assertEquals("abcd",StringUtils.mergeSlashesInUrl( "abcd")); + assertEquals("ab/cd",StringUtils.mergeSlashesInUrl( "ab/cd")); + assertEquals("/abcd",StringUtils.mergeSlashesInUrl( "/////abcd")); + assertEquals("/abcd/",StringUtils.mergeSlashesInUrl( "////abcd///")); + assertEquals("/abcd/",StringUtils.mergeSlashesInUrl( "/abcd/")); + } + + @Test + public void testHasVersionString(){ + assertTrue(StringUtils.hasVersionString("wqeasdlv31423423")); + assertTrue(StringUtils.hasVersionString("v1")); + assertTrue(StringUtils.hasVersionString("v1fdasfasd")); + assertTrue(StringUtils.hasVersionString("asdasv1fdasfasd")); + assertTrue(StringUtils.hasVersionString("12v1fdasfasd")); + + assertFalse(StringUtils.hasVersionString("121fdasfasd")); + assertFalse(StringUtils.hasVersionString("")); + assertFalse(StringUtils.hasVersionString("vvv")); + assertFalse(StringUtils.hasVersionString("v")); + assertFalse(StringUtils.hasVersionString("asdvvv")); + } + + @Test + public void testRemoveStartingChars(){ + assertEquals("abcde", StringUtils.removeStartingChars("abcde", 'b')); + assertEquals("bcde", StringUtils.removeStartingChars("abcde", 'a')); + assertEquals("bcde", StringUtils.removeStartingChars("aaaaaabcde", 'a')); + assertEquals("bcdeaa", StringUtils.removeStartingChars("aaaaaabcdeaa", 'a')); + } } \ No newline at end of file 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 f9d2801f..506d0f40 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -3,6 +3,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.ResponsiveBreakpoint; import com.cloudinary.Transformation; +import com.cloudinary.Url; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; @@ -18,10 +19,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -47,6 +45,38 @@ public void setUp() { this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); } + @Test + public void testUrlSuffixWithDotOrSlash(){ + Boolean[] errors = new Boolean[4]; + try { + cloudinary.url().suffix("dsfdfd.adsfad").generate("publicId"); + } catch (IllegalArgumentException e){ + errors[0] = true; + } + + try { + cloudinary.url().suffix("dsfdfd/adsfad").generate("publicId"); + } catch (IllegalArgumentException e){ + errors[1] = true; + } + + try { + cloudinary.url().suffix("dsfd.fd/adsfad").generate("publicId"); + } catch (IllegalArgumentException e){ + errors[2] = true; + } + + try { + cloudinary.url().suffix("dsfdfdaddsfad").generate("publicId"); + } catch (IllegalArgumentException e){ + errors[3] = true; + } + + assertTrue(errors[0]); + assertTrue(errors[1]); + assertTrue(errors[2]); + assertNull(errors[3]); + } @Test public void testCloudName() { // should use cloud_name from config @@ -238,7 +268,7 @@ public void testSupportUrlSuffixForAuthenticatedImages() { } @Test - public void testSupportUrlSuffixForPrivateImages(){ + 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); } @@ -293,28 +323,30 @@ public void testVariousOptions() { @Test @TestCaseName("{method}: {params}") @Parameters - public void testQuality( Object quality, String result) { + public void testQuality(Object quality, String result) { Transformation transformation = new Transformation().quality(quality); assertEquals(result, transformation.generate()); } + @SuppressWarnings("unused") private Object[][] parametersForTestQuality() { return new Object[][]{ - {0.4, "q_0.4"}, - {"0.4", "q_0.4"}, - {"auto", "q_auto"}, - {"auto:good", "q_auto:good"}}; + {0.4, "q_0.4"}, + {"0.4", "q_0.4"}, + {"auto", "q_auto"}, + {"auto:good", "q_auto:good"}}; } @Test @TestCaseName("{method}: {0}") @Parameters - public void testAutoGravity(String value, String serialized){ + public void testAutoGravity(String value, String serialized) { Transformation transformation = new Transformation().crop("crop").gravity(value).width(0.5f); String result = cloudinary.url().transformation(transformation).generate("test"); - assertEquals(DEFAULT_UPLOAD_PATH + "c_crop,"+ serialized + ",w_0.5/test", result); + assertEquals(DEFAULT_UPLOAD_PATH + "c_crop," + serialized + ",w_0.5/test", result); } + @SuppressWarnings("unused") private String[][] parametersForTestAutoGravity() { return new String[][]{ @@ -431,7 +463,7 @@ public void testEffectWithParam() { } @Test - public void testArtisticFilter(){ + public void testArtisticFilter() { Transformation transformation = new Transformation().effect("art", "incognito"); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "e_art:incognito/test", result); @@ -522,16 +554,17 @@ public void testClientHints() { .width("auto") .dpr("auto"); testTag = cloudinary.url().transformation(trans).imageTag("sample.jpg"); - assertTrue(testTag.startsWith("P>_R5yO$kvG?vb+Q+1;U7i9{)_9n9&IVL4voJZ|qR9BSvR>H4fMKO^I*~vTCJ;B2y~|mzA?T=O8tFqWtlVKkf*h|&hq?THa>ZL02cWy4Bwv{XF#@t2I&PD zTvoDHaWcv2=E4AMnz$Lx!UI!Wrgd!cqllvrINWz<6H3WnghbCp)Y6Aisu89*6{noh zOw8G+W1_?!mN1WrB#*HN{2mR`JDZWu2VkYQ0yqNuk?{Kibzd-o)Ke2CU)|yO;Rk46 z<~WIw4NiWt858W;k^2iwev}~#lQ&&ly|*4&Ui#HOq%#0S87pt$K{R$>$-Mhy1CJet z&ALSG;7*zyHSJUmqdb>e5@3+p-Ck95X?m(agWU!J~7~r$ns7EpuDlw5)!N31wDOgaL$`dmxX@sF_9b_VZrd#Q-_uUttPm?rj0ZlTn5*;BVKqL+9i;PW?{q5` z&H@lOa_PMCF5WKuN7VyZU3d=DdTOvjMC;8)DvRNI5-SPa^3r#58FV=(Wm(+V{@b>z%(d?~D*7eAyW;65R91eB3`(p1H zhxl%#@XpM7Aoo@t_v50XlO&+~(1Dy-9MwXZ?bP7#%mCA*T5dUwxi- zTe~FR%7|XMBp*G!Od)5pUT)S@O@5N`(>S8Tn1PO-;cCeSvwLD)ooDX_XR+rufWGQs zRkfKlwvyCc!8Eb$o}QgFkL2Tp?9?E<>+v5CZjHF|>ap|*7)SbQ>du^6F-PqVNhyHo4I zpXP!ze|j4_$6wVq!hoHX{czWI@1JQ_t;L@e*dbfK8i2NVpXyFY)pM;=Be;L<&DBIv z-8u-^VZC>WwB2->P^U6cwg5y@vcncbH<+R9{(zD@8rA4^)YnVJhZ6LKY?v2j zv~~ugWicn|Z>~kGs-salX4_?qp#Z(df8HY)qBRVg`^WCwQlg#dKyoWe#N5HNjZeQ`eb!vcqd1e( zFy9aP$ye1Vbb(vPUcBoVR*bnt6F`NS4tq#&A}a@YPq?deSLmXy>nzm?5zCGtoSkt> zh`waRERsvtqm81+ASss`L)IyO7lhH|IW=e6Fr=JacsetQNC5GPv0ThfLAtyWnV(SFu^x9U{e)gMmz*rP()vlUF&8u7-Ge4(J1Sa8)Cj79pR zME|G}G&($T{_@T(toj}MXQZ*uU#T}e5#;Q|6Ny?L0OJzQkqiz|OC(=PZ7cANjBR7V znS}VxRDvKNdss(g{6s2kA}(#g#3(TYwX1sJPvaHV7pH_!c@@|(U;$_ic^6a0ts)pP zvgv7^b&1s|o6XnzT8nGt3jGK#yi?A^llXIme;S64LO z`92op$;ZTme+~LRXWDcA0cJh=;@5s&o?S#)^8q*o__OJw6tTpy23bKM09Ift@d*Vs z!BSrB119`K4+AL0fU(JKPODMKjsyxx7N&kNUMj)%2UDvuN?f>k(LCy_aw^`PITjG! zWD6=>n0ZxxlK$p*S*%PFxyC^Ya7*-fgnM0jXhpj8$vI}L$p$T_AbfBU4sR$%V<>{2a^{eQlRgE*-6iMOLbY>#C?wFfgW z?GTo=iUva%pPh34wuxoJ2erV~2XmCZQUUA}uZuvfkCp-s$#?En0BVpLr|$eA|G0<- zZ(t9{@`~jX3z0q^(WG1$3DAl9KwG(TAxG=`GbA?@o{65jJn15S z4Pqh2xrSq|ULlmLmi>-itm8kF)*rb`w}s@YF+i6AS6;;E5ZB)<6Q)e%7LBivzB@Dw z@q>LZfspO14@bl_q?NZkM77l5UA+bp4P@NAP)R`t*w`SFXPA7njPWR^onL9iG*2WN zU(35ZN2)lp(+jn*lLMX4qI8SE6gcoh8Idan^`Xu(;S)0;_Inv=#^42OM4xXaR z$Hf^*MJ>|C-Z_180K4X-ap5>J>I5VUV7I{abUI^V?abH2e70p(UeuH4wK$z_K0sLg zJ~;X9I^E#Rch@8)7d8(<)N=R?eS*%1r46efe^t^+CSsE~U&V{3tpmZqkT!#nJ9xS@ zQ|-kE3)xAFOh2WtwbQwfMAZ7+(waI$=g#ecRA08HHo=^#UzD!)f#z4RhY zLvT1qj?j^mSv0dPy*$~43U`JITiQE0+fydZCl`7S!W4eBu#(^}9`7JHHn z6QM@F$TEz4kc9i!Kxk8^~|x1c!0zI;1O|PakFw#I*U8?aPC6Hbk#*# z^%m0GrQ=(#{qr1OG~RJG40p#-*HClXrXQct9KT$oW%ahKv+3mHShgc?Sh$2ASnM5~ z64Exqwo<$Bs=!MsVlyQL7~!h8hL{jHk(Fd3(dEtrAZP1&`&10c4hw6KGpk57rmG05 zMH&}wOS6PzRgtUa(8gIT8cR+JWjcp7qa&8vbYpKR5u#MJ!nPozPy=-4-zp~KAC6!M z)Zd|Eqq4^r`;J`RJ`EpmZlu$Itb~&2|4e`1Wc@=hXZV_g8yyuP%mkVS2m-zsCl(%izq^f-j4)j& z8%Uiir>(VU3QeS`8g6LzWc?Ek%-_mKov*e@fqepF`*xM1$}bXsi4za3K3ZowTh)&Q zK+(N|+D~T3&)L_1ml+8RV=4d`^6?WS=3>aN&0f-sos0vpcPby*?N7D)nJP`NK!Exq zcw+FhLj4=&Aacm-G$>(|jg{yrZq>g3T;Gocv@SpGLhMVk?F7J; z2Ixv%++Y2q7)3kN&!fob)=w8ETaC8dxaM*2ujmLJFgD)U2&wlDFtY@>ZCC{N=6980 zS+}aF&-Mb=AFVE;rE9Nhhs4!|HQ#4B$j%%$vtfg#KKB(=dDq$+sc%tsGLlMC#WcIV9nr4ZdhWPT42{jYc;%_fvATeKKi*2t#yb0{Z*^jlv5dl2o)qRJ z_(pr!bOD3#+}<0UNvKO5iLNoVd}5A?;{_(G>p*Be#|Sif))uoc%jmssu^zSOed+C+^?t zWeosfJUxizr(Ch?u+1bTiB^8)C}9wKAj=%@y9v5xOt8G0HZQV7F1lMvmg=E%Z~GIO zqQhp|G~4lh_2?UNurs4oSSSiMVFM@y8Pc_g@`e{3Hy!qEH~N56bq*dh@^5muYjHP z#LuhP?65NrBrBOaiBr)$=5b>8Xli&6jPRY-np2RYbqhgXQrxHB9w3OUQylN(P5GER zFyV4B$p)ot7iq<0d>UxFqGQ*j6o4SW*vwSjA2W-S#?ZFpWdV->VlvDk>sALJ#tUG| zg$hL3v6S+{k&94z@*ruxa)tP(NQHc&l0aWh6LHb}mMWp4T&SC~z>uQT@;&ms_H)P_ za|c$lqp()^4dSl{52NpUmhw+UXafj*6GkGoT;o%UPq=t$GCo0B-6_P^ELFmC+9-SI zpJjOx;*IS!+=3`K3iEVfrfSTtmMZ|%d=fY7S?uFa;topY`fa4Dm%GC8qv7+^Gg?dp zs|1xGOdrkEr{%Ra>rvKJUJ28AO#K5yub--BGDAKz8-GkZ!Uu$w5vkjW3py0X>Qk=Q~){@CD`IhW2WiHzle;1;-@UY?9_cH*f1XHH_T!_bjLL#@O2F;o8Iw++s3%X7tm&G8zl29NJ}~> zzlI_nh#k`TjFw!sogtuM#|QW7vB3o%uZ%+Qjt5iCj%Iv38FOOg0_tC>n>RW-QXPEi zSVdh;QGS(8PioMISNoDMkg>nb0muk?Z%^q=bYLi*-FqsXU0;EQYUKnV2pu{P-6wuA zX&@ulj^_lMaa*Q%KPhq?LdZ0zIq>-@;GcKbjJg$BMn1gIClSD(U!7h=Ro|$3BSEb> zFt5oa?iuXY%l*!ZN8ULVJ0KXh)IXwrSo;Qu4m@oxZC~F#0R2#9N%wvGPrZedKXTE5 zK8zar2}8vLxSwCk7f>ad(4I;QalH2HDd+@&D%^*8ncO|lau;4-XRvg?H~u=k$ggez zBg#IR;HRH^f;^zaS@V_aIv*rQz@gY4+xG*+`<+-phU1Jq?5IhQe9h$tf^*{md)vN}Csq#(-O>n=(pZE+9Z1aXzaJ*xELE594_$^m~~!(-1&cv!+`d;kDYj@)o~vT%g>s z8|Xa0wl(HlUa3cs2!yhbsWK`!BhDEMri&#^OSWp+ea9_8k zxb{G!Z37?zbs|vy#h>p&{>}!j(E%>u0ZQ%$PO;d}*4RgT(BTMJi<0ySjT&l1{$oc`Kx3DhrMI~c(3O4u#nI2gEUe4+EkF=^lIC0K9ZOZ#`p)UVjX4ZQo6 z2){)<{u&3r#p@L+O%SDXFGI-JdcRoQas$1&D*@0}@iOuu2z|OO_q`*UE40-_+vL?J z^_s~8!<7ZbSTIZyA)3)CWqL;8F?~GF- zS_Q~o@~U^w^FIWE<5p({1mJe^GD_;`+7MUV%%&xVjpZ>|{<(Rj zvLbYW7w+JGz|;QI;STo?w|$E*c=nn9fC}Fs5WFDixHS;GQW($ug^>mbr`|W(Exd`}ZaaI1FX`m3eogCks1!{-B9${kg+qEh}2A!*Ivr{gLQ5<}lvC*01`8+!PSG z`4t~{#6Y$Cg)IEd3I&TW7o!a;g0w@9zKB;8lW)*^>AVIuBp3PT<06K87B0juAH5#H zR@9)Z9%nKopy~K#8?c&LHn=dX9Mw=ik@0q> zsO5&mU0naNIOlP0zFTJhMf`T~c9G-8d%Gqc@@q%A?Wkw?Rmt>Auk z!*vU$p20OYgLy6g2{u~5i=O9oUCQSUF)K3Y^0PB4_RMzp7ql55sY(IUav*83q5hD=XR=~#{;^zg#(*h-wp-Q~)E1&_w`P#Ep%DwQVlE3_ zlFg9qh=_Ws;`uX}? z+XLzexHDc41jlk1K^he1X*)9JL3fhUbcjB1#=YB@#sz2;Mj1(o)5r0$V%<$r4$mw7 z(?(CDudz|;!8lq&N=~JWG9E(}k%C(bZ6^zBg3}Qf3$jhK-Jwk*bCuUuaj4jcs>RvR zn!!(<=dZ?ys?uv@h4#7f7^_kdp+j$KZY#xrEgm~BtWYm(Dnh&I+#=()d(6e=Sa!~o zXk)F5Ed_*H@Ybigq}?VYCO{3!L|lyMnn|^CB@@ym84NJfl!EW*r!j7i&$Ym81eKn` z(`Bb#R57=vUJM@~8q^Z|o_GlRxzM6`0JZ35Oi?^yS zMgiWbYIW6NYqZ%{9`B7ABPb{F@^2p+g;yKepK)OSn(83rOR!5Q7M&>n2{hnp&1Ip{l;@vPz?Ts{=?z zT;9Tt^RA`tzA}>sRjYEk@zw4aFFP*0FMH^xCRtW-gC`VcQIy`Q-q#>K8ZMn$?ldWF z)zCM-!qsXb{V_);C$&|G{Y^IsVO?kPMf$?VBFWug@>Pe$bUolQH~%a zY5`vjZ$?@Z5!T=B$p@$gnKW;Ikpn^=d%jtJnPAP zqZQ^~pbnnr*6*PbH=}a2#8d~D2h>_*I>PN(jsJWfEnOHO;p>JD5-`60zF!e7Y$lv^ zKq|%YMCOZ9crOIo9rwZ(AG?OFf+_IMp;+Z%isk$bds$)`$;Duj^*0SoX9R$7uFB&Q zKMl2@`B&3O^nCWpUQsGlwNE;e&)Om_<8wkb$^RKSHJBr+%< z>ay_5+1e5OBhL!Hu?}ategIg5J0=~~OW>&qc6p)V)_k6oGOxcvq{Mpgi;;u1G9vqv zoo^Uz;!fdhX^u~#1>2sg1p$jgLX|@vnBZBZOT5N~ zQgprLk0>?x>L0IJX7g(DOlJxKo*1s*fv*l60);{<9|4x%tZ!{1rvU(axLiC7`?Jb* zNqek`5_d`Z(Q_LnaLeGUL71un$F);*Uj!cdOzu?$CF*4yPjpgKY?D~fMCvb}H(K+20sR`1lfJ@W^=n#gejy*Kv z!T!i$Ui1jOD%3BSGMvtca{wlV1?FPcl+fqV7qZ*sQV2lzSy_cm{G zmb(-{9z_Z1XSS`Ofld*LdKZ}{P=6g=w4Yd8kzQF_G8OEX&a4*JEZr940nHa^4-^HS zI`SbuGX78m^XwX(7QHJp;q7cXCzES0Gcz}nPXGY;h9C?ygRV`;wu+Ro45m%A9S=9! zMqwg8BI~aY`VHiGRRhBlm3O2*{qTd+5YW*|BK${**i$j)iAi9%f6}>#!f$eZChJ;>S9(a znH1NOs1ZdIRr%njb$)Wo>{_O;RmD}DNk-G&tR7~1KX=t-Esv3^EJaqWq(LA;ai9pu z>H9L6LQz)1gBFP{Jfj_HY1&FNKPN83+Qi%Kj7mW_>b+KQpmad(tT+efvdGNRmsFR5 z*ygJrBq!jF(%O%#9xP_CSbsTI6|>xEs#$IKuuUS)V+=5Q66Wvm@r3T?3~6`jBYk5Y zMyfi)4NhTkpYGu(p%J_rL(;rq6uwS2iLRI#I%c+drR#Q}1mgQ1)Zq>sWw89cyTGjZNxH!t( z(Ny^Gm#m@+n_r<*yM54BXhFszE@daqR+On&(aWjv>WY~la%Q(1TZ(!rY@Yy?PO^e{ zx{7pReUn6qdRvN5s|Op>i4kPjYGcAa9*drx0q!+9EdSW=g!PK+^ji$hFnL>PdeXeS zF2C!S#pZ&d@IR*77kmQ1L3|64&jS0;aAvOLH)j@hMV1NTe1nkUL|+fy1$}_^TH2iP zvc*8NgX3pNB%25?#_`jg&>8?-S+b-i9YXC~6?Y3@3=67nBq4M6;(rT-`aM|9jRoeN zfSlNbv&tB^B%XGGOVsQ=fcC6OrHKL8iW^NH$I_6HrqPxbSgwtH9tY5lDL+loS}?Kss5coKdl@+IM(irU^!S0%n1srl<}OA&*0W+r zt7>l1Q1Mm+G|Ld z>A;M#Uhti5y8zB|eCF%`KOZlkg2*!1sfH87fMuOmhj99sjuh zOTFl5!xoaRR;IJYs^iSmakqRYd&AW9xSq9)C3hM_Z}!YE^J4%W`P3|=8r#$ZE{hFq zI(uzB7QS0VQ-%|@j^)#QI+uk^7kBIMVb{IseJ6ZgM_Y9XmsLn&k1M+b}T!ww=HB8GunVo4Pj@xzl>MG@cc70tRfU5jP0N8vLV- zqlu9Mw3J@1cWUMsJPYXHw%c28w5vio9g~h33w88NF$k^qu0$~gRy0_+=*DOG2knkK z(eNdl3wAP-ZB-|lXt8bc3@#-^X4!nxPpnggT|C#|BM(5TQPGWPcdiD}Z2W;px&ALA zJc`{2K(3vRe#cP1mVf<;PyckbZsmkTQ9Q6O!)pA6Acap%84-2E!t{dV2H6TG7AqG| za3PjK@;bSyAb|2pK|vsHGHPm)rkD+NH2p*l`%iX(a1eG*|wF{)GI% z@+=`}kGAb`FEM-*d?V-Pb>oOI2Exa&d^;P=`rUHP`Mqp6zt;b^1K!3w0ZcJ&aXm3e?3^&T z?N|^uchBOkaYn=r+AFFn%I!AVRmw{$Hs-x`*jrvZ^Y+#1BF)Yr`m69eebbJ9n>at@ zgG>${`_3%caEn=;wM;okVX@}DHCi6a&at$GJz7dO*;S>{UQ?9NU2q3#H-%PKYp&Hs z7}+Oj3J@n{MwYAE069BW)*w2sq?i|1Q3`m~mjb@w3x3$41}jv%l_ppP+Z|TUK0{2W zF?(9=mWj1h*Pq0X-mI!haW=IUx}A#CE;^RoC^y0US_<3M3!cciI3TIIQ}Pkh-Z06A^eC%6Uw7p8HsW-B#H zIZ=c-cO&!AZ5VkY*#XMkvyPzz-lQ1gCq3}&x1=K)a57sa(H>G&N5oew=DfsP?0ER6 z$^{ZE1-zD$eOhv`Y6r|iNJ!1bs+lhHb&Y(rmeq2BEoUsNQn4oSj)-iR#L9`$Wm*wO z1_9U;3GIk)1}pE&}tVV=w}vAw9k+D=Q8S-XW? z1pZ0W{=oV+dKyFirL025J3Yc%h_0mB!Vw(lPF?L` z4g&q#1kjf#^A5N(HXwgu^Q?=sh(OAa921MlG=-k@P(JVhoke zJ6)j3r+iHu!h(4)K7P^*lQoiXk@t%Cz4pkxvT`oWAg$1jY9XN5VnMq@bGe7^Mpq+z zi=}I3_k~f(qS!HI6(%AA+Y*I=;rZnpOBtgp1CU6yy4*TIY9*IlB>46XdteLMjEXCU(k_}>xmw+*_A4+IEk6$}VS{J;K;gLAT5pg3U5en}9C z?@D(YB0Q`GUQtzBx}Z~u!%v7(2~1Qt3Kdwns?Q-Qc4M-UW~=fI?T3h=2Q?CMm<15h zF}tabBu6MoIXQd%Hl4|J^iY0xCjeA&L`K}`~;ZjVCXT~ZKq1Q>jJgUcT}kI!Ig!mFPUptI_*2>>~r+R z8h2TM<-O3f^Y9(z!|%WkUU0%aXtlfj4EmGjt~bX_7msLXgkWp+$9+1tu<9epr|&*W zh7t$vgRn;l=d<*dO5m()25%}RA@*IeQ-teQi%_q;?s*#6JU(7>LlF=s4p-w6`7QRV z3Kul$Te7|I5CBZ%U)kj?0+`7@aP1B7CWcowqfZhGv3#6Oq2jNB72V-q8j}6-?v&!}WUF%rgALM^E z=Ag^z{~t9AR6jho`{qca4}sWI~nk`;11m1|Ge^>M8)f@>>^oUi|_MP_Umo; z*5{7Tp5MgXukRCHfOrgqBDfg@{1yll2{jRV03x*t{xQ+42nQlI;uawDmk1hKz?<|O zm^KVKAVxor?>bxy43?dR475U!fZ>zu8p@C#|KVr>$ogIrDEd_nBh5N3Hr;?s@mw3J;zJ|tQm2}u7wN<_2CcJR;Z?`jBD|e?>%M2bvpc8Nf>{7q#I%AK{X^@%i zm!&iw>uX<74r#t(4s1}3OwvMck|xQUEG(<4 z{MKkqhjqP54HIDQiKhD29YG|i7dlmm%28RJP=N*|gNL4B%XUT8HkhS)yrEmoC0HZO zxgKS6nVDpRnfT5uBBU1t>Fx73A+NI&Cl_@V$8x_gF`OJf?Xe*y+~yrWBM2KtvpkWt z_>vC0{JjS*j~gK?#(bO7gw1|`LkGlVRCvQUYZ9T)!V_SVA9K~9EY5am##*2xj*>R1 z8HUEZxj>}oXOyp;+2T%E<$YjqX}3mhDR8X6QW)i2O^nK-N>hU4KCX_hEE0t;ZRU%& z6K{g`*J#U~O**eP_WOFbAO@6^vY|^1v68R%C9XxY!k|&Ow``$&WLfiEXb4V>!LUny zkO(W>NfaOsc4dYu79Qg2t%U|XTO`i0Avc@rL$8r?_j%L(Y! z;b-nMUjI9q5MmUlSZAEX6to0FL;t{yW|?K|rT{?7$_-WLskIR}?yRjcr$6*VIoP#5#X4eOapn(JT=yt};tyqqiy0X?2@y zTLkcN!zLvorY>NfpNJQfcJ26S1}9$dXx4V)7nvN=rXebJI5KfZTPrB&{Q+; z<-#rs$?1_D1zoN6&@1?v1>JYt5=@sUtGm*m3&@Qd?YvLd)=bJ$BWTgGzx*Ujr0d9$ zY;=nqk>>IZn3hk1uuvdL4y*da^gTOGF#yn?Q8%-ZiwFdaKAhAd2sya3z&qEt46Hq7 z)4B7WO6?`i&c)(Z>~NO$2lMrJe?FRF}twP}CW|O#TrNalsx$DO{2}f#E++xHE@fo5N$X z(5IcNOB4}?c$?!Kfm;o`PfoC23V?XQlw`DDMrjD8$ro`Z1w$m3IYWPpV4wGi$TEwE zhY^fnw?9M=r6%(3XkMpafAx4tYRS#?SB%l@G<=RU_zd@emErX(Aq08bG94hSL? z8Rvx4TbF7nWe;2}Y3^(5t=~rJ2_aS)>0Zd<-{Z;T`YKU_Xqd(KQ|6-Ju>p!ZvS!le zkFf&@8aS2pcw(B4et%A?s0d0KV;=PK4yz*?M25A!zV-yHurY_4BIOaB&|QYxQ+x#! zp#a0*hZN7zF3*C$Siy(92$2R(>R(|lhaO+VKaTg-xfakv zJUL)|LXo$NP&|lh<~>pJwtyhY(|2?RVe*S4PKblctW9MDs{yFtN-3qyBTc=t$I@i! zQ9aeX!Av(VaN;BTdO=g$e=wq>eDZ zTgrmawk!eN@Xcs%HdH>S2Y(UogQYyAE5`IUVEmI9BLY3`X5X& zbh^rQ0&bn=Od0cFj3%RI^wwOl(5vEVFs_o;Y@0jmbVj0Ed-w6gCe-&H0DSr_ofQ~sd{Xu_opNE6>29fejp=Z~ zap!`z?n17CFKgc^B&)binA9swdyqTMcX(wif}6;8vbn>dLIxi0CvETR%F)d3)8^{`hWe2fFWW2y$FM-nVsJDE@j%lZH~N{$ z0f&UE#sHsTMwcvfmg)SyK)B@ItpP^ApaZa2dCl7cgo~ek4_lSc%&9zYBiFxZ-S8@& zaGCnE$~3vT#FM6O&R*6+e7fJO(pJc!n1A3NOIkp)L!3_k=rsc|py2P#mw zQvl)Su%J;H^vx&oHU&XCG@MSy08%+=ZVA+c1|xFNT^KYuT848B!GHbiDW;7eavd&n zM>^3|KR!{Vxcc9A1&!Nl*gx0R@hl)iK=h~Ww53HA%Bl%L#HsDs!(bR=;%f*~ z<@ew{_dtKZMalRB;)SQ>x^Bi;YypvDL89hEhB5{*!Ao zgPyBzu@TBLa#hr<+A+@H!9P2ApYMOx*8n)r*U{xWhQT%eI;T8#Z*(KKfePyo5uLE zZ}?l2UCf4@Cc%1%+`K)+8L2gJCCg71RY+k%Uo1n&eZqQ}iHb~@MyL5R2Koq%rvbMl zA50bCE6RChg^f}rng}MSQ;Koc<9U$W3d$tHIT(!ijP*$jN=zoFEGe&geVE7ECqACW zdU=@ihks76=uL?CH-;1;)tLE-q*X;sH#CNKiCuu)G`yKba>w?F+yaK}_pJ%Kva(DK zT_T#_@pqIp+ELHN9a~kBN4T0Gs2C_NKAqJg=e5us#S#rNLCm2O33W(!uS^r;e*Zrs zvOFpc7)ZuT`mezEe`l}%?dpOOJ7)c)uaMIuOboLiQWVszWg4v^v&i8XbC#FXT2dRy z4;%o>;0r|j&ydA$tkPfXIX#>lTV>g{U6(npS(n-WpzX_5{hR?1>VvQlHVe$hNHJnF z^C_xihQ@dsp^4IHQc%e{MI)_=SbzSwW@;n3h{d_q8G!wj1M|?!EnSG3>yvy)Fm&tG zy}8k%%OQp@npFg?++4FeY-)sGRr-hLmdVr|!S8mRtlY`6BkZmNuCmE4+kS$XcaC(} zc*N;Sdx!0XPNiB)m&A6c;Z-bSE$k}O{+1c?gnUh#2j-a?04}s&DM7N#?8HsiInIc6 z;|Od}0BC0KxW}%-#gqs$$8y)J{-AHa@-`$HSM8hWX!bB%_3c$DPBW`vDF}_Hl~Ka5 zi|oy}7}tZGnb$dS+q%y#vQGt&EkAdbep9`8yV9{oXjgK}wVU@H7rmR{9h`x=!C=9a z-J{|#=P}jjHa`f_ZAn3 zl`$nE5A;=6!04wGzwR!6uMCCGFvYuC;~==aX)D~^mJoyH-EgO??MxKe{Jtlf!D!)% zZyxBPong)oS$|Ytd`q)?Kl?AN&rJ2Z1pfn8%N1tROFA|A_VYl!pk%|t;|ul??%1`i zfX;*rez9v3XePL&W(mp-j+lfT7$D3Mxd^cLGY^4j`%v29=;!BYt`GAIbwJSa1Db>^)$PCBk~z3Gk>?Qn^6v#8L%$;P}LE`2QWuxg@SF$<(P% zV1Pn_^5*>15MXe4*BIfHWXg1LK(pO{k(3#oL6Ielx>b8hX+%CDwvB8Im@Fga7e|YY zR@081J4SK*A<7%ZpFmL*g8u*V9*Zu*dGb&JkK10Bvt5_l>93#9Ba8r;yw@X`C~Pbh z5ePEm*%GByMp&7V90)WrBkIBFawmlxJ;evX5s@%3L~OW?RJ@-^9r{;nz^XSYr@l#I z;hck5++AYxv8Q~?G^5@*?^-66JtJwg|BgAPt`o11TYIM&MKc6+NOzwtFp_fTd<^z?$AxRcD;qziheN6|v}cUZ@*F0JZ`M zGdz#?MF?FwKmfSd-9aSh-P$(^@uUR1b44J zgD~W4W)!!-GDx=KfmT)f*)25Ve-cp`@2;AGhJ>eKbUtn@nK8xDoVl_B!ucZ|LWqyYtK^MzJlay7YDsNeIx3{8cNAT;!aXilSP| zO6nx?b{RsoQ%)&xo97iCq{Sn&n*A>ZkU5VCA-nKf3^#)L&;r3q8>T#QvBMm7#7oGt z94eu}raE4-8@A&IOk!#>|0T(k!z;R@A@>*F;T6^n`A)HD!4>`tdpL>>WWn*F$oUYZ zE0f0e?zW!}jx0xdZLvi2p?X64@M`df$*~TGCt}A4UX#R!4!2G-k1M#&4?T(x$-W_> zU{PekniCvJ$qBr-Cy5V99L3wdF%&<1=}O}_XQz^($w+G>4RG=zgW0+D(+v*OX&IFK z1^r*`cBV=2_$R|=DFNKKLQ6xV6w&C2)h(%-P?3bCBT6ErlEq~iNxg9iZ|MA$=Y|BFRUa0VxkNZAHp0qeHWwOnW|e*&GfpSMBlnz$uN^CP>Zfw!NB^niU%7bg3y`$Ko9*%6yX8IrP1NjSrxQmiln3iBs4ZQuHf` z-H3g{_x%Y%>i$m`Ul|rh)2xjpcmf1xaabg1HaLU;A-E=sySuw>2=0(U7x&;EoZt?@ z-Q8USNe&P1`Ofp@onJN8ch}WD-96V#Ro%rK*Unpsl~mM3GWFZh#7AbzoUmiYdC>rt zcm#X#nyB;wJ}PprY*v@|vr8`~4KC-%`R@5R%5((1Wg<%uT?7`MBhd<_mem+H2U9! z+q}Biq8V}dXIoeVrC=B*D1q*Hd^Y;9z~>pQ*0HxZ@$UTK>M^D{`kWfgI&xs4oqUWp zMzb5`2(KA*Rwn^Znx$E*XFKJZG-6!z)@*IQO|1P)`SBt`I?h@9&eE1 z%EdR(nW=W`8w5~DrGrhsClugznCZ*5SVvFO_^N4!b0{JSoarG?4}U=&(_VJ3~jr2%eL)s2p{f-@4XI0famhD_3W zTKYiW?Po~KhyLpo(PG-vLq+nrwdliYUb~)F1vfi}2SH!Tb&g?aVG8Zo{-FA1wOfY? zn-sh=A19J8xE49;!=1P)qI>}+`U#5CmzGTm+xSTzTABnZ z{RPpr4rZBB2$?FnSk5C;^W1NQX+QpX_&vRD$K*O$y7f3EgK)AgD&i8Jp%ne*w>YSU z6JCYRNc4$@mDj>#Jr@?gBa&bKXcm!mYq{S*pASoJRAE_sdd)$2x+1H)(ocg@)ecjA z_On872loWered>lBZ8{&4!N601#9Mh$ee+Xv0O(MS*;Wa3=v^MWUi7GnM*M?+cDug zC*PVTqWB6a`ND}yhJ68U9;XGpp%Ft zLP`_TH;kWtK#K6w-T%1gN;)ZFnswyU~Lg+K5I^Y0gw_S4skfIzxG z`b(Y`LYsxUQQfNE^b#bDCM{caI4%N08RJ8aBpb|K@8e|sAMd11c^fur6r^yBcMmjU z1bWdKEF3OrBw~~fnk71(zE+MGreIO6J)NSOxdy`G$uWz<$tf=Cys?(4k~f-nI(KQu z&ZAeXEnT@K+EyhBOUDZWkR&NK@Kw};;}PHTb*k;J#ba5W-#ba!2!sPb{fuAj#JcsR zn*^-K-A%Q_*6HS?G;(>(l|Ny5{KOL**4sgO%0SM)G#wKrYE#t2^nI;E@SgIG za+D!FdQBtd)5cCB8--jGzexx?mCt2qfy7&-jlCTrAu4x^F2yJ&3wmLy4+UZPC`NE_ z0KfDpn?n39d~if5&=py^Q^};Thoy^CQwa81x)ojeC}hw6%h>oMMes&5)v@x7Tb=<>wBlJ`8M~^a$UW?0?_W;DH8phOy zeG?5)AHZR(nlp+AKX~GGW+%TI)}|Dgm_FIC+Drv&563t<&r%H`nrDK;gRCZu0jp=d z?iN*0fE5WXJWsMSv;;Hxw2pviaA77HO`NtQM2&H*nXRF0Iy@=FOcpqQaz2%0U&Mr0 zOLMA7;}pUbFR8d_06NNLZ_lT=YhU@4b~E(2H#o-V8(Xj3I_RW5)T zEuM9)3(n122RqMeR_A8E)bC1}tOL(KfgHp^#46mfYRXbQ4k5Iu_~lc3q20xt`|txv zfeiicBvqBei%h)(8Z%wUkY@VC7W?t|ya?7tO;R|StF6UMW$tMcp@o>Ki*Q z#&mbF&HmfNhZ9pm^AhDlc4VdH-G^6sCW<`C%{hJ6*v=Az?{vcnxlN8sopd@(avG;_ z%MHX{PifDSLj*chtR;9qCRbaFF&gmom#WDnjSt%KHhkxEYTc7OFoUwPa1(>(ls>jW9phvoVmLIaAD5}%5^K!@@S4_=9a6~^> zd0VutvJ$Hh+%3cOrhXkub2Z2GNxqz!QD2>4KK^%tT_YO^hn(zZm>KhUteEyhLiAh5 zU_yWgE;w(=ktS;xf^bt0Ym-_MbcEl7$1x=FFX<)C}W^8Q4i;ei6239j#%nHlOz5{T#Ww!xV zK97|+uk1dt<_e`Q3@U{q$$&Y@4q~85ix4YO`HYosQdXP-8rx~EX0i8KI55HSONuey zIW!IwfQsL9WT~t1(~|C28x+tusKjYBa~2Ie6Sv5v5-Y*ZtQ(uCn%-lu(lQ9VbNl)u zl+IwUd6XoqlC!!~M@4J2#b5fZDz>`sy}?11uYhJ}Qi1UpY{QgEZqrNO!!O%3-TiI3 zUNNfnc&$;mNDsBYL*II*7xK#t*4&NnSYDc*%r$uBxgcVBNbana@N>iz79#Pav{*5) z#J*P2T7K-S0!(XkS9xM6^4UuYcJ&6cn2c&G102TfeE#j$ETv)N(DR8tyGToRWt#Yx zxV~{ljxBp0meuT2Z4^?O$Ak7?EMh^(wQGdPJZoU&^bb|+E8L=EJGiFFrxX;~eKulj zcw;bJT|#^X|Auv=a*fyKJUL?g?!jt%A~+?pi(HSIZZ#HC z$c9Dk1+4hA3m3z+umN|@IYP^_*V&<&I$b5QPJ|7z0ddHt=IMh5dJ)!=I8=Gb1DaU% zP$@F9R(mPy-tA4ygK8~3W%@qXDx@ZQ6LqX|y;HiooYA|0vm@(0`$lTl-c%+5V1{JY z(isei-sF>7HMHdlT%dO~O&Y@qOZtV8#C1ypdy!o@(<;20j?MQKitmnoQcRFT_M3+fUM0cD)DU>ZFb}eOd-0Uk1I5rTWmXJje z1pt-c7lNm6xJq!wly%_jhpWSN9?r$%$}W3Zb{#cRT{TcBQSxe{vC9>a>zO851-~UM zmQYvK#-}$r2+B242BD2H%bGbjV2Lz2fp0|QsPKc}Ztdapq1QopLS2|eL{r3di0Pvn zWPyS-Q5I)%P~1Bd0WebqFDd#pk?T8JbO?TCRHrs8lL9~1WX4% zdDHAAirdqup#WZ9WMBGBpMKx)iiGjaaIW|6#){JdxliS#;1!tt>rXn@FMTWCS7ius zJ^8Lo&7V2zM4c{^?E}~f31&E3M3^a;kLCN2h%m|pbfw|iDn3mV%x)>qos~|c+yMS=sxOO#tY_{J!N=&o`Y^-&Q$t;^xB^GlU3-N z#Ktu`XFjw`gb+GHW6WO~12fSiu%pnFL0rp#HNP1Cel+Sp}uZrK=#+3Q#S&46H;4Pi;^@VB6 z*7P$8;e}-aLUJ?8ZtL4!Edry4`Ap=|L=n~RXbTVsXJVNkq;WoJHj@Id)U|j|n~+pQ zLYMK)RicEx3G%S3OB>7rM@&ej$l@>P5^GA`89>7SR9#sY;`!r82Wk6ilrLp`n7rQ@ z&yKk|*U>#*OnStpNU>?n(?~VO%PQ5fh5>fR(>}W9jb+Qr`J+C{MJw(&S1)F2l7vsc zsy@rTSUJ^9r6jvg;on_{-D`1pK!;_0(e;GR83KR>X3y8NUfQcIyfyIEEjgb(a8hGm z*x8vvj-`uoV-~Gg>HII<1*wwoE5ZHzlh>tP2bKq4Gn@~0HA2c%ayt6YVmg)%I7;kd z7E-?OaEZZx@}P3&j)w*rsF?oD%fZQrBGW(}pKLktksWQkkB$Oh6{y>YC#xuvOAhhb zHeEPgQ)$bC!aC81w)@g2(KjXdb3tLTo zKfII?WLCRg$qt>a$hVL@dw$fu2rAp6zoROMYl4CtdAC$PQuuNQw;Gy@_#_vM5^{`F zOJgWjiPZ(&>8>rn22OQ;pKc9jGfg-W4Gm@!AEPab zA}#LhmF^1QadjiOEhN=y+{0T+ITZWny?KMXf?gXneoTY^L{K!(XXU{4*yHGccb7iK zE+fL>0elzNvh)M#f}>N&$}rQDq_(fS#`HkBLT#D&&5;6N<|3*O*hVP*ZK z!tnErt^fFh2Uf5NC)CxpQ7Uy%Kh$T*hI)N)A(-BurMb+J6k@H++icJx;k{Zwm{KI_ z`^Zx-{An!YLit?-d8IfjG9_yvMduoGoC1YFdfHop^kmNDfO3QqvsS_szw_G#j2H9e zL_`^s)^&LE>dyocT?I}NTvnO1g1>jz+!GV666JUyAHv)y$$10ilaORgGG(?hG($&e zH)m+`4a=%&SOm%yuBe!scj8{!RV?@?3I^8+wqNI0=(a@oxFaU?7v01*1-#sJtyB5^ zX@9eNCPphLNQ036%k1Ca~gc`*2c3`OkVfx-I+VsSE*z0R-`U}+hPX-(Kh|;j(+|b5 zPD<=IrSa>=l=XTh(yMQ1Dj%0gPfiu^5qF#O&A6mS-QIaja>Qe36q0ka74Ol-PcZox zkaq4O_be3?5B1$%aPK4_c)v=xsR=eDH!M%$TpoxY(gN8Da`rGp1GczQ5TQIU8V~_$#zLyf#3_d~9 z^Ax96If7XQ_jV`)bId{IDJSX*YTBdk@wNE;p{$eUUgX^t4F)KdI}MP%=E-k7dm;K# zu%;{%sh}HjPHgExjnImiXoBx-^hQDfuPb4dToTeY96U_MMR0m*4OJ@T{btH@KSZ8z zlI|pY?oaO7^Wy8ZQ9{I6_-W24rsw#DBRQQdp9D3psLcYl?b^r&vG+Q7r?Yp6IH@Os z4E!q9&9_zi^elN)hrZ02i@ym_sF4`a+BWoHjE_HMX*Q3K=*R4sF{+vmN>_u{&ECOS zhAq#(MyQDFQ+*rB*n~294(Bt8qcoQU%!9JM)Hke`a^3}<&J4+RU{WaLujIuvaeR~3OTo!2 zJ^CgEf5n{-o4uFBO=)VCez*I%k+2RdUh_0mFp9EI#to`NZI!!n$X3kIiC1h#fT)^l z#rDGoZ8_U3B7ZsZc%3hAJDIWVJZE6D!13hsP(?@s*2#}34_=(j_1QpdANXL+z?#0O zA&>sO`28o#WTsJ_4^pYjn8P~V%aBdSO}?hjl$B_o&+vWTVGF9!T(3aeD-z;iLNd+J zq=GEIi0tfb;hf8RiO+?OQwnuky}a=F(+}~vr9KgISHj=dB!%pT(dLK;4~Lc}q`ePZ3X)7OTPvoCXE7i6(C_0|YtT+UrZQ}; zbG3K=K%)?57`gxa_MCrsW!s7sR%6y#c4$_d#|^cr)#>;?@{MX+YGgbAOt%ly^UCq3 zN?VtP^esK>2-CJRjnmVe`3qY+r;OjKD6@!ZtDmf%3uPC-(U{e8cB;uA*4#*SB)%&O zZIapZ=C1ttrCHJrrEwmEU7GMy*(KT_b#+yTlK4{0+%c%}4+_g>QmKd&B}{tkB0RT# zSf;hb9>v+Z@De2YwZ8vpy6kGQFRASeacbEkU`fx9xlaun#GI-YS#L zHE*OEY}X_V*+`MjMdfAwGK1mG+3I^iq9PHbNA%vqQtr%C}=Uy|2c?kF%ct z$wc{bE3EFYp5)lgy?ZqHXVarncFhQWzr6cWBDEPgg@fGlX+?tF0r{utb2>u&z$m?C zpq!$1{?q93y>gyAx#=In^o#F1HTpKpnO0FHYREld?zo}$;f`8uoNWRXQS0=v73u4U z4p)v%6GiK2>E&SMaBj_pO(^(&Cs{xhK{<;JS%QZyY(CVCR{}!&o zt66#W;OrAY0IoWCv8zTPCqHq?C`3w>JF} zNM7oX;$Z(fL^S|+0E%R646qv00}%tvh@C*)fFRPjG0p?|p4fxuCfVn{tqr4BOnO;)d$P<5!CHM3TO`FJoyj##cr4YDbwSzLCXUVXd1>r z`G;=+j0_VZ#d|;M6}`#+=|J}1Nyz^ip97SBsDu7ZAVokB`nT~x^7MZ+wEKev^q5%l z;Ih4b$TSuF3se&ZaE{1>{!Lx@Z!1-i0A~WzLox#)@Maw6$$!Gc$0Gzt`7w_T?4wkm zf8uNThzJOI4^M#iFZ0c@k09+h+<$t59;0p#<4AenPDB0zHpBy?qvB+b(T)cI^a1eq zY|;VFF-6c{zURLTxTQSKwsnje^p8h!^}&eELrr1fzd%3I9zj&&Y@mPLlmETS*jW!C z8X$a}18KhsXc<>V>SzSmYG?q)W~_&`XGZF40jwrWkWyQLl{SHgE%UHV+W~<|)`uF4 ZA_$L%!fuifsjVFdo@9Os>iA~_{{v4YGME4W delta 23155 zcmZ6yQ+Op@6K)%KY^~U~-LY-kcCw-s+qP|WoOGOyZQD+Vo#cGaKF_}R_wBqMvuf0w zZzWYhcGN&7azm|A#&{(0Yr_G~01vMBuHayacuj34jBt51xUWkvU|?J+Buoq-Ura4P zfYLMDq-7wguCt!?yyO}K5<-zOTo62*gIID@Y3>Ew36+=e#-|+S}+ZxR> z+g|k;uc~B9Ooz+5jrW-Ez~Ap$ua&plEjiov>98lj=Um=hzl;~ZUH)BO;Ky!5AB-{n zR~w?b@8oE}Q&#q7oS>CK!f23QEzIhAK&11bgwpoYGnLWRa}NA~R_qX+Xgg zy^KN#kvXVY+Am2Rorj#tDupNFkqlUjK|CNOv2WJw*Nj0tM$LCG<0l)gMC~jxTp)AM z@~V$skYSwiDhi=QdDki+L_UNw@+BuD`qdn6#VJs+PY-Wkh1fmj`4|rJAEXnsUN0Yv z-l9E>P5QM}TkA#d9<4Y)_c4@S(b(1ZP+Jx4)H%~(HH*(%UO))450$JDXT0U3t}`8B z7gs9cKJ^)~pJ#_}%M!c^ZC=_*)}a=20}M%v40z=^DS`+z`x1RF+D^Nu=_mWcz7qFQQI9fT zy~~^@E7`~81kBPugqjJ14n<@wPF7NWDc#P3Dg~@Pi{&`g%w8b*7G)yOS-v(qOw1)X zwo?0CjtD(K@rNU=RX(djbEJ27cJ65F!1|$C1e# z6WJ%-mG($H1Yc19_@-mlv5@)uB9q?8s=Gi!!zFINW8P<((U>RTW(b*(-{p7wi*ZK0 zh+jxww%GYA6_L93rfkV4)+AGAete9#$XNTGgQiQ2ukM(axnLxYhab-F;=Mm0fTTI& zP7LYi!aW-k?QmNv;_T|Z8SoS1U#iOG-_l2)u4-FifSD2s(PjRDn4xRmK)nVcnGu)*j8nruFQZ&liCA+TuYT zI1yaDgNz2zU)gR-Zg2%k7#UJ95*h>NpAv8(=qVCM|5mb^wJo&IUktYoHPkV0msc>L zFTaWmh2Dmfh50UA`{>%aU9?UN{at`|eglrxxkkECy4>W@b$gC*C*Xc@V3-{}T4ot| z(|XnSm9g6^_9rX0$v!n2A>9>w>CjOY$FJRvMoWLIfV{JqkJ1})LJu) z*deVx7}NO`PdZIc&UJN1oYc!?j-4|{cDL{(PyFGxaVl9Cu~4-Hat%M$Xu&jJWdVM0&F2!6_zZIdx)0#wLM)@ zulU16y0^!RXzs#?J*+K2Y&y4F20k|bmPnAf>b#CPd(|3)X_mSl_WhKGb%SyXB#LwF zB2nzOVZ6^>584L{VrR&`bTM|Xmq1viw-&RJ)PeU2JYTi8C*k;^);ys8i7wr%VNZEv zqNe9l8L!H8S)z7VU*bigwn#XN#jnW+x!nMnvD+vRyEoL@9A$N$cT1@2hD?zeUyBCV^T2k*;Q$MRYK&I1hv3hmQ>rj$tF!6_~vOc@6kK(K4-4aCYIersB zl^dzw&x^*17kqeP3gb@1jckYNj1W9AwkT<9u;+~x0ADQrXAdQh(j@J)nWjRPFOfWE5STHa!5wJXj zPGJfRFtBG@Fffw;PMln=QX=F~Q+hekKv>7Wep0`j%uG)=LW_3PlB16kd2-O;C6hFf zl9;fP98h>A$8J&1=hX%F6E)UT1d0Zihg`7~xJrL?g3=J{j_CXp4cDv^$dHVdIx`9#IE0%9k#>fV}( zRuSIFcJKx0#peRt!xaw#=d|f39AT#s*2oDcE^ry)mFbg5E&AE;Hgu1j(KT3xs7GP& z%*lWlF^+-^>F)&+=9E)cv=?^(h@V_sLLcJ@>p_4$VfG-6R48W_Zof`*UxQQ^*$}rT zrQ9$Zrl?VuCKvyRb78Ua4hRoo`lBFXNKZIT$``RJ(;_r{`Un%IC)yf5_5)ZVz%8Q& zIGFB{*hj#0Oq0qiDmtXtywm z9kX)w40YzHqYbeSW*?6qfSo-Cq(Ytg`}A3nG7hn0NHt8=)G3n5wEDY^U7l)-qlACs z81cC|>Ni@OTnrxL|Ng=Ut+HTo7!=)$lV4qH|XJGW2$DHdg24iqs-Xq6^+zizcv4KWDFL7ZBkvZFg;J zAfTB!s1ymT+6}y4@oIBypE)FyC!a8x{Q_{>Zf?ezyVen{60TQ+dTBTa+`#`p&u|=; z_a>hgCo9cgE^_krt(E0u{qu;59N;J#kHw#^(#|n)SYo9il5V}6pYF1l@akH0^2)l~ zl2@8J$g##;h}d8hYmf6`w_(rQuNuI9fs63W%%hcSpBDF%sJe7#S7GEr(`vO!U{A|u zp|!>Y_nowt8-Jk!(NA#fP!e-rT`1t=v6iv;%F1Q*S)VtjtH;`?s;f85;;Fydx^u_f zSn9g!)6e3!z~^G^>pSTJio!iK2~YH<9pAzkgD^_;(kZdNAhv=Bp4Bl=rqR6}$EE4Y z4CfCo*?h9rhK%dzwZB74zcmS zH#__yO#%zYj$Lr7^)V|{OTFIoAzydQ+OD7chqf5Zhp~-?o1n>^6`!vrCEC~E3;VoL z*=ZGj)TX)ux^cBok}@*LnD1k!?bFd^)7&_;4e3`mt;aDI>)lp7R~cIbj*Cy&auW!g z;`eS#b~}jYCK)e?hV$8@1O0VUrz7u1^y1To8{ktsiu!W;z@z7|d!BBp07p z##>8GxSt@@+q7MJO-827RRd#{@e~|d8s#lnf&*|s_>$%!MN(V2nXTkesd?bVLp~mZ z(dTW~@^?~6L&6o^NN+rom6vs)=BpaC>=&wo`I0GPiiv1uP&2C*!A=q|P&3h^JleWd zj<`R4W&bEZ*`c$}0Zs#!C{0-f<7w2I$V}>{y_e-3Sr6AT3V$_}y3=FIQ{;H)WZ|$D zhaV|{CWllrD=E}w0>FXySoXKUrs?3;N;@ZjS2WVpoM z=jej%Q7H@{i2_>6+%*tt1~th;=QH^E#)+wv-NkLJKe;& z0m-ze4K`AXE=_HZ*BGg0{)a(%C4j6|8a5C35ZWA*PpTV}pmf>MX%V@CUZeqJYi&bj zHl&N0{TVbuoulREgM;FxL#3~o+uGeyViCFYkg}qQ-Oh8qCDD_euSL12?vrn*d=3eE z_f31Z5iToOWLEdd&R03dd*@H9FK4cNf)%V@AnJpORjM9Ll_&!(QJw(eg+-pr1^&3r*XXb>X`<*-Q!Qv?XiOiyH$P5`Nx=cd(um{JM<#!Rxt4r z1A|iekEA!mz}WjfTz!knOp;2E-N3KDBr)B9#`-ZPu3Ao^V|n0g*3J1J5;}e-1m7 zt)H2qr9kQdvZ4{894NYlEM3?DEdY7!%_0Dta9N}EMEnE2w9(r&L ztsflPT}s}a(lzqrb##Krzc^_@x}5BAhuX#D1QAW^*f{I4neJGRD2nc_HO|#Mv8l0v5=br{5F=oKSx3h`XjWK~V z5&M<9BB(!QLkL$RnR50~G&o!H58{?MdV^JiVCEmuM=O8%L(IQq>A%e!4Y7^Njj35tZB=Xs`HT;7vqOCk##=)OLHl~>R)KTi|!IMzTkdWJt(~~#{TWFD4cM5 zoK+8q*p)!K(D|r*;{WO#{H^>ZC^E+U_DL$2P&Kdqh2=N(8~bXC0CXMeK_1<1vrfzF zEB)^-r~_JWj`$oS?5Fx0w_n`E@8iuRy@X!s!e_#Zq(rH|?Po_y<6`+9TN%gfOB1Rk z=^R`WZeMVQu!Xk6Xl@jrQIusRPEh4>*{L3*`KkxN>XrBkz+!w#C-|HEDjamlu%{w2 z;BPqnJnD&sBeo~#1k`L*x~!Ih)`` zmP*`Zq)s4>mnLP6cT!HZW7vf=%fZXx3EpG(yOb^7xE=_5M}hK=-FZt&9k2dPeaenB zdHl^LYQ+$9jV0FE5u;^aN4;!w5khq5v03LGjxFkQsPz^2OZ${8e?ysaV!={`!h~-Y z8`}*zS|&X(uA&~kHlDO0DAPS}w3#k1uGU6+&*7SKC1wHk^Sx2Ryx@CPEPKD@$~?g_ zvqZ12*tNm@DH{|YVUTb*DdP`xWhM{wtQV9W)9*OS8GkPdMj|^y<9S><-1Q0?5kFI} z7{hQ$byd--s-h&~22<>Vx?mhjUJobed(Gr1SL34EZf*L@H*Vx*fidWzz2FPP2@e1lzFf?8I)`udv~vz zaNmH-G9N*m*#b*mV&qoO(PjJI|AVK!e-v0Qj)G1NA7?_e?j``FsU(Tr@gN+7t&lu* zWH|5IeT=5jPj(1SdSMUChlH(S671wM0U)W6hfg8)AC1Ks@_+LB3{4H!La=wO>IbbU zzR+{jUxz8{!N>s@v%2*5(wNMw+UMGrb zDuriOyqOb2+8&3|)`15-v8U!!+hRtd9~OQFNbmsyr)fyahU+9@x(E-{^OKOJM8z%^ z#}U%BH9X=$%@blAUxh>aKCO$ifu2m7wj5!KVIaguYc8zAYmP2(atkJfcX`cZ3Ud&yu3Y=Z}8}$g=*Z-tJ{j;Y<-}7JzOD3 zpNBcnpU1yts5)c{Z=&nl3uVrd%Ibo5m4RP4WgIHN(l&9-B*lF4+Z# z4mw@{JRx-f)J0oE9fSP+we=+17YOIRIa2zf+QIx0YCg4r+$(!M7Z*cu zX9y-o8^Jaav^}1QSPAzpZ3nS(zeq!xu@a%gY=cH ziS!lWHRkH2BOysSC?&bi2%B~1sTq%4S7=;AiMPlx+Q>gF#rN*8Amrf&(2r*)XH$|u zA*?2EXsB^13W4+5U||3!{uwRx>7SgvpJPCL_X7&Os*0l6nx!0X+1h2~1y|!Yceoq& zo5B(AB@Qj2?x|b7biGLHj)c~U!7T~SAm_pY%`FjI37$KOyeu(2&r^tTVs*q1%Qmq) z%SsOfKR5ccIS1IwP13tMkUQ5f9{e48;U_q?bcHgnpqx}=0G{(#Zb%p4?)>FGoqo^) zvsU4Q71s8kH#hcWD_`vFEy!$x_}~`kGoVoMPpIRl=-Z6sg5fASBt(5l*RatwaK>cSkZVyoH@E5B#_!9nNkLR_a&ms_S>t7q*8&W<0iy|KuoFbqU#L1#*HURv5;&tI5 z`dnI4vU{1o5V3V6EcK{k1AT(!{S?NhP-`_F7{!yAYOnf5^-6{nmKHqCv@|r39VJdp2Fagc@d(6&{hXawFLc0DCdZTape+T~aOCfJ z4AMzCPA>Wpg{B5iA0I0UsPCK9{;kW^2vXQfW&2@>)q)Lf1bLK!bfh@(mvHSmMSEZ= zY)G`@{u><;^u(s!8*J>2Mdgb`d*wm#PsTrOi2r0FspjjceicR$9f})CHR8%EkEywfLNF&eU!g~TkY!X6=VqS2QktVG zF#*^8yn;vheT9S)?EYp2%?#50988A>Y%t(lHXITXWR9?<_FpPJ`=#GiC5DK5B@MO!GnE6zK z@VVLCyCCHmO5v05lF2u$Zb4!F8wQPku;05)%FSy1t34sd947cy#=iQ=bBju%kIPl%SMbExxgdxP)$*{!+ zKI0=tl<&!{GI9>DwuQjTBfr+iYXNA~Fz2&{pquEmK;Gg;Rn7Q1MjriCD1u;ig79qq zurJPOPV z#%|%9WDk>Ur)Jw3#k>{1LpNyVd+yt3{+^gehVNSnB$opetRx}%-6FacenqY!BVWPqq z;dcJ02s0xx=mv5;tg1wR{RVGYlvbyPC-WKCO@;;o%R)+Fb!G;+Eo!2ErQ%LDNuVPR z?-P*(nqh2VL!_1}!?p`c2bRi+6-qQ|NU103>zpin^nN1rJk=eSp|q+EmQK094~tp7 zQ@@LC1W0L`uQ$t;r$o)C8kVh2m!}s)7?aNPEdki@g~}3&)(LXm;ZUP^&3j zRxnZIBxOc@(amd)&eC<#=QsnTdTUPT;vJ95Ijpor$5IVBG%8H((4SX+6IMAQEi;^y z6XF{keojKXI zbgi)QTHBl@*~m-FzGmUPHEC|xE^EECh1weiS)MJ}jpmrn;yP?Z#tiPKt;%1mrP*OF z+e=j2e;m>0(YBZJQqWb;!D3mZT9%>3@QE^$PyxT-vuOZIit6du9KO0e82xoUwo{dD zOwajjwm^{0uk+6UaDC-^wXtRZ=K;gT)nGo3S#3j6f}E(5zjIqZ2@N6HOuz1htAM*=qMd@&$D$pZ8U#<4EA`_~ ze2Sa$WxvZR{9nIZM~p4?M4e{U-AfALVB8nH@>)P8=Zy`VMe^t?Hbad!&`oU>KX{1r zKDD%9rI`V`{STa>KMWrl-Fi zy9#}k{#+#9Rqv75FSKIs7wa0DV?y&c$w}%2h4jKdM@mUr)4|^y63&$o-~}SM(DuE!y~(i#Oq)O@0R}-BkrcioPQj#9ZS%u^QQl?iv#_|I&kB zz%|}KweuqMFfvsi(0bJ!;3o6jjiJ+s@N?adPxsPSovJ~TIeHXI$WKh3DJp6@xwZXAETnCm?IJEg>8neNoHljNwUpw^^S8StmJK&Y7=~@KP*L z9Se#Cln-X9SoIpyEelqOTFPK z`6+h#(6GEISZUr@r&wOgkOH|b27lG3{c4+6v+y*F&R?4HU59#RDq!7W@k|zz)+2{) z3)_SX#RT#ZjBoBBWO%lh#@{U!k-J~Bd3i%rX2%N*2RGQ=o6ijnYYkuAXmmhLOlCt2 zt4D67Et6}nga_+4eHQlF(^T9sUV}8}@Z&_YTz^q}v0$`~0M*@;~4*XL0X2VO_Nx)h=vF>Y@ z!^NrV<56q&F+oNFNP(!(mCkB0B=3}6T@NSC z*1FN-k4396=(C=qo3cBR)Khy*23S=7rQf5!aC!v2x0|UeWJTq0 zESCFv9WZe262TEKtseIChGKL^_I+@Ub@1iQqs1 zxhZ>MHGxv-RIHT7x`T*B?C!i4AtqDvNmd(OP=@IuKgRi_rJi{bVgf-U^X6oL zA*`-x$*AZG}8HX*H!Z3h^|f8{tjaeL=dN@ft-FixS$x6{a@D1qa!Vpi=i{oEh9n=^|nf;aEnM3mh2z?{A|KiqJv{1Q;0Y4=^zHlqP?Q zlsz9tkcuDdxHyJ;R1d3{9pQ34r}idlN--Lu6qrlRL0*`niM zwNvkSFv-VU{)e7o3@PZ;!Z>Ek31aM}wCe=ertJZ&u}HBciY15vP?1s{T+&{eru=SL z#|Os|+5E%8W&j-7bzM9*9NK~nJ>=B1rd^72R309*_mUe$Rm3E3?@s6v!f})kHz2FZ-1Tse)P%RSu;^%8}-2A ze$XCO2=6reOEPrj(pF${|GF*0%>&c|0m{1+pBf+ODBoE9&XzB~i%bscUK`FmN6PT0 z7%#sRNxaj#H=jv~gXm^iI+U7suJ_pA|6q0MYEC>I%Ifqr`MpW=J_jro1qsI>1qFYH$yJTBG3CZ0G zTi0+na~=Bh>J?Hu2UitrA)@=6q=eXl`8dJGZAd_0q($brGg}9yV@@g+3&7HySD#mc z8v4v#kq!|tY1IaAw)7Zft{<8hPmZHFvkX)i#B2Q<{C|NJo5wy$sr1(ctx1On8^Vgz z2%{pPC89?;q;jpbD`+n+o3F0hSA&Xv7JU!Sw1%WtJ?mM$mwlJ~EM9c;GJ~aR9B?T& zx|!^p6+G>n&G&u&`g`ld3|8u5G^#U|S8Qo0iR(rH0H9-AFzo4q$#u-!M`bd&)&BR= z4q(&dU4dzwaZwwE5lUkPMJmMS#-g_IJP@=4-mh=}`nZRiFj(oPsgJ07)#z61FRSUa zXsX3+=Q>)qYt+>107RKD!X24eYn6P4jHGx@wz>2zZF58>vb*ty048g;OS0J>Qj8ko z=o48Mzq;ylyE3`sk_#tM4d-ui(%Z`QJi6y-IQ`jZ@WYFA)~j_v4R%mDj}(CGi%3O0 z8e-w@$VDJ_xX2>STD8R~@p_kyv)>HMQT(=ct9434-uM>e-yfVB8ga7K#(G^URLB9#)v)J$|K{7-2?e5#khh?-Pf!%pdSM)SiELBoc4J-ixjHiurGmf_QFdkN zfGvvr=O?~w!A-wBLMK-4P-L)4&!kHPDUb{>a?%ao zb`L$40Uy0)66q~dxkuDzn)wpzzUpnAnGnY`?tN2935~--rR_h7`jfA}S;J5G>I#LXU_Qr zdOzc0D2==3M=z?Y7}HNp#%q?M5^PJQfPr&EV8vxq)L}3#moJ@YuOXcdW5zEZK|XG= zj7wFN7vMzPa^qjyi}ZJC%vmuQI&-XP%Z@e0#pzTn-%W)dh$qk?_lCGv&WZU!Vi#X) z3Tf@iJ{xu*_l9g3BP&s?T-BEHWQIor?;U=_D$QTWUhDJ+pN}vWYlo|QpD3@thZS;% z>B*i7;TQR1p+ky$7XP3WR6akw_#{|z+uT$bMvZ{Nn6Z_v2EN&+{k;n|7ER*}#EA7o z0}aN0nB$jo4w|L<4FObsQQ{9tb*w$TbHmI@^|qmas&JJW1)m&70)lPcUVoakpc#&v^z^;S=)3eqm{u#{A@j-T|7nUbi;-ccUB!VPE(p}(2 zu`f~XnPt=kQf#wEY!fHN8Xuc|$W9Dz#Fs^ec1&M5oIJV%Yj$xG3iuUCI256O>XD2o zo+>JcLZ=JZAy6m1g5An}d|ob=>Co)dSKq}?eK z9A$j}wW$>-Ffhsg6N*WB50V5KIH9Yc3VOY1YvaVUO4L==VKEwc${EE5p{5O+W0)#f zzXt>%>{zZlcxH_LJ^<~FWMsb@`#nnmPMPpyp+|;MZ>I7D-7IIWu3m_Z`XH?Lh6Cf1 zgJStqy^G^3_>eU^YD=n1%1dSv9c8q_9m2&sU1t)nyu}6@=hN!F`SUb}DOl79<$6I3BWk&a8FCY`yh^@QxTyQ9)pi`qjlWRF5X(R!?B>3% zR}aqBPi7nH(;@l~Y*g6|vrPT#R{$x^O@iVRPwl-T-ySb2!K@dyzbFha;XhkXJ2*2Jr z(XY1?q_1aE3vwAV;o*xc5?7j8I3UX{C2N;~mQ&R|4wxmf&|8#BB}DRDL;e&G4V5Q~ z`j5QmPmr$58{Ar@7f-(>eV#K$-5&oNCNH)#kDzrJHK#Rem}bCZzEr&<1%2}S142Qe zA%BEM^D3?4B=w8v_`QX(-=>uQ7qFi=*570QIZ(uZ$t%JC<3U}aL?AnF%ww#8-{vVg zRFL6eU_qJ{*@@Uh**9?3-EZP*)?ede_^Ab6lFfO|Tn2 zL9*zgJxhj-#eY|dmA;>W?*jhsy9ITER+gUs1&|w~etw+hJm%eQ^|^h_+>CtZgTRIP zchZ`mz3K}oNlP%y&_VJ5QteoohX44Hz2NAUQPAz-kDh@8b!#{}(O$lAk2rF`Eu(|M55_KckMzg+dxk_3=(~F^gTG1Y~zHS;FVO|;6{~Axo~P_I7fH*W5D2&&Z%y# zF!*qJ=*yM9g3{CcBYwHmaUSDEtq2XdoZF8CvYOgDPEZYSBE);We}r1bx5B*Kn7Z6l z?k}|(e4X?`%l9s_AhpZwiTb-dSV$IioW}6`0B7uRF=78|yF9F5$uAV(@t1D?XcdDS z6o0SHOvhca4|xG+8{R?J_DZo)$n4YLo!>It3LLCG0_m-@bzdhfWjc02bYDCUEjMnd z!`T6VNq#O6HES=~*Uy(y`l^g`n!oJ{Yb4#h5>BS{40U&%YrFsl@>K56vRN6;5R{T} z{TsDCR~{aEZj3Xv@RNkg*Zv{^IvM8Ha|grbV+o-@sBj4g!h5^lDCau4UE6Z|$>a__ zt&a->ph;{)KH&OtIMZm7;r;E_x3n5=EXzWk(?w$gW3p~*kG9N+u1L=lvP;(i%}wu(Ee1bhDm|Fm-&V}DYsZ6ZkygO# zeq0Dr#~N;#UZnW7`%V&0mpQ--*8)Fdra0i{EjjoIZ`dCLA22p>wvi*-0kdo?*;S$l zI&14Qe`DT%LH{b;OSlU8xi1=j`}v@9$JYHh>n$-L(L>i5Ksr*NniP4-^>+=jq^o+5 z^IurTDl}K=G9VsrHKmP+ykL0QlFSFPLE2kwK#$>{!#|C|U8p6~wCX+d9kh;T$YEMMh7DtdJ?>)Vq7(C-B;l2ww#Sn_ zmbXtee6JIOKv53)O=U$*hGE7QAbd_&pf8ZWF5EB=R#0*~DwHz`m~hsVj%dY=^JalD z^t+;efRcy&``)k`lD<`2-YYlzd4|h&T~KZ%4FCr3?Ydv~U`U9EFm7{R@ze$Zif8_6 zJ&uEp5$sp*i+8~}-|FFjVT(8A!i{ZBdJq3s;*@(@CU%X zp-IPU?mSXIbN?MUv!j<4LV~SF+%n4E)XOSB#q0M`+Srn|@GicaZ}%@wWlCWt$LdqPv7 zp3oM0HYMH@vGz#bb)g z594q~)|x7O7#X#xRV0=IJtV*}IpvOIqa!92{;FVaU;9IZsw9H0l=8QtNhen%!yCKz zpw=tIqwjeN>HXdYG0_3yhqIgUA*znjA(yR;oHw3EN~NWLi#9b&RGORi ShXFjdJ zbbWos)sM;}Zuxv8IGB|f0+cYad;(>K)~~YpQ-5f!dc0|4e&+7~22nbcdIu87C}LY& z{gGL+&^hz1>ndE;y!#^Hde`Ot`({sEJnw-={2VwNrda9}V8I8Wg%#h=u#MrHV|kCJ zsVC9D9BkQO_l}=aq2hM&33@ZNWiAXUOmhr*M(1GVF#cF+d1Sv$;A1-}-Qz+-740>R~L?URD8@$h)TW{{q*>tM*P?y{9 z3bmC@;15K?Xy-!U)tn==Oc3LYmo9&VSM0M0T~O-qw(aBAhs7o=zF?4k;a}le_`^9? zFw+fbEA2J}ecSMdkBL5fgZ*#cR~3aLto%V5p4Mt zjBfaHqv=%Fyh^igd8@6UudjEc!cZ(M)+3E!mB8fI9&e4lSQ7$j3=PPEZnU_teX4IKrO?> zvRRNh_TramLpeeQ>Gm|h<~?wPRLe$NF@yq|l$VqbKz1!;L+V5Ia0OQ6w@*FzQ5yb- zz?_I`)(hA8wx}6}N5-n8k5@4XN-0@ea@8gldJa?uR(*uvA>PO74S%ExvOl0fiVtSz zxpc9XgP0MERsxV@eFr%cLWWiGrQSMpC=oGT<8wKWgHcnam7zwWoHRTvbB9hbl+c-Fm*Rv3h+FeeTBE&T(3?O#;Ajp?EU-U`(>-%Dql*zW)9F z{1FJOrA{11LgH(Fun{dO$ibsxEhGIQHqr`%#ZG6*Nx@QKYABIJs^gZU0J>R{OE+VS z&Cs8x;ecw`v@EjJ3men-w)B5Qu}mX9`PtrcrBCdRQ*S^+;WO>Z@Xa(dRORg5;a5Lc zXy4^w%S8#vUuU_DspP#}{zrMd&ZT=WjPjGD@{cEgEy3_T{nEbYgiM+KW2>U4@0zE4 zt+n$sD^w5pnr*Wn>dUTc9JH&c@DK`$G{McHyS~@zF?Op^q4^ke#jEM8KvZGEYu?YN zh#U6s)IW_R0oJa6e4Y*R3+KhI_f-lvk9K*R27nkwZ^w-&#^|b^EsNSj}*sK zGSq0#H$`)XhbbS&S@qGjG3>t`YjR4nBfcQW{o+Str#=)AAjZqw0bR=?Z~Pijn@J@7 zX11z2AcDlD8q(tD-!HcC@gls>Qy~?QB zmw^8SRewld!PGxDl~l>Vv_s+}oaN#Yr3FPa126&UIq%#^y*UV+==o{Z2UeA z9K4nv>IHe2dXDACiqK>VXPMB|+Y)4$(R{uPm$W{KQ`B3I~LWSQADd z*pA^YO$q=0%T$CD-Zb;trobC^gU<1#{w(?{qwl5v-1fzMN4wCvyNl8mI_3!*zZ40t z*sBmWD1pF5=YWIHrZX{DFamTJ2MbRAbag1|8KzR4h8oDa^}|TaR>k~ZS}x_eejdNr z0mK2dXZ(@ZlY!_)-YvRAB#yL8-)|n$d=a7Ec&HEL^j?D8E zNvlyV4GQj2o%3waXYX>K1eoKQg&hi7I;2iHmKoy6bdDpLRBJJ1qL(5G{9U8HgSP%3 zrgQD}XH#ZVU7)^8ewVh0r-gz;OU+weC8tcMOM(_r+ERZROk~hx2WxCFTF@R)M~>oQ zkb`3&!h9oLYp2_Lb+roL>E0(_e84L>`FsQz^t2w&jrr0|$wkJS;PScRCY>b3Ga#B8 zR#|)3ngQq$F}BMJZZ{RhUitEkHO7$SSsSC{FEbbJQj1(^FAzun3+3?p789{e<$`{) z`%FskI+o-Ro+FFiL=5orZ&~18x^X;)1kUf0HN45t-~Eln01z3Q5X_hooAy5|DmkrF?UR z&)EC%?{kpYHJOZv-7dv>v%Fyz=VRBoGTRV|+`=RjKv44=i$zaM?I9&g4Hjh`cbW|& zckN{d=j4%7v(K>*P74pkW(Pev76o=B4LWR{n@ApXW!YF|D%32nPSNQNm(q15<8I#w z>Pz5p+k_>(Bm8eQ#{G0S0{y2H8vjM5{;yI{g1RiubqgO#jDJOH7nS`{6^5lHO{St! zz+)XqJaeE)VKeWR^K2gmK`ZVEti|9+bFk)rmc%->VWkyw5_#?g z0of8sf0yw%|6$jCyck@^>09tzwF#A?GnT&151nv(5jEw^_6Al30OIM*UT zgGzSJ;hc3^Loy=ZTd3!spt4u%i0E2F*#EIGyPaD#WxPTj6FqZkH+G=D7VnJj`9tUb z>*6W{qT0IlFd*F>Lx+TPcZZCCNT;YUG=g-FbdCt4bW1nV9Yg030@5WRCDNrYm+yY> zeZA+;S$ps2th3MAXP+NyJWKK^KW4zW%X2I`gxh!*c4ax!MUcG!)BEpgg&1eQ*~`40`!0PTlK1(n zCE0NfF)Yf3e%C$2@4S`4G;?8##LA_5 zHsdYmaI-ahlTbSODC{V;sRzn8|C`N*2ec#7^=Uf~={#~0-6H5kTs18ZKm*KWdo}0GEqIw(CBUMs24Khx0W<}ugh zSL|0ZzUqq>3^yd%QjS__5Vy_HkcFzJ*(E-45wpp^8S+Xb+AtLp2}~B{U}d;4v$I{{Efu&%4RbU%q*FO;zQ@^k!BElPmOe8!;flSX+b=0z zgJgO8w1hWI!e-ZzCNqjf-1DZz=I0LLgu4@x3(W}<=P>%I5M zCR=&d1ez~>aIpOL-PP6s15=A(z|WAfo{>fN1czW>%4i$sd|qcOq9CKyb}rOgL%tJC zFD%d5H-)r=6V-z&W{ zT`(HarfR3mG9Y$nUJZpcF#yoZc}MAFfLErWLwQ~~yl{0*H^TAK{Zebrbu0#lqq4*S zf~A-U242Mj6yE+2P}2Q?#)lae?xm&~A2{$NDH)($m@~zBs$G&};crk>rmy@sLc6r= z@TFsk|Due;;r{88$-M7t-+3QwD6^6|K{AyGMt$o=Z0ujU-A! z(NL|;3H&SQ<&Clo{lHcQ^(}YeFHQR0jcgr>eT#{rNTYR7x|^+;1l8X3SA==;w9uJt zT56xf){bsRnC7LF!g`zp*-@d?=vI&v&xYWJpz%6WI{ptz`q(eFQn8b>6_YG8^4_HUVJUKlQvVjgv`uo-O3c6wQMQ;$p!0yE_&s>sgzWv1N zkHWsx#SZ3NmDG``R?@0<@}SE^xNhiV9hs(+yVmv|yBQB23!7)Tz%+-vvNda4)FR03 zTa*fpd$7UoO-~`@$Boo8-`%SCOnY7ZOdsltYe5pOZ3@)HpUY5e`!QC$Us9%io(5Ij z^~#Ig6?S@mz)93N*JrPE2=-fGZ=bRAE7CN;Tz5BRXgytvdV2z-e!jWR_H_n5-Prrn ziO8i_wc*yA^!guU5~(d;`bKiFZpL$}d&*>--~%48;3wUdrI_JmHfH@XJ*AYBNqXr= z7Ay;tvFHd#_qcbOf_A;k+OT?ljdG~qtLZ4`VeG!3qZLiA)Ci7c^Yk_+$;k#VVjdA^ z_QLl~EfvgYytjd>UI<@)t*0P)*MifT5?_$)3&tz{h&pTgPmXrjzD15O6K!9ie3ua0 z8ftM_N>~p~bDcs3zTg57eW-L=ZEBEura{o^?VK58YVduuRR0iVM*z&to*$ZCWp_G9 zRzh7TG&Rk;{JF_vf9_kN5Z|SNlb~mcp4pK_A*WrfQFDoYs*4SodA|_nORnZp$$?k8 z)b6ccu~HU#Elg%(Je8{u-;%4N_c9p;7xb+J4I2cA9ZT$@Q@9yRip5Dw)%P5&_#D0t z@|XfM9zfVhzv|I1IM%WZ_jE&PDp7?qR(Xd>>kQMiOTOy1y(MKQpqcv|9LFhKBSYQI zXPC*ap}&U!#U1zQ2_gK9mM4gFO*7yCuBnLQY^P>aEp=Sc>pdAGTr^q?? z+tXcOb>ce!(uX`nO$Od}(`_c^YL~rb`;1tP6#USI1?3;1nxLLUw<2ipDs#pWKtC`K zL`Jc!8$3G9YI_?3AsOkKQ&XrLBE=W)wC%5~iO{{4pXPmnXYEAdGLziOk5Mn-Xl{T* z;%zQ|GA$@7%_dmfBngMcecQkb&yD|JBfHCSE=!4QOr< zqtDoC5>7L$>97D1!uD)Z)Th*tcNKB)dvnE_#Y*-FNF8k0Eihg-9`&kIJefHI-wNr7*ET{Q`jlQkLfa)c$r$+lWePZS%xpN^~+@VX2-NH!36BUG!8{wk-) zzWEE@?5u5A6T{T2&~@$0)&-|%fjI9J>IA*G@&>f5PhtIXX^OPp#riE>0^U#xF$;bO zdy_#}8aVys#Q`KG#ZkWRM|whS_T+nRx4E!M{`Cf*17sh3UE;h>jgY9Wkj2s8jD_@b z%E!KRg*lW`DKWNg=)jIzo!zboAJKviyy^qjXR8h}elER!uX>N#x+tw)JmQ)sBM}Yy z9P|QX=O|&(gO;zr>7Fcb#KKg;=(aBBcZh2>S+qu~q$}IxNqn|^7U=`I(Su}EuC1X# z*Rbk0PsigetK2z$c}7X;MzQFK zly92M;zc^5FUorgz_fmve<3^Ei$dfle0>p(6mvonGz?`ST1e=9p30m92r)#wvZ?I* z*j&~mx7l$#ZCmD7oZ2R`!gu)d*RtOQ%kRbYQa`u)LAS((E6bc&l3W}vbq3lsHu8t{ zUgNeUR7`rcLpw9S?c)6cEd$bq-f;)N-aw5Aj=~O>9TVl(!{rca{Z4Kqx=vYGIO}z} zHu4R9xeEig?PI&e562tj;MZG{02@~Dt30fzo{v`O%L5pp+dRRCn~DHOVctmraCo<^ zcVt!6$VV%+At5mh{nVjP<7!hYh@6wzP9qxJS~Ug1GpIWObYR8zQYQG2LQ$&2lk$c^ z*766!uYZMiQW{;hE7s1hd!+)i-T?!DP$Zasy@CyuiKLx1pe=oMrB-_=C^#8{SN)cz zK{L|5oh`EuFCeHrW5F6Yg_ti(&~+ZAVLRRfr*&%m)S)sdf~kx+i3+t&=T^2Z(1Wipy)l!^ zTTXyL4t>){IJh#5wqm_hFyR30?e;(>)-n~;xw>RV(q=PSu~MB-F*{!0-5$aGJ_sqs zs~!j@CUU}TA&7~=p;kAFoHsJL-S$U43ys}(=&-XGF|UY;aTK$yy+$eCL)8?=6vKs> z-e}JU>-#--VM1MMH=M=dqapP6t_r)G6wB_@Cp6&$Rbs^W?i8Y`T`q!@VL9ph5X$*T z^wj9o`PC{&h#~F~$7kJYE0u2#(M$LJV9!M_@U$%z2J-WKMcmExXn@PDG&-$O+^IwA zP`m&*QHxGk467;rC~dq=|5oq=#m{1k;4bI2#mn?S+p~gpRXmI+tz#8_PcLVQ?SYpK zmua`ooun5+iFvCl{+pjvFL!ompYh(d?7ZtKdnZqTb>`CIoK-p|QK)Am>x1Nm1~+Y5 zXOj;~S5T4c#bev7uQXMEvx1w(Y4v4Q5`8~Wpf0PcfR7j#UYMmJd^}5PUSBI11)*Ci|GwXnHml4ueI4`A? zGpMt2O$t1m@iYGF9Vy7=QMHiLn%{H+Gy{2O<{QIjM3uT8LoqhF1~)_!q65lBiiSr$qNEEtG*NN%CK{bHn3Lvm z;>t%)<>O3}@bP|bFuI7o+8)+YvLXM>GNvxP6bem)aV<+d8Vo`W-Pz{Dv1*`pH%04RR2r7RgaSiK< zE~k|_;~MC|xl?W8qU`SQa*(76_%h5&m1UPx)==+TYJhSPF^k>T9YI=o_K>HNIn1Wz zCEo=g;rhl$nMaig?w{~Pl+ls=Q`RtMsIy> zpD23M!+O5-?R%b&i$U`h{hs**UMx+y>1Ip5$t$gVDF6|o{>hhUa%&fHvl zYf~uW?##idEY#(e0FO6GV?C=p^=;v38c;3T<0A`f0!@W;R6sLtJr2Ja)G z^R`^>I)Y{z+-@XBYE}A5Ml{mk{S)E`JIyt)0QBN>b|v~}80k5Dv00e0d72q&k_|~Y zrs^&ks%8snlHSbwYmQSR>29otc_kNs{Z&<4UfFlceNY>ES*@9)lA6rre3}c?rZ}jU z=rR3{{x7b@Ui!&>@%UfF*V1a0PL>FR9s7%=$M78&A7HEMdk2Ahc|k-*D33@wx~nEDei+VP4Mwpva*~1 z@hQkt;7+mXi*~g--O;Y>M5~&h`R%zjy);JZ`9ppcZRyOoGLi1qx4*D__yZ+e`I)FH zJ~lGuI{+{pR+?zkLF%YH+z(7K(v3E7U%K!bE^KwYq>sO4O?}Xu-56G-5k%8klhxLe zy{1$NJ;(LPl z0_lw3Y)oBSq4JAAm}X;0Ca6nBYaUiI!Q(9rq@PBN(X5D$dy?D;W4T!PDyytV9TBc_o{7&c2i@zZlLFW2tf_X)Ugt4DW~HEsZ_)9;EYDq&m{L zs)ITb&2aueP5EhZTT@fik~sRnZyKndD2(!8*Jr0CEQ+-4Z2V19PUdL%4o{(r0lWPa z06}~v#?cxg-y)KV6FP!jsjU_f)6PDy@siIda(LQ-r}5VUTB7>|SgwYL;n{w`+x!cE zP0C5tQwxs}Hi9Qwlv4iRwc9HySH;SLNDHBr##TCAEcu_%(2k(7Fl*!YbK+lg0#N$**ex@tTKXmXj^gxYc10avty@AzNud%2X+lD z@IKXn18t_7VA3^2G{%H#2i7EY&(M(bre2M$99rn!aLLYbbFc4gA*%@mI*eYdKrmE} zjRI>=6+2O(`K#K>7fc)yTN;&=1BDdzghhffO@@*CJThQt<=EXS@di)LPGM zU2u`J=ncPRTBECkR|s6#?M0>P|h@d&h7q%bACRyQmKn8j2T{4O}02#gOQ%hiZU9tF$`S|1c>aEykYL6zz6;}~hDQY4k0iuBG z-T>nTmON`-_h}5$i`@0|+$den=8xtFa>OW0IJh9*FG9d&*>RgIK?dlpVu}_t&`~C5 zX!|ug(=Sb5K2PqLH#maQqhkEC%46$=j}@+MK2WND;ZF_5CA1lnhgJl6Q0)2T1$l_# zhY)d=FN)z6grQUY93j$pVSmHqDO_;ZS+f>{3>Pfk0>RN0V$g`R7Ad{$XSF*nuyMEjq@n_lFdom zoCoz;BhAZh8iLOn-J8hw-6=CeJY?s_a$4z*m2G6h&(Lc`n_Rca0dr%)zu0O-gPmxR zC$z5*WbgSWaF+?{YOIM4T6gy zxbCauR$C*m(f+3{u1}vT=-;&h?=JcKpcRI{A0WFc8i)t~-Ny-ps@*r``bB`m;CmC+ zuLT5Y{B7dJ5AdO=!TATwP}t$I1I9o!)4LH6{JkkYTz#An&N@hqLIjr^e2U@>j~kRi z$%Ky%ngcm)-~U?f++Yf9`pG z53(H=0j9dcvxg=A7Wu6VlL5m$?#7t!v`7!&Pd)F_AR~mJKa@&;IHL9W8%zwRm>{J2 zOCj_EP2<<-PZaz*9oJhOxOCD!LyN}4W-Su%~-uK~W(%rwEeYJv|) zTnk@rW`Z}~ncrLPOrE=Mh!D;(Ne%qb4p*Lp0J}TkWs_FGL)_W9opL;1&~w-N9cQ=I6E-GBA|2kLbfwg3PC diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4c050a70..bb5158de 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,3 @@ -#Sun Jul 01 11:19:29 IDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 5b1eb03d6c5f3afa50f37f3ed5efd3bfe19a5136 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 8 Aug 2018 16:58:28 +0300 Subject: [PATCH 374/592] Fix bad token in javadoc. --- .../src/main/java/com/cloudinary/utils/StringUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b846009e..2ed8c4da 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -244,7 +244,7 @@ public static String urlEncode(String url, Pattern unsafe, Charset charset) { } /** - * Merge all consecutive underscores and spaces into a single underscore, e.g. "ab___c_ _d" -> "ab_c_d" + * Merge all consecutive underscores and spaces into a single underscore, e.g. "ab___c_ _d" becomes "ab_c_d" * * @param s String to process * @return The resulting string. @@ -363,7 +363,7 @@ public static boolean hasVersionString(String url) { } /** - * Merges all occurrences of multiple slashes into a single slash (e.g. "a///b//c/d" -> "a/b/c/d") + * Merges all occurrences of multiple slashes into a single slash (e.g. "a///b//c/d" becomes "a/b/c/d") * @param url The string to process * @return The resulting string with merged slashes. */ From d7d6ed8658582a89c85d428bcd2f9d3cc52a4c5e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 23 Aug 2018 10:56:37 +0300 Subject: [PATCH 375/592] Call `isHttpUrl()` again after modifying source instead of relying on previous check. --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index deadd74a..4421a6c8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -353,7 +353,7 @@ public String generate(String source) { String transformationStr = transformation().generate(); String signature = ""; - String[] finalizedSource = finalizeSource(source, httpSource, format, urlSuffix); + String[] finalizedSource = finalizeSource(source, format, urlSuffix); source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; @@ -404,11 +404,11 @@ public String generate(String source) { return url; } - private String[] finalizeSource(String source, boolean isHttpSource, String format, String urlSuffix) { + private String[] finalizeSource(String source, String format, String urlSuffix) { source = StringUtils.mergeSlashesInUrl(source); String[] result = new String[2]; String sourceToSign; - if (isHttpSource) { + if (StringUtils.isHttpUrl(source)) { source = SmartUrlEncoder.encode(source); sourceToSign = source; } else { From 2a8c7dfe1ef6948f4d9ce6bbc68e3f58aff0a395 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 Oct 2018 10:55:51 +0300 Subject: [PATCH 376/592] Rename `customAction` to `customFunction` --- .../java/com/cloudinary/CustomAction.java | 31 ------------------- .../java/com/cloudinary/CustomFunction.java | 31 +++++++++++++++++++ .../java/com/cloudinary/Transformation.java | 4 +-- .../com/cloudinary/test/CloudinaryTest.java | 13 ++++---- 4 files changed, 39 insertions(+), 40 deletions(-) delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/CustomAction.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/CustomFunction.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java b/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java deleted file mode 100644 index 6ca5a8e4..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/CustomAction.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.cloudinary; - -import com.cloudinary.utils.Base64Coder; - -/** - * Helper class to generate a custom action params to be used in {@link Transformation#customAction(CustomAction)}. - */ -public class CustomAction extends BaseParam{ - - private CustomAction(String... components) { - super(components); - } - - /** - * Generate a web-assembly custom action param to send to {@link Transformation#customAction(CustomAction)} - * @param publicId The public id of the web-assembly file - * @return A new instance of custom action param - */ - public static CustomAction wasm(String publicId){ - return new CustomAction("wasm", publicId); - } - - /** - * Generate a remote lambda custom action param to send to {@link Transformation#customAction(CustomAction)} - * @param url The public url of the aws lambda function - * @return A new instance of custom action param - */ - public static CustomAction remote(String url){ - return new CustomAction("remote", Base64Coder.encodeURLSafeString(url)); - } -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/CustomFunction.java b/cloudinary-core/src/main/java/com/cloudinary/CustomFunction.java new file mode 100644 index 00000000..b2a13d32 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/CustomFunction.java @@ -0,0 +1,31 @@ +package com.cloudinary; + +import com.cloudinary.utils.Base64Coder; + +/** + * Helper class to generate a custom function params to be used in {@link Transformation#customFunction(CustomFunction)}. + */ +public class CustomFunction extends BaseParam{ + + private CustomFunction(String... components) { + super(components); + } + + /** + * Generate a web-assembly custom action param to send to {@link Transformation#customFunction(CustomFunction)} + * @param publicId The public id of the web-assembly file + * @return A new instance of custom action param + */ + public static CustomFunction wasm(String publicId){ + return new CustomFunction("wasm", publicId); + } + + /** + * Generate a remote lambda custom action param to send to {@link Transformation#customFunction(CustomFunction)} + * @param url The public url of the aws lambda function + * @return A new instance of custom action param + */ + public static CustomFunction remote(String url){ + return new CustomFunction("remote", Base64Coder.encodeURLSafeString(url)); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 0b04cef7..9cb02fc1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -863,10 +863,10 @@ public T variables(Expression... variables) { /** * Set a custom action, such as a call to a lambda function or a web-assembly function. - * @param action The custom action to perform, see {@link CustomAction}. + * @param action The custom action to perform, see {@link CustomFunction}. * @return The transformation for chaining */ - public T customAction(CustomAction action) { + public T customFunction(CustomFunction action) { return param("custom_action", action.toString()); } } 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 506d0f40..5650cb16 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -3,7 +3,6 @@ import com.cloudinary.Cloudinary; import com.cloudinary.ResponsiveBreakpoint; import com.cloudinary.Transformation; -import com.cloudinary.Url; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; @@ -23,8 +22,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.cloudinary.CustomAction.remote; -import static com.cloudinary.CustomAction.wasm; +import static com.cloudinary.CustomFunction.remote; +import static com.cloudinary.CustomFunction.wasm; import static com.cloudinary.utils.ObjectUtils.asMap; import static com.cloudinary.utils.ObjectUtils.emptyMap; import static org.junit.Assert.*; @@ -1137,10 +1136,10 @@ public void testKeyframeInterval() { } @Test - public void testCustomAction(){ - assertEquals("fn_wasm:blur_wasm", new Transformation().customAction(wasm("blur_wasm")).generate()); - assertEquals("fn_remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlBY3Rpb24=", - new Transformation().customAction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryAction")).generate()); + public void testCustomFunction(){ + assertEquals("fn_wasm:blur_wasm", new Transformation().customFunction(wasm("blur_wasm")).generate()); + assertEquals("fn_remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlGdW5jdGlvbg==", + new Transformation().customFunction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryFunction")).generate()); } public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { From 9c9e47d14550ed2a732ef5ce8de2a4f6290dc88f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 Oct 2018 11:52:40 +0300 Subject: [PATCH 377/592] Rename internal custom action param for consistency --- .../src/main/java/com/cloudinary/Transformation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 9cb02fc1..e83da54d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -37,7 +37,7 @@ public class Transformation implements Serializable { "dl", "delay", "dn", "density", "f", "fetch_format", - "fn", "custom_action", + "fn", "custom_function", "fps", "fps", "g", "gravity", "l", "overlay", @@ -867,6 +867,6 @@ public T variables(Expression... variables) { * @return The transformation for chaining */ public T customFunction(CustomFunction action) { - return param("custom_action", action.toString()); + return param("custom_function", action.toString()); } } From 75c141a3767a8c6e1d9f3500f0cbcc3cd12654bb Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 Oct 2018 13:54:41 +0300 Subject: [PATCH 378/592] Version 1.20 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d65846a4..6e79c93b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ +1.20.0 / 2018-10-10 +=================== + +New functionality +----------------- + + * Add support for web assembly and lambda functions in transformations + +Other changes +------------- + + * Improve performance of `url.generate()` method. + * Fix url encoding for AuthToken generation + 1.19.0 / 2018-07-22 =================== diff --git a/README.md b/README.md index 9c7fc4e0..70dc891d 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.19.0 + 1.20.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.19.0/cloudinary-core-1.19.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.19.0/cloudinary-http44-1.19.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.20.0/cloudinary-core-1.20.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.20.0/cloudinary-http44-1.20.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index a5346a29..b8a6785f 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.19.0"; + public final static String VERSION = "1.20.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 187b47f9..420f2362 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.19.0 +version=1.20.0 From 6fa478390ebc490d97863d0276f1e93b7d9b0466 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 11 Oct 2018 12:44:00 +0300 Subject: [PATCH 379/592] Fix uploader tests cleanup --- .../cloudinary/test/AbstractUploaderTest.java | 88 +++++++++++++------ 1 file changed, 61 insertions(+), 27 deletions(-) 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 c1c07f68..9690b666 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 @@ -23,11 +23,11 @@ @SuppressWarnings({"rawtypes", "unchecked"}) abstract public class AbstractUploaderTest extends MockableTest { - private static final String ARCHIVE_TAG = SDK_TEST_TAG + "_archive"; private static final String UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; + private static Map> toDelete = new HashMap<>(); @BeforeClass public static void setUpClass() throws IOException { @@ -36,10 +36,10 @@ public static void setUpClass() throws IOException { System.err.println("Please setup environment for Upload test to run"); } - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG})); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "resource_type", "raw")); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "resource_type", "raw")); cloudinary.uploader().upload(SRC_TEST_IMAGE, - asMap("tags", new String [] {SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, + asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "transformation", new Transformation().crop("scale").width(10))); } @@ -58,6 +58,13 @@ public static void tearDownClass() { api.deleteResourcesByTag(UPLOADER_TAG, ObjectUtils.asMap("resource_type", "raw")); } catch (Exception ignored) { } + for (String type : toDelete.keySet()) { + try { + api.deleteResources(toDelete.get(type), Collections.singletonMap("type", type)); + }catch ( Exception ignored){} + } + + toDelete.clear(); } @Rule @@ -70,6 +77,7 @@ public void setUp() { assumeNotNull(cloudinary.config.apiSecret); } + @Test public void testUtf8Upload() throws IOException { @@ -112,6 +120,7 @@ public void testUpload() throws IOException { assertEquals(result.get("signature"), expected_signature); } + @Test public void testUploadUrl() throws IOException { Map result = cloudinary.uploader().upload(REMOTE_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); @@ -126,7 +135,7 @@ public void testUploadUrl() throws IOException { @Test public void testUploadLargeUrl() throws IOException { - Map result = cloudinary.uploader().uploadLarge(REMOTE_TEST_IMAGE, asMap("tags", SDK_TEST_TAG)); + Map result = cloudinary.uploader().uploadLarge(REMOTE_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals(result.get("width"), SRC_TEST_IMAGE_W); assertEquals(result.get("height"), SRC_TEST_IMAGE_H); Map to_sign = new HashMap(); @@ -186,7 +195,7 @@ public void testUniqueFilename() throws Exception { @Test public void testEagerWithStreamingProfile() throws IOException { - Transformation transformation = new EagerTransformation().format("m3u8").streamingProfile("full_hd"); + Transformation transformation = new EagerTransformation().format("m3u8").streamingProfile("full_hd"); assertEquals("sp_full_hd/m3u8", transformation.generate()); } @@ -206,7 +215,7 @@ public void testEager() throws IOException { @Test public void testUploadAsync() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true)); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals((String) result.get("status"), "pending"); } @@ -217,12 +226,14 @@ public void testHeaders() throws IOException { } @Test - public void testText() throws IOException { + public void testText() throws Exception { Map result = cloudinary.uploader().text("hello world", asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + addToDeleteList("text", result.get("public_id").toString()); assertTrue(((Integer) result.get("width")) > 1); assertTrue(((Integer) result.get("height")) > 1); } + @Test public void testImageUploadTag() { String tag = cloudinary.uploader().imageUploadTag("test-field", asMap("callback", "http://localhost/cloudinary_cors.html"), asMap("htmlattr", "htmlvalue")); @@ -234,34 +245,35 @@ public void testImageUploadTag() { assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } + @Test - public void testSprite() throws IOException { + public void testSprite() throws Exception { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + addToDeleteList("sprite", result.get("public_id").toString()); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); + addToDeleteList("sprite", result.get("public_id").toString()); assertTrue(((String) result.get("css_url")).contains("w_100")); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", new Transformation().width(100), "format", "jpg")); + addToDeleteList("sprite", result.get("public_id").toString()); assertTrue(((String) result.get("css_url")).contains("f_jpg,w_100")); } @Test - public void testMulti() throws IOException { + public void testMulti() throws Exception { final String MULTI_TEST_TAG = "multi_test_tag" + SUFFIX; final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, UPLOADER_TAG}); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); List ids = new ArrayList(); Map result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5")); - ids.add((String) result.get("public_id")); + addToDeleteList("multi", result.get("public_id").toString()); Map pdfResult = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", new Transformation().width(111), "format", "pdf")); - ids.add((String) pdfResult.get("public_id")); - try { - cloudinary.api().deleteResources(ids, ObjectUtils.emptyMap()); - } catch (Exception ignored) { - } + addToDeleteList("multi", pdfResult.get("public_id").toString()); + assertTrue(((String) result.get("url")).endsWith(".gif")); assertTrue(((String) result.get("url")).contains("w_0.5")); assertTrue(((String) pdfResult.get("url")).contains("w_111")); @@ -272,8 +284,10 @@ public void testMulti() throws IOException { public void testTags() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); String public_id = (String) result.get("public_id"); + addToDeleteList("upload", public_id); Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); String public_id2 = (String) result2.get("public_id"); + addToDeleteList("upload", public_id2); cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); @@ -288,10 +302,10 @@ public void testTags() throws Exception { assertEquals(tags, asArray(new String[]{"tag3"})); result = cloudinary.uploader().removeAllTags(new String[]{public_id, public_id2, "noSuchId"}, ObjectUtils.emptyMap()); List publicIds = (List) result.get("public_ids"); + assertThat(publicIds, containsInAnyOrder(public_id, public_id2)); // = and not containing "noSuchId" result = cloudinary.api().resource(public_id, ObjectUtils.emptyMap()); assertThat((Map) result, not(hasKey("tags"))); - } @Test @@ -431,6 +445,8 @@ public void testDetectionRequest() { } } + + @Test public void testAutoTaggingRequest() { //should support requesting auto tagging @@ -544,23 +560,31 @@ public void testResponsiveBreakpoints() throws Exception { @Test public void testCreateArchive() throws Exception { + List toDelete = new ArrayList<>(2); Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); + toDelete.add(result.get("public_id").toString()); assertEquals(2, result.get("file_count")); result = cloudinary.uploader().createArchive( new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).transformations( new Transformation[]{new Transformation().width(0.5), new Transformation().width(2.0)})); + toDelete.add(result.get("public_id").toString()); + assertEquals(4, result.get("file_count")); + cloudinary.api().deleteResources(toDelete, asMap("resource_type", "raw")); } + @Test public void testCreateArchiveRaw() throws Exception { Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).resourceType("raw")); assertEquals(1, result.get("file_count")); + cloudinary.api().deleteResources(Arrays.asList(result.get("public_id").toString()), asMap("resource_type", "raw")); + } @Test public void testDownloadArchive() throws Exception { - String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); + String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).targetTags(new String[]{UPLOADER_TAG})); URL url = new java.net.URL(result); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); ZipInputStream in = new ZipInputStream(new BufferedInputStream(urlConnection.getInputStream())); @@ -597,17 +621,17 @@ public void testAccessControl() throws ParseException, IOException { Arrays.asList(acl, token), "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertNotNull(result); - List> accessControlResponse = (List>) result.get("access_control"); + List> accessControlResponse = (List>) result.get("access_control"); assertNotNull(accessControlResponse); - assertEquals(2, accessControlResponse.size()); + assertEquals(2, accessControlResponse.size()); Map acr = accessControlResponse.get(0); - assertEquals("anonymous", acr.get("access_type")); - assertEquals("2019-02-22T14:20:57Z", acr.get("start")); + assertEquals("anonymous", acr.get("access_type")); + assertEquals("2019-02-22T14:20:57Z", acr.get("start")); assertThat(acr, not(hasKey("end"))); - + acr = accessControlResponse.get(1); - assertEquals("token", acr.get("access_type")); + assertEquals("token", acr.get("access_type")); assertThat(acr, not(hasKey("start"))); assertThat(acr, not(hasKey("end"))); @@ -618,9 +642,9 @@ public void testAccessControl() throws ParseException, IOException { accessControlResponse = (List>) result.get("access_control"); assertNotNull(accessControlResponse); acr = accessControlResponse.get(0); - assertEquals(1, accessControlResponse.size()); - assertEquals("anonymous", acr.get("access_type")); - assertEquals("2019-02-22T14:20:57Z", acr.get("start")); + assertEquals(1, accessControlResponse.size()); + assertEquals("anonymous", acr.get("access_type")); + assertEquals("2019-02-22T14:20:57Z", acr.get("start")); assertThat(acr, not(hasKey("end"))); String aclString = "[{\"access_type\":\"anonymous\",\"start\":\"2019-02-22 16:20:57 +0200\",\"end\":\"2019-03-22 00:00 +0200\"}]"; @@ -635,4 +659,14 @@ public void testAccessControl() throws ParseException, IOException { assertEquals("2019-02-22T14:20:57Z", accessControlResponse.get(0).get("start")); assertEquals("2019-03-21T22:00:00Z", accessControlResponse.get(0).get("end")); } + + private void addToDeleteList(String type, String id){ + Set ids = toDelete.get(type); + if (ids == null){ + ids = new HashSet<>(); + toDelete.put(type, ids); + } + + ids.add(id); + } } From 1fe5dbad2838a610969330a1dc4ad843f79b9a07 Mon Sep 17 00:00:00 2001 From: aditi madan <32554955+aditimadan-Cloudinary@users.noreply.github.com> Date: Sun, 4 Nov 2018 23:32:25 -0800 Subject: [PATCH 380/592] Add support for font antialiasing and font hinting for text overlays --- .../cloudinary/transformation/TextLayer.java | 17 +++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 3 +++ 2 files changed, 20 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index 206f078c..df2986c4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -13,6 +13,8 @@ public class TextLayer extends AbstractLayer { protected Integer fontSize = null; protected String fontWeight = null; protected String fontStyle = null; + protected String fontAntialiasing = null; + protected String fontHinting=null; protected String textDecoration = null; protected String textAlign = null; protected String stroke = null; @@ -42,6 +44,17 @@ public TextLayer fontFamily(String fontFamily) { return getThis(); } + public TextLayer fontAntialiasing(String fontAntialiasing) { + this.fontAntialiasing = fontAntialiasing; + return getThis(); + } + + public TextLayer fontHinting(String fontHinting) { + this.fontHinting = fontHinting; + return getThis(); + } + + public TextLayer fontSize(int fontSize) { this.fontSize = fontSize; return getThis(); @@ -137,6 +150,10 @@ protected String textStyleIdentifier() { components.add(this.fontWeight); if (StringUtils.isNotBlank(this.fontStyle) && !this.fontStyle.equals("normal")) components.add(this.fontStyle); + if (StringUtils.isNotBlank(this.fontAntialiasing)) + components.add("antialias_"+this.fontAntialiasing); + if (StringUtils.isNotBlank(this.fontHinting)) + components.add("hinting_"+this.fontHinting); if (StringUtils.isNotBlank(this.textDecoration) && !this.textDecoration.equals("none")) components.add(this.textDecoration); if (StringUtils.isNotBlank(this.textAlign)) 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 5650cb16..a04f46bc 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1034,6 +1034,9 @@ public void testOverlayOptions() { new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) .fontWeight("bold").fontStyle("italic").letterSpacing(4).lineSpacing(3), "text:Arial_18_bold_italic_letter_spacing_4_line_spacing_3:Hello%20World%252C%20Nice%20to%20meet%20you%3F", + new TextLayer().text("Hello World, Nice to meet you?").fontFamily("Arial").fontSize(18) + .fontAntialiasing("best").fontHinting("medium"), + "text:Arial_18_antialias_best_hinting_medium:Hello%20World%252C%20Nice%20to%20meet%20you%3F", new SubtitlesLayer().publicId("sample_sub_en.srt"), "subtitles:sample_sub_en.srt", new SubtitlesLayer().publicId("sample_sub_he.srt").fontFamily("Arial").fontSize(40), "subtitles:Arial_40:sample_sub_he.srt", From c447b5c02a48f7d958436d7d4a5eb194ab18940f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 5 Nov 2018 16:54:18 +0200 Subject: [PATCH 381/592] Clone configuration in `Url.clone()` --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 2 +- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 4421a6c8..34543dde 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -52,7 +52,7 @@ public Url(Cloudinary cloudinary) { public Url clone() { Url cloned = cloudinary.url(); - + cloned.config.update(config.asMap()); cloned.fallbackContent = this.fallbackContent; cloned.format = this.format; cloned.posterSource = this.posterSource; 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 a04f46bc..7e279576 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -3,6 +3,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.ResponsiveBreakpoint; import com.cloudinary.Transformation; +import com.cloudinary.Url; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; @@ -1159,5 +1160,10 @@ public static Map getUrlParameters(URI uri) throws UnsupportedEn return params; } - + @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); + assertEquals("https://res.cloudinary.com/cloud/image/upload/123.frmt", url.clone().generate()); + } } From 683252a1c77fa339a9a886fc934449ec4923c58f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 5 Nov 2018 17:36:59 +0200 Subject: [PATCH 382/592] Version 1.21.0 --- CHANGELOG.md | 11 +++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e79c93b..765ab6eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ +1.21.0 / 2018-11-05 +=================== + +New functionality +----------------- + * Add support for font antialiasing and font hinting for text overlays + +Other changes +------------- + * Clone configuration in `Url.clone()` + 1.20.0 / 2018-10-10 =================== diff --git a/README.md b/README.md index 70dc891d..cfc630d2 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.20.0 + 1.21.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.20.0/cloudinary-core-1.20.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.20.0/cloudinary-http44-1.20.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.21.0/cloudinary-core-1.21.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.21.0/cloudinary-http44-1.21.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index b8a6785f..12418178 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.20.0"; + public final static String VERSION = "1.21.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 420f2362..115fa197 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.20.0 +version=1.21.0 From 582029d91d409154c976bc1d1df8ddacb17476b0 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 12 Nov 2018 16:31:03 +0200 Subject: [PATCH 383/592] Add `named` parameter to list-transformations api. --- .../src/main/java/com/cloudinary/Api.java | 2 +- .../com/cloudinary/test/AbstractApiTest.java | 41 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 03ca062d..bc6d245e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -192,7 +192,7 @@ public ApiResponse tags(Map options) throws Exception { public ApiResponse transformations(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("transformations"), ObjectUtils.only(options, "next_cursor", "max_results"), options); + return callApi(HttpMethod.GET, Arrays.asList("transformations"), ObjectUtils.only(options, "next_cursor", "max_results", "named"), options); } public ApiResponse transformation(String transformation, Map options) throws Exception { 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 66bd4b99..7f5412e4 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 @@ -233,7 +233,7 @@ public void testTransformationsWithCursor() throws Exception { Map result = api.transformations(ObjectUtils.asMap("max_results", 500, "next_cursor", next_cursor)); transformations.addAll((List) result.get("transformations")); next_cursor = (String) result.get("next_cursor"); - } while (next_cursor != null ); + } while (next_cursor != null); assertThat(transformations, hasItem(allOf(hasEntry("name", "t_" + name)))); } @@ -447,6 +447,45 @@ public void test17aTransformationDeleteImplicit() throws Exception { api.deleteTransformation(DELETE_TRANSFORMATION_NAME, ObjectUtils.emptyMap()); } + @Test + public void testListTransformationByNamed() throws Exception { + String name = "a_test_named_transformation_param" + SUFFIX; + try { + api.createTransformation(name, "w_100", null); + name = "t_" + name; + List named = (List) api.transformations(ObjectUtils.asMap("max_results", 30, "named", true)).get("transformations"); + List unnamed = (List) api.transformations(ObjectUtils.asMap("max_results", 30, "named", false)).get("transformations"); + + // the named transformation should be present only in the named list: + boolean unnamedFound = false; + boolean namedFound = false; + + for (Map t : unnamed) { + if (t.get("name").equals(name)) { + unnamedFound = true; + break; + } + } + + if (!unnamedFound) { + for (Map t : named) { + if (t.get("name").equals(name)) { + namedFound = true; + break; + } + } + } + + assertTrue("Named transformation wasn't returned with named=true param", namedFound); + assertFalse("Named transformation returned with named=false param", unnamedFound); + + } finally { + try { + api.deleteTransformation(name, null); + } catch (Exception ignored){} + } + } + @Test public void test20ResourcesContext() throws Exception { Map result = api.resourcesByContext(TEST_KEY, ObjectUtils.emptyMap()); From dca6b14627c2439a3159527166d90512c282f63b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 13 Nov 2018 10:48:40 +0200 Subject: [PATCH 384/592] Add `quality_analysis` param in upload, explicit and api.resource calls --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../main/java/com/cloudinary/test/AbstractApiTest.java | 7 +++++++ .../java/com/cloudinary/test/AbstractUploaderTest.java | 8 ++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index bc6d245e..d81dcd9d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -117,7 +117,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"), options); + "image_metadata", "pages", "phash", "max_results", "quality_analysis"), options); return response; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 436c01e6..63f03d94 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -8,7 +8,7 @@ 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"}; + "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async", "quality_analysis"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map 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 7f5412e4..e81a0b05 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 @@ -13,6 +13,7 @@ import java.text.SimpleDateFormat; import java.util.*; +import static com.cloudinary.utils.ObjectUtils.asMap; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.AllOf.allOf; import static org.hamcrest.core.IsNot.not; @@ -940,4 +941,10 @@ public void testUpdateResourcesAccessModeByTag() throws Exception { assertEquals(resource.get("access_mode"), "public"); cloudinary.uploader().destroy(publicId, null); } + + @Test + public void testQualityAnalysis() throws Exception { + ApiResponse result = cloudinary.api().resource(API_TEST, ObjectUtils.asMap("quality_analysis", true)); + assertNotNull(result.get("quality_analysis")); + } } \ No newline at end of file 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 9690b666..096760ba 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 @@ -660,6 +660,14 @@ public void testAccessControl() throws ParseException, IOException { assertEquals("2019-03-21T22:00:00Z", accessControlResponse.get(0).get("end")); } + @Test + public void testQualityAnalysis() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("quality_analysis", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + assertNotNull(result.get("quality_analysis")); + result = cloudinary.uploader().explicit(result.get("public_id").toString(), ObjectUtils.asMap("type", "upload", "resource_type", "image", "quality_analysis", true)); + assertNotNull(result.get("quality_analysis")); + + } private void addToDeleteList(String type, String id){ Set ids = toDelete.get(type); if (ids == null){ From 98bc0d753d61407ab3ef2f96fca61f1728389bf7 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 25 Nov 2018 14:58:14 +0200 Subject: [PATCH 385/592] Fix illegal detection error message test --- .../cloudinary/test/AbstractUploaderTest.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) 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 096760ba..cf3b84f5 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 @@ -27,7 +27,7 @@ abstract public class AbstractUploaderTest extends MockableTest { private static final String UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; - private static Map> toDelete = new HashMap<>(); + private static Map> toDelete = new HashMap<>(); @BeforeClass public static void setUpClass() throws IOException { @@ -61,7 +61,8 @@ public static void tearDownClass() { for (String type : toDelete.keySet()) { try { api.deleteResources(toDelete.get(type), Collections.singletonMap("type", type)); - }catch ( Exception ignored){} + } catch (Exception ignored) { + } } toDelete.clear(); @@ -77,7 +78,7 @@ public void setUp() { assumeNotNull(cloudinary.config.apiSecret); } - + @Test public void testUtf8Upload() throws IOException { @@ -215,7 +216,7 @@ public void testEager() throws IOException { @Test public void testUploadAsync() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("transformation", new Transformation().crop("scale").width(2.0), "async", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); assertEquals((String) result.get("status"), "pending"); } @@ -438,13 +439,15 @@ public void testCategorizationRequest() { @Test public void testDetectionRequest() { //should support requesting detection + String message = null; try { cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("detection", "illegal", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); } catch (Exception e) { - assertTrue(e.getMessage().matches("(.*)(Illegal value|not a valid)(.*)")); + message = e.getMessage(); } - } + assertTrue("Detection is invalid".equals(message)); + } @Test @@ -573,7 +576,7 @@ public void testCreateArchive() throws Exception { cloudinary.api().deleteResources(toDelete, asMap("resource_type", "raw")); } - + @Test public void testCreateArchiveRaw() throws Exception { Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).resourceType("raw")); @@ -668,9 +671,10 @@ public void testQualityAnalysis() throws IOException { assertNotNull(result.get("quality_analysis")); } - private void addToDeleteList(String type, String id){ + + private void addToDeleteList(String type, String id) { Set ids = toDelete.get(type); - if (ids == null){ + if (ids == null) { ids = new HashSet<>(); toDelete.put(type, ids); } From 5eb95df46e27888ea3ce38efe37a1ad9d7bcf4b3 Mon Sep 17 00:00:00 2001 From: aditi madan <32554955+aditimadan-Cloudinary@users.noreply.github.com> Date: Tue, 8 Jan 2019 01:33:17 -0800 Subject: [PATCH 386/592] Add support for google-storage URLs (`gs://`) in uploads (#154) --- .../src/main/java/com/cloudinary/utils/StringUtils.java | 2 +- .../src/main/java/com/cloudinary/http42/UploaderStrategy.java | 4 ++-- .../src/main/java/com/cloudinary/http43/UploaderStrategy.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 2ed8c4da..f6ed5d3b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -209,7 +209,7 @@ public static String read(InputStream in) throws IOException { } public static boolean isRemoteUrl(String file) { - return file.matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)"); + return file.matches("ftp:.*|https?:.*|s3:.*|gs:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)"); } /** diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index c6b3871d..b34ae4ff 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -85,14 +85,14 @@ public Map callApi(String action, Map params, Map options, Objec } } } - - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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)); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 2199c573..956e9bd7 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -102,7 +102,7 @@ public Map callApi(String action, Map params, Map options, Objec } } - if (file instanceof String && !((String) file).matches("ftp:.*|https?:.*|s3:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)")) { + 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); From 15c9a80b250841133c5b722ca02aa27869e06e20 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 10 Jan 2019 10:15:51 +0200 Subject: [PATCH 387/592] Remove auto-tagging request test. (#158) --- .../com/cloudinary/test/AbstractUploaderTest.java | 11 ----------- 1 file changed, 11 deletions(-) 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 cf3b84f5..1dc09885 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 @@ -449,17 +449,6 @@ public void testDetectionRequest() { assertTrue("Detection is invalid".equals(message)); } - - @Test - public void testAutoTaggingRequest() { - //should support requesting auto tagging - try { - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("auto_tagging", 0.5f, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); - } catch (Exception e) { - assertTrue(e.getMessage().matches("^Must use(.*)")); - } - } - @Test public void testUploadLarge() throws Exception { // support uploading large files From 8c9b1818e8c9ef7fbc3a327ca39df8ddb207b627 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 10 Jan 2019 10:59:00 +0200 Subject: [PATCH 388/592] Add support for range value in `Transformation.fps()` (#155) --- .../java/com/cloudinary/Transformation.java | 24 ++++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 25 ++++++++++++------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index e83da54d..afb5011e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -490,6 +490,30 @@ public T fps(int value) { return param("fps", new Integer(value)); } + /** + * fps (frames per second) parameter for video + * @param rangeStart String or Number, can be null for open range. + * @param rangeEnd String or Number, can be null for open range. + * @return the transformation for chaining. + */ + public T fps(Object rangeStart, Object rangeEnd){ + if (rangeEnd == null && rangeStart == null){ + throw new IllegalArgumentException("At least one of [rangeStart, rangeEnd] must be provided"); + } + StringBuilder builder = new StringBuilder(); + if (rangeStart != null){ + builder.append(rangeStart); + } + + builder.append("-"); + + if (rangeEnd != null){ + builder.append(rangeEnd); + } + + return param("fps", builder.toString()); + } + public T streamingProfile(String value) { return param("streaming_profile", value); } 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 7e279576..aa3c9228 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1117,15 +1117,22 @@ public void testResponsiveBreakpointsToJson() { @Test public void testFps() { - Transformation t = new Transformation().fps(12); - assertEquals("fps_12", t.generate()); - t = new Transformation().fps(12.5); - assertEquals("fps_12.5", t.generate()); - t = new Transformation().fps("12"); - assertEquals("fps_12", t.generate()); - t = new Transformation().fps("12-25.6"); - assertEquals("fps_12-25.6", t.generate()); - + Transformation t = new Transformation().fps("24-29.97"); + assertEquals("fps_24-29.97", t.generate()); + t = new Transformation().fps(24); + assertEquals("fps_24", t.generate()); + t = new Transformation().fps(24.5); + assertEquals("fps_24.5", t.generate()); + t = new Transformation().fps("24"); + assertEquals("fps_24", t.generate()); + t = new Transformation().fps("-24"); + assertEquals("fps_-24", t.generate()); + t = new Transformation().fps(24,29.97); + assertEquals("fps_24-29.97", t.generate()); + t = new Transformation().fps(24,null); + assertEquals("fps_24-", t.generate()); + t = new Transformation().fps(null,29.97); + assertEquals("fps_-29.97", t.generate()); } @Test From aac2f182ab89c769b71a13260283db17d3a616b4 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 10 Jan 2019 14:04:11 +0200 Subject: [PATCH 389/592] Add JVM version to user agent (#157) --- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 12418178..301b1956 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -33,7 +33,7 @@ public class Cloudinary { public final static String SHARED_CDN = AKAMAI_SHARED_CDN; public final static String VERSION = "1.21.0"; - public final static String USER_AGENT = "CloudinaryJava/" + VERSION; + public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; From 355009530ad5cceec185159f9f6d7e3bc2ba5d8e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 22 Jan 2019 14:39:31 +0200 Subject: [PATCH 390/592] Version 1.22.0 --- CHANGELOG.md | 9 +++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 765ab6eb..a8d56ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ +1.22.0 / 2019-01-22 +=================== + + * Add JVM version to user agent (#157) + * Add support for range value in `Transformation.fps()` (#155) + * Add support for google-storage URLs (`gs://`) in uploads (#154) + * Add `quality_analysis` param in upload, explicit and api.resource calls + * Add `named` parameter to list-transformations api. + 1.21.0 / 2018-11-05 =================== diff --git a/README.md b/README.md index cfc630d2..9f750a1f 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.21.0 + 1.22.01.22.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.21.0/cloudinary-core-1.21.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.21.0/cloudinary-http44-1.21.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.22.0/cloudinary-core-1.22.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.22.0/cloudinary-http44-1.22.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 301b1956..d77541d5 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.21.0"; + public final static String VERSION = "1.22.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 115fa197..15236500 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.21.0 +version=1.22.0 From 738b328a9057084097b7e381bf8a6e989cf69b43 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 24 Jan 2019 14:53:01 +0200 Subject: [PATCH 391/592] Fix eager transformation chaining. (#159) --- .../com/cloudinary/EagerTransformation.java | 16 ++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 18 ++++++++++++++---- .../cloudinary/test/AbstractUploaderTest.java | 6 ------ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index dd6cedbd..2884cbb0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -27,6 +27,22 @@ public String getFormat() { return format; } + @Override + public String generate(Iterable optionsList) { + List components = new ArrayList(); + for (Map options : optionsList) { + if (options.size() > 0) { + components.add(super.generate(options)); + } + } + + if (StringUtils.isNotBlank(format)){ + components.add(format); + } + + return StringUtils.join(components, "/"); + } + @Override public String generate(Map options) { List eager = new ArrayList<>(); 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 aa3c9228..9e9a6e3a 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1,9 +1,6 @@ package com.cloudinary.test; -import com.cloudinary.Cloudinary; -import com.cloudinary.ResponsiveBreakpoint; -import com.cloudinary.Transformation; -import com.cloudinary.Url; +import com.cloudinary.*; import com.cloudinary.transformation.*; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; @@ -16,6 +13,7 @@ import org.junit.rules.TestName; import org.junit.runner.RunWith; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; @@ -678,6 +676,18 @@ public void testShouldSupportAutoWidth(String width, String result) { assertEquals(result, trans); } + @Test + public void testEagerWithStreamingProfile() throws IOException { + Transformation transformation = new EagerTransformation().format("m3u8").streamingProfile("full_hd"); + assertEquals("sp_full_hd/m3u8", transformation.generate()); + } + + @Test + public void testEagerWithChaining() throws IOException { + Transformation transformation = new EagerTransformation().angle(13).chain().effect("sepia").chain().format("webp"); + assertEquals("a_13/e_sepia/webp", transformation.generate()); + } + @Test public void testShouldSupportIhIw() { String trans = new Transformation().width("iw").height("ih").crop("crop").generate(); 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 1dc09885..4836094c 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 @@ -194,12 +194,6 @@ public void testUniqueFilename() throws Exception { assertEquals(result.get("public_id"), "old_logo"); } - @Test - public void testEagerWithStreamingProfile() throws IOException { - Transformation transformation = new EagerTransformation().format("m3u8").streamingProfile("full_hd"); - assertEquals("sp_full_hd/m3u8", transformation.generate()); - } - @Test public void testExplicit() throws IOException { Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload", "moderation", "manual")); From 1bbe10154779d0c1e908270327be92f72e05bfa5 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 12 Feb 2019 09:22:56 +0200 Subject: [PATCH 392/592] Fix Java 1.6 support (#161) --- .../src/main/java/com/cloudinary/EagerTransformation.java | 2 +- .../src/main/java/com/cloudinary/Transformation.java | 4 ++-- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 +- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 6 +++--- java_shared.gradle | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index 2884cbb0..29a64228 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -45,7 +45,7 @@ public String generate(Iterable optionsList) { @Override public String generate(Map options) { - List eager = new ArrayList<>(); + List eager = new ArrayList(); eager.add(super.generate(options)); if (StringUtils.isNotBlank(format)){ diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index afb5011e..ac725a44 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -679,7 +679,7 @@ public String generate(Map options) { components.add(variables); } - Map params = new HashMap<>(64); + Map params = new HashMap(64); params.put("a", Expression.normalize(angle)); params.put("ar", Expression.normalize(options.get("aspect_ratio"))); @@ -707,7 +707,7 @@ public String generate(Map options) { params.put(SIMPLE_PARAMS[i], ObjectUtils.asString(options.get(SIMPLE_PARAMS[i + 1]))); } - params = new TreeMap<>(params); + params = new TreeMap(params); for (Map.Entry param : params.entrySet()) { if (StringUtils.isNotBlank(param.getValue())) { 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 e81a0b05..b67d2cb2 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 @@ -228,7 +228,7 @@ public void testResourcesListingStartAt() throws Exception { public void testTransformationsWithCursor() throws Exception { String name = "testTransformation" + SDK_TEST_TAG + System.currentTimeMillis(); api.createTransformation(name, "c_scale,w_100", null); - final List transformations = new ArrayList<>(); + final List transformations = new ArrayList(); String next_cursor = null; do { Map result = api.transformations(ObjectUtils.asMap("max_results", 500, "next_cursor", next_cursor)); 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 4836094c..202b6433 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 @@ -27,7 +27,7 @@ abstract public class AbstractUploaderTest extends MockableTest { private static final String UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; - private static Map> toDelete = new HashMap<>(); + private static Map> toDelete = new HashMap>(); @BeforeClass public static void setUpClass() throws IOException { @@ -546,7 +546,7 @@ public void testResponsiveBreakpoints() throws Exception { @Test public void testCreateArchive() throws Exception { - List toDelete = new ArrayList<>(2); + List toDelete = new ArrayList(2); Map result = cloudinary.uploader().createArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG})); toDelete.add(result.get("public_id").toString()); assertEquals(2, result.get("file_count")); @@ -658,7 +658,7 @@ public void testQualityAnalysis() throws IOException { private void addToDeleteList(String type, String id) { Set ids = toDelete.get(type); if (ids == null) { - ids = new HashSet<>(); + ids = new HashSet(); toDelete.put(type, ids); } diff --git a/java_shared.gradle b/java_shared.gradle index bfbf0e8f..6e570564 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -1,7 +1,7 @@ apply plugin: 'java' -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.6 +targetCompatibility = 1.6 tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } From e1b363218f89df9ebe92a86ed64ce7ffd2c25a09 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 13 Feb 2019 16:11:37 +0200 Subject: [PATCH 393/592] Version 1.22.1 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d56ca5..50fba4c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ +1.22.1 / 2019-02-13 +=================== + + * Fix Java 1.6 support (#161) + * Fix eager transformation chaining. (#159) + 1.22.0 / 2019-01-22 =================== diff --git a/README.md b/README.md index 9f750a1f..347e447c 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.22.01.22.0 + 1.22.1 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.22.0/cloudinary-core-1.22.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.22.0/cloudinary-http44-1.22.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.22.1/cloudinary-core-1.22.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.22.1/cloudinary-http44-1.22.1.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index d77541d5..2aa14c2e 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.22.0"; + public final static String VERSION = "1.22.1"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 15236500..a2bab305 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.22.0 +version=1.22.1 From ad2fc48755086878abd0164fc7095f43fdded0f1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 18 Feb 2019 12:04:47 +0200 Subject: [PATCH 394/592] Add support for custom pre-functions in transformation (wasm/remote). (#162) --- .../src/main/java/com/cloudinary/Transformation.java | 9 +++++++++ .../test/java/com/cloudinary/test/CloudinaryTest.java | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index ac725a44..bc9a1042 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -893,4 +893,13 @@ public T variables(Expression... variables) { public T customFunction(CustomFunction action) { return param("custom_function", action.toString()); } + + /** + * Set a custom pre-function, such as a call to a lambda function or a web-assembly function. + * @param action The custom action to perform, see {@link CustomFunction}. + * @return The transformation for chaining + */ + public T customPreFunction(CustomFunction action) { + return param("custom_function", "pre:" + action.toString()); + } } 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 9e9a6e3a..68af7ef6 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1163,6 +1163,13 @@ public void testCustomFunction(){ new Transformation().customFunction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryFunction")).generate()); } + @Test + public void testCustomPreFunction(){ + assertEquals("fn_pre:wasm:blur_wasm", new Transformation().customPreFunction(wasm("blur_wasm")).generate()); + assertEquals("fn_pre:remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlGdW5jdGlvbg==", + new Transformation().customPreFunction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryFunction")).generate()); + } + public static Map getUrlParameters(URI uri) throws UnsupportedEncodingException { Map params = new HashMap(); for (String param : uri.getRawQuery().split("&")) { From 8cb8fff593f1ec0948caee03aecddc27a536a25f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 19 Feb 2019 10:11:51 +0200 Subject: [PATCH 395/592] Remove test for similarity search (#163) --- .../java/com/cloudinary/test/AbstractApiTest.java | 12 ------------ 1 file changed, 12 deletions(-) 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 b67d2cb2..903cd1a8 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 @@ -596,18 +596,6 @@ public void testDetectionUpdate() { } } - @Test - public void testSimilaritySearchUpdate() { - // should support requesting similarity search - try { - Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS)); - api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("similarity_search", "illegal")); - } catch (Exception e) { - assertTrue(e instanceof BadRequest); - assertTrue(e.getMessage().matches("^Illegal value(.*)")); - } - } - @Test public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates From 6dc19610e68ef0c563e0204e49fda458ac86058c Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 21 Feb 2019 16:51:38 +0200 Subject: [PATCH 396/592] Fix build script and travis.yml to support more java versions. --- .travis.yml | 12 ++++++++---- build.gradle | 1 + .../main/java/com/cloudinary/test/MockableTest.java | 7 +++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f27b6890..e5b7ffdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: java -dist: precise -sudo: required +dist: trusty before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock @@ -11,8 +10,13 @@ cache: - $HOME/.gradle/wrapper/ jdk: - - oraclejdk7 - oraclejdk8 + - oraclejdk9 + - oraclejdk11 + - openjdk7 + - openjdk8 + - openjdk10 + env: - MODULE=core - MODULE=http42 @@ -24,5 +28,5 @@ branches: - staging-test # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean ciTest -p cloudinary-${MODULE} -i +script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${MODULE} -i diff --git a/build.gradle b/build.gradle index 59aa535c..ef4017cd 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,7 @@ allprojects { subprojects { tasks.withType(Test) { + environment 'CLOUDINARY_URL', System.getProperty('CLOUDINARY_URL') maxParallelForks = Runtime.runtime.availableProcessors() // show standard out and standard error of the test JVM(s) on the console diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 3c5a3341..13bc82ef 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -3,7 +3,6 @@ import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.IOException; import java.util.Map; @@ -18,13 +17,13 @@ public class MockableTest { protected Cloudinary cloudinary; protected Object getParam(String name){ - throw new NotImplementedException(); + throw new UnsupportedOperationException(); } protected String getURL(){ - throw new NotImplementedException(); + throw new UnsupportedOperationException(); } protected String getHttpMethod(){ - throw new NotImplementedException(); + throw new UnsupportedOperationException(); } protected Map preloadResource(Map options) throws IOException { From 10268357eca773e785d00ca4868bea6cfe8d67c0 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 Apr 2019 10:50:16 +0300 Subject: [PATCH 397/592] Fix base64 url validation (accept parameters). (#165) --- .../com/cloudinary/utils/StringUtils.java | 2 +- .../cloudinary/test/AbstractUploaderTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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 f6ed5d3b..e3bec84b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -209,7 +209,7 @@ public static String read(InputStream in) throws IOException { } public static boolean isRemoteUrl(String file) { - return file.matches("ftp:.*|https?:.*|s3:.*|gs:.*|data:[^;]*;base64,([a-zA-Z0-9/+\n=]+)"); + return file.matches("ftp:.*|https?:.*|s3:.*|gs:.*|data:([\\w-]+/[\\w-]+)?(;[\\w-]+=[\\w-]+)*;base64,([a-zA-Z0-9/+\n=]+)"); } /** 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 202b6433..c213778e 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 @@ -17,6 +17,7 @@ import static com.cloudinary.utils.ObjectUtils.asArray; import static com.cloudinary.utils.ObjectUtils.asMap; +import static com.cloudinary.utils.StringUtils.isRemoteUrl; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; @@ -121,6 +122,28 @@ public void testUpload() throws IOException { assertEquals(result.get("signature"), expected_signature); } + @Test + public void testIsRemoteUrl() { + String[] urls = new String[]{ + "ftp://ftp.cloudinary.com/images/old_logo.png", + "http://cloudinary.com/images/old_logo.png", + "https://cloudinary.com/images/old_logo.png", + "s3://s3-us-west-2.amazonaws.com/cloudinary/images/old_logo.png", + "gs://cloudinary/images/old_logo.png", + "data:image/gif;charset=utf8;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", + "data:image/gif;param1=value1;param2=value2;base64," + + "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}; + + for (String url : urls) { + assertTrue(isRemoteUrl(url)); + } + + String[] invalidUrls = new String[]{"adsadasdasdasd", " ", ""}; + + for (String url : invalidUrls) { + assertFalse(isRemoteUrl(url)); + } + } @Test public void testUploadUrl() throws IOException { From 58ee1823180da2dea6a2eb7e5cf00d5a760f8aef Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 7 Apr 2019 10:25:26 +0300 Subject: [PATCH 398/592] Add option to exclude asset version when generating cloudinary URLs. (#166) --- .../src/main/java/com/cloudinary/Url.java | 17 +++++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 34543dde..67cbaa1e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -39,6 +39,7 @@ public class Url { Transformation posterTransformation = null; String posterSource = null; Url posterUrl = null; + boolean forceVersion = true; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"}; @@ -312,6 +313,19 @@ public Url poster(Object poster) { } } + /** + * Indicates whether to add '/v1/' to the URL when the public ID includes folders and a 'version' value was + * not defined. + * When no version is explicitly specified and the public id contains folders, a default v1 version + * is added to the url. This boolean can disable that behaviour. + * @param forceVersion Whether to add the version to the url. + * @return This same Url instance for chaining. + */ + public Url forceVersion(boolean forceVersion){ + this.forceVersion = forceVersion; + return this; + } + public String generate() { return generate(null); } @@ -357,7 +371,8 @@ public String generate(String source) { source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; - if (sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && !httpSource && StringUtils.isEmpty(version)) { + if (forceVersion && sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && + !httpSource && StringUtils.isEmpty(version)) { version = "1"; } 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 68af7ef6..65e632eb 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -572,6 +572,29 @@ public void testFolders() { assertEquals(DEFAULT_UPLOAD_PATH + "v123/folder/test", result); } + @Test + public void testFoldersWithExcludeVersion(){ + // should not add version if the user turned off forceVersion + String result = cloudinary.url().forceVersion(false).generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "folder/test", result); + + // should still show explicit version if passed by the user + result = cloudinary.url().forceVersion(false).version("1234").generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1234/folder/test", result); + + // should add version no value specified for forceVersion: + result = cloudinary.url().generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); + + // should add version if forceVersion is true + result = cloudinary.url().forceVersion(true).generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); + + // should not use v1 if explicit version is passed + result = cloudinary.url().forceVersion(true).version("1234").generate("folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1234/folder/test", result); + } + @Test public void testFoldersWithVersion() { // should not add version if public_id contains version already From c1870071ce6d5b0428295872612314a78df0df24 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 24 Jun 2019 12:05:15 +0300 Subject: [PATCH 399/592] Add support for forcing a version when generating URLs. * Add `forceUrl` boolean to `Url` and `Configuration` * Add missing fields to configuration and fix to/from map. --- .../main/java/com/cloudinary/AuthToken.java | 13 +++ .../java/com/cloudinary/Configuration.java | 28 ++++-- .../src/main/java/com/cloudinary/Url.java | 5 +- .../com/cloudinary/test/CloudinaryTest.java | 86 ++++++++++++++++--- 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index 2b8d4708..c12698ad 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -56,7 +56,20 @@ public AuthToken(Map options) { this.acl = (String) options.get("acl"); this.duration = ObjectUtils.asLong(options.get("duration"), 0L); } + } + + public Map asMap(){ + Map result = new HashMap(); + + result.put("tokenName", this.tokenName); + result.put("key", this.key); + result.put("startTime", this.startTime); + result.put("expiration", this.expiration); + result.put("ip", this.ip); + result.put("acl", this.acl); + result.put("duration", this.duration); + return result; } /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index f6d21206..c9e68b2e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -40,15 +40,12 @@ public class Configuration { public boolean loadStrategies = true; public boolean clientHints = false; public AuthToken authToken; + public boolean forceVersion = true; public Configuration() { } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath) { - this(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, 0, true); - } - - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies) { + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies, boolean forceVersion) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -66,6 +63,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.useRootPath = useRootPath; this.timeout = 0; this.loadStrategies = loadStrategies; + this.forceVersion = forceVersion; } @SuppressWarnings("rawtypes") @@ -97,6 +95,11 @@ public void update(Map config) { if (tokenMap != null) { this.authToken = new AuthToken(tokenMap); } + this.forceVersion = ObjectUtils.asBoolean(config.get("force_version"), true); + Map properties = (Map) config.get("properties"); + if (properties != null) { + this.properties.putAll(properties); + } } @SuppressWarnings("rawtypes") @@ -121,8 +124,10 @@ public Map asMap() { map.put("timeout", timeout); map.put("client_hints", clientHints); if (authToken != null) { - map.put("auth_token", authToken.copy()); + map.put("auth_token", authToken.asMap()); } + map.put("force_version", forceVersion); + map.put("properties", new HashMap(properties)); return map; } @@ -148,6 +153,9 @@ public Configuration(Configuration other) { if (other.authToken != null) { this.authToken = other.authToken.copy(); } + this.forceVersion = other.forceVersion; + this.loadStrategies = other.loadStrategies; + this.properties.putAll(other.properties); } /** @@ -253,6 +261,7 @@ public static class Builder { private int timeout; private boolean clientHints = false; private AuthToken authToken; + private boolean forceVersion = true; /** * Set the HTTP connection timeout. @@ -269,7 +278,7 @@ public Builder setTimeout(int timeout) { * Creates a {@link Configuration} with the arguments supplied to this builder */ public Configuration build() { - final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies); + final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies, forceVersion); configuration.clientHints = clientHints; return configuration; } @@ -383,6 +392,10 @@ public Builder setAuthToken(AuthToken authToken) { this.authToken = authToken; return this; } + public Builder setForceVersion(boolean forceVersion) { + this.forceVersion = forceVersion; + return this; + } /** * Initialize builder from existing {@link Configuration} @@ -410,6 +423,7 @@ public Builder from(Configuration other) { this.timeout = other.timeout; this.clientHints = other.clientHints; this.authToken = other.authToken == null ? null : other.authToken.copy(); + this.forceVersion = other.forceVersion; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 67cbaa1e..17a95d1c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -39,7 +39,6 @@ public class Url { Transformation posterTransformation = null; String posterSource = null; Url posterUrl = null; - boolean forceVersion = true; private static final String CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; public static final String[] DEFAULT_VIDEO_SOURCE_TYPES = {"webm", "mp4", "ogv"}; @@ -322,7 +321,7 @@ public Url poster(Object poster) { * @return This same Url instance for chaining. */ public Url forceVersion(boolean forceVersion){ - this.forceVersion = forceVersion; + this.config.forceVersion = forceVersion; return this; } @@ -371,7 +370,7 @@ public String generate(String source) { source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; - if (forceVersion && sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && + if (this.config.forceVersion && sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && !httpSource && StringUtils.isEmpty(version)) { version = "1"; } 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 65e632eb..4f8f6339 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -15,6 +15,9 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.net.URI; import java.net.URLDecoder; import java.util.*; @@ -44,29 +47,29 @@ public void setUp() { } @Test - public void testUrlSuffixWithDotOrSlash(){ + public void testUrlSuffixWithDotOrSlash() { Boolean[] errors = new Boolean[4]; try { cloudinary.url().suffix("dsfdfd.adsfad").generate("publicId"); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { errors[0] = true; } try { cloudinary.url().suffix("dsfdfd/adsfad").generate("publicId"); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { errors[1] = true; } try { cloudinary.url().suffix("dsfd.fd/adsfad").generate("publicId"); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { errors[2] = true; } try { cloudinary.url().suffix("dsfdfdaddsfad").generate("publicId"); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { errors[3] = true; } @@ -75,6 +78,7 @@ public void testUrlSuffixWithDotOrSlash(){ assertTrue(errors[2]); assertNull(errors[3]); } + @Test public void testCloudName() { // should use cloud_name from config @@ -573,7 +577,7 @@ public void testFolders() { } @Test - public void testFoldersWithExcludeVersion(){ + public void testFoldersWithExcludeVersion() { // should not add version if the user turned off forceVersion String result = cloudinary.url().forceVersion(false).generate("folder/test"); assertEquals(DEFAULT_UPLOAD_PATH + "folder/test", result); @@ -582,7 +586,7 @@ public void testFoldersWithExcludeVersion(){ result = cloudinary.url().forceVersion(false).version("1234").generate("folder/test"); assertEquals(DEFAULT_UPLOAD_PATH + "v1234/folder/test", result); - // should add version no value specified for forceVersion: + // should add version if no value specified for forceVersion: result = cloudinary.url().generate("folder/test"); assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); @@ -1160,11 +1164,11 @@ public void testFps() { assertEquals("fps_24", t.generate()); t = new Transformation().fps("-24"); assertEquals("fps_-24", t.generate()); - t = new Transformation().fps(24,29.97); + t = new Transformation().fps(24, 29.97); assertEquals("fps_24-29.97", t.generate()); - t = new Transformation().fps(24,null); + t = new Transformation().fps(24, null); assertEquals("fps_24-", t.generate()); - t = new Transformation().fps(null,29.97); + t = new Transformation().fps(null, 29.97); assertEquals("fps_-29.97", t.generate()); } @@ -1180,14 +1184,14 @@ public void testKeyframeInterval() { } @Test - public void testCustomFunction(){ + public void testCustomFunction() { assertEquals("fn_wasm:blur_wasm", new Transformation().customFunction(wasm("blur_wasm")).generate()); assertEquals("fn_remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlGdW5jdGlvbg==", new Transformation().customFunction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryFunction")).generate()); } @Test - public void testCustomPreFunction(){ + public void testCustomPreFunction() { assertEquals("fn_pre:wasm:blur_wasm", new Transformation().customPreFunction(wasm("blur_wasm")).generate()); assertEquals("fn_pre:remote:aHR0cHM6Ly9kZjM0cmE0YS5leGVjdXRlLWFwaS51cy13ZXN0LTIuYW1hem9uYXdzLmNvbS9kZWZhdWx0L2Nsb3VkaW5hcnlGdW5jdGlvbg==", new Transformation().customPreFunction(remote("https://df34ra4a.execute-api.us-west-2.amazonaws.com/default/cloudinaryFunction")).generate()); @@ -1208,9 +1212,65 @@ public static Map getUrlParameters(URI uri) throws UnsupportedEn } @Test - public void testUrlCloneConfig(){ + 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); assertEquals("https://res.cloudinary.com/cloud/image/upload/123.frmt", url.clone().generate()); } + + @Test + public void testConfiguration() throws IllegalAccessException { + Configuration config = new Configuration(); + randomizeFields(config); + Map map = config.asMap(); + Configuration copy = new Configuration(map); + assertFieldsEqual(config, copy); + + copy = new Configuration(config); + assertFieldsEqual(config, copy); + } + + 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(); + for (Field field : fields) { + assertEquals("Field " + field.getName() + " should have equal values", field.get(a), field.get(b)); + } + } + + private void randomizeFields(Object instance) throws IllegalAccessException { + Random rand = new Random(); + Field[] fields = instance.getClass().getDeclaredFields(); + for (Field field : fields) { + setRandomValue(rand, field, instance); + } + } + + private void setRandomValue(Random rand, Field field, Object instance) throws IllegalAccessException { + field.setAccessible(true); + Type fieldType = field.getGenericType(); + if (Modifier.isFinal(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + return; + } + + if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) { + field.set(instance, rand.nextBoolean()); + } else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) { + field.set(instance, rand.nextInt()); + } else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) { + field.set(instance, rand.nextLong()); + } else if (fieldType.equals(String.class)) { + field.set(instance, cloudinary.randomPublicId()); + } else if (fieldType.equals(AuthToken.class)) { + AuthToken authToken = new AuthToken(); + randomizeFields(authToken); + field.set(instance, authToken); + } else if (field.get(instance) instanceof HashMap) { + Map map = new HashMap(); + map.put(cloudinary.randomPublicId(), rand.nextInt()); + field.set(instance, map); + } else { + throw new IllegalArgumentException("Object have unexpected field type, randomizing not supported: " + field.getName() + ", type: " + field.getType().getSimpleName()); + } + } } From d03ca736ab1ff32b6e83f7ad5f0b2d9a3da5cfdc Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 24 Jun 2019 12:07:31 +0300 Subject: [PATCH 400/592] Add support for folder deletion (#170) --- .../src/main/java/com/cloudinary/Api.java | 12 ++++++++++++ .../java/com/cloudinary/test/AbstractApiTest.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index d81dcd9d..8a09ef72 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -519,6 +519,18 @@ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, return updateResourcesAccessMode(accessMode, "tag", tag, options); } + /** + * Delete a folder (must be empty). + * @param folder The full path of the folder to delete + * @param options additional options. + * @return The operation result. + * @throws Exception When the folder isn't empty or doesn't exist. + */ + public ApiResponse deleteFolder(String folder, Map options) throws Exception { + List uri = Arrays.asList("folders", folder); + return callApi(HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + /** * Update access mode of one or more resources by publicIds * 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 903cd1a8..009789bf 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 @@ -14,6 +14,7 @@ import java.util.*; import static com.cloudinary.utils.ObjectUtils.asMap; +import static com.cloudinary.utils.ObjectUtils.emptyMap; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.AllOf.allOf; import static org.hamcrest.core.IsNot.not; @@ -935,4 +936,17 @@ public void testQualityAnalysis() throws Exception { ApiResponse result = cloudinary.api().resource(API_TEST, ObjectUtils.asMap("quality_analysis", true)); assertNotNull(result.get("quality_analysis")); } + + @Test(expected = NotFound.class) + public void testDeleteFolder() throws Exception { + String toDelete = "todelete_" + SUFFIX; + Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", UPLOAD_TAGS, "folder", toDelete)); + Thread.sleep(5000); + api.deleteResources(Collections.singletonList(uploadResult.get("public_id").toString()), emptyMap()); + ApiResponse result = api.deleteFolder(toDelete, emptyMap()); + assertTrue(((ArrayList)result.get("deleted")).contains(toDelete)); + + // should throw exception (folder not found): + api.deleteFolder(cloudinary.randomPublicId(), emptyMap()); + } } \ No newline at end of file From 39c3401f5a190da7f9f9595842ab1a038751356a Mon Sep 17 00:00:00 2001 From: aditi madan <32554955+aditimadan-Cloudinary@users.noreply.github.com> Date: Tue, 23 Jul 2019 15:55:16 -0700 Subject: [PATCH 401/592] Add support for 'live' parameter to Upload Preset (#173) --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 4 ++-- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 8a09ef72..8ddc1632 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -236,7 +236,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")); + params.putAll(ObjectUtils.only(options, "unsigned", "disallow_public_id","live")); return callApi(HttpMethod.PUT, Arrays.asList("upload_presets", name), params, options); } @@ -244,7 +244,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")); + params.putAll(ObjectUtils.only(options, "name", "unsigned", "disallow_public_id","live")); 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 009789bf..0383505d 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 @@ -653,13 +653,14 @@ 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)); + context,"live",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); Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); assertEquals(outTransformation.get("width"), 100); assertEquals(outTransformation.get("crop"), "scale"); @@ -692,12 +693,13 @@ 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)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true,"live",true)); 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 9ecfdc937cf2e1b3bd2fdde8a35eaff29d2539f4 Mon Sep 17 00:00:00 2001 From: aditi madan <32554955+aditimadan-Cloudinary@users.noreply.github.com> Date: Tue, 23 Jul 2019 16:00:57 -0700 Subject: [PATCH 402/592] Add duration to conditions in video (#172) --- .../com/cloudinary/transformation/BaseExpression.java | 5 ++++- .../java/com/cloudinary/transformation/Condition.java | 7 +++++++ .../test/java/com/cloudinary/TransformationTest.java | 11 +++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index b9f6a54f..993be1e7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -45,7 +45,10 @@ public abstract class BaseExpression { "currentPage", "cp", "tags", "tags", "pageX", "px", - "pageY", "py" + "pageY", "py", + "duration","du", + "initial_duration","idu", + "initialDuration","idu" ); private static final Pattern PATTERN = getPattern(); 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 3e548448..35613813 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -58,6 +58,13 @@ public Condition height(String operator, Object value) { public Condition aspectRatio(String operator, Object value) { return predicate("ar", operator, value); } + public Condition duration(String operator, Object value) { + return predicate("du", operator, value); + } + public Condition initialDuration(String operator, Object value) { + return predicate("idu", operator, value); + } + /** * @deprecated Use {@link #faceCount(String, Object)} instead diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 84741494..9e41deef 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -88,6 +88,17 @@ public void ifElse() throws Exception { assertEquals("if_else should be without any transformation parameters", "if_w_lt_200/c_fill,h_120,w_80/if_else/c_fill,h_90,w_100", transformation.toString()); } + @Test + public void testDuration() throws Exception { + Transformation transformation = new Transformation().ifCondition().duration("gt", "30").then().width(100).crop("scale"); + assertEquals("passing an operator and a value adds a condition", "if_du_gt_30,c_scale,w_100", transformation.toString()); + transformation = new Transformation().ifCondition().initialDuration("gt", "30").then().width(100).crop("scale"); + assertEquals("passing an operator and a value adds a condition", "if_idu_gt_30,c_scale,w_100", transformation.toString()); + transformation=new Transformation().ifCondition("initialDuration > 20").crop("scale").width(200); + assertEquals("if_idu_gt_20,c_scale,w_200", transformation.generate()); + } + + @Test public void chainedConditions() throws Exception { Transformation transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").then().width(100).crop("scale"); From 948e5c6f659910bb6ec8c4983899b70f28fe65f0 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 8 Aug 2019 11:59:19 +0300 Subject: [PATCH 403/592] Add structured metadata APIs and entities (#171) --- .../src/main/java/com/cloudinary/Api.java | 128 +++++++-- .../main/java/com/cloudinary/Uploader.java | 19 ++ .../src/main/java/com/cloudinary/Util.java | 2 + .../metadata/DateMetadataField.java | 40 +++ .../metadata/EnumMetadataField.java | 10 + .../cloudinary/metadata/IntMetadataField.java | 10 + .../metadata/MetadataDataSource.java | 70 +++++ .../cloudinary/metadata/MetadataField.java | 133 +++++++++ .../metadata/MetadataFieldType.java | 17 ++ .../metadata/MetadataValidation.java | 177 ++++++++++++ .../cloudinary/metadata/SetMetadataField.java | 12 + .../metadata/StringMetadataField.java | 10 + .../strategies/AbstractApiStrategy.java | 4 +- .../com/cloudinary/utils/ObjectUtils.java | 20 +- .../test/StructuredMetadataTest.java | 4 + .../test/StructuredMetadataTest.java | 4 + .../test/StructuredMetadataTest.java | 4 + .../test/AbstractStructuredMetadataTest.java | 252 ++++++++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 2 + 19 files changed, 893 insertions(+), 25 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 8ddc1632..74164173 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -5,6 +5,8 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; import com.cloudinary.api.exceptions.*; +import com.cloudinary.metadata.MetadataField; +import com.cloudinary.metadata.MetadataDataSource; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -15,6 +17,7 @@ public class Api { public enum HttpMethod {GET, POST, PUT, DELETE;} + public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); static { @@ -30,6 +33,7 @@ public enum HttpMethod {GET, POST, PUT, DELETE;} public final Cloudinary cloudinary; private AbstractApiStrategy strategy; + protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { return this.strategy.callApi(method, uri, params, options); } @@ -78,18 +82,18 @@ public ApiResponse resourcesByTag(String tag, Map options) throws Exception { } public ApiResponse resourcesByContext(String key, Map options) throws Exception { - return resourcesByContext(key,null,options); + return resourcesByContext(key, null, options); } - public ApiResponse resourcesByContext(String key,String value, 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"); - params.put("key",key); + params.put("key", key); if (StringUtils.isNotBlank(value)) { - params.put("value",value); + params.put("value", value); } - return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType,"context"), params , options); + return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "context"), params, options); } public ApiResponse resourcesByIds(Iterable publicIds, Map options) throws Exception { @@ -372,7 +376,8 @@ public ApiResponse createStreamingProfile(String name, String displayName, List< /** * Get a streaming profile information - * @param name the name of the profile to fetch + * + * @param name the name of the profile to fetch * @param options additional options * @return a streaming profile * @throws Exception an exception @@ -395,6 +400,7 @@ public ApiResponse getStreamingProfile(String name) throws Exception { /** * List Streaming profiles + * * @param options additional options * @return a list of all streaming profiles defined for the current cloud * @throws Exception an exception @@ -416,7 +422,8 @@ public ApiResponse listStreamingProfiles() throws Exception { /** * Delete a streaming profile information. Predefined profiles are restored to the default setting. - * @param name the name of the profile to delete + * + * @param name the name of the profile to delete * @param options additional options * @return a streaming profile * @throws Exception an exception @@ -481,11 +488,11 @@ public ApiResponse updateStreamingProfile(String name, String displayName, List< * @param accessMode The new access mode, "public" or "authenticated" * @param prefix The prefix by which to filter applicable resources * @param options additional options - *
    - *
  • resource_type - (default "image") - the type of resources to modify
  • - *
  • max_results - optional - the maximum resources to process in a single invocation
  • - *
  • next_cursor - optional - provided by a previous call to the method
  • - *
+ *
    + *
  • resource_type - (default "image") - the type of resources to modify
  • + *
  • max_results - optional - the maximum resources to process in a single invocation
  • + *
  • next_cursor - optional - provided by a previous call to the method
  • + *
* @return a map of the returned values *
    *
  • updated - an array of resources
  • @@ -503,11 +510,11 @@ public ApiResponse updateResourcesAccessModeByPrefix(String accessMode, String p * @param accessMode The new access mode, "public" or "authenticated" * @param tag The tag by which to filter applicable resources * @param options additional options - *
      - *
    • resource_type - (default "image") - the type of resources to modify
    • - *
    • max_results - optional - the maximum resources to process in a single invocation
    • - *
    • next_cursor - optional - provided by a previous call to the method
    • - *
    + *
      + *
    • resource_type - (default "image") - the type of resources to modify
    • + *
    • max_results - optional - the maximum resources to process in a single invocation
    • + *
    • next_cursor - optional - provided by a previous call to the method
    • + *
    * @return a map of the returned values *
      *
    • updated - an array of resources
    • @@ -537,11 +544,11 @@ public ApiResponse deleteFolder(String folder, Map options) throws Exception { * @param accessMode The new access mode, "public" or "authenticated" * @param publicIds A list of public ids of resources to be updated * @param options additional options - *
        - *
      • resource_type - (default "image") - the type of resources to modify
      • - *
      • max_results - optional - the maximum resources to process in a single invocation
      • - *
      • next_cursor - optional - provided by a previous call to the method
      • - *
      + *
        + *
      • resource_type - (default "image") - the type of resources to modify
      • + *
      • max_results - optional - the maximum resources to process in a single invocation
      • + *
      • next_cursor - optional - provided by a previous call to the method
      • + *
      * @return a map of the returned values *
        *
      • updated - an array of resources
      • @@ -564,4 +571,81 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O return callApi(HttpMethod.POST, uri, params, options); } + /** + * Add a new metadata field definition + * @param field The field to add. + * @return A map representing the newlay added field. + * @throws Exception + */ + public ApiResponse addMetadataField(MetadataField field) throws Exception { + return callApi(HttpMethod.POST, Collections.singletonList("metadata_fields"), + ObjectUtils.toMap(field), ObjectUtils.asMap ("content_type", "json")); + } + + /** + * List all the metadata field definitions (structure, not values) + * @return A map containing the list of field definitions maps. + * @throws Exception + */ + public ApiResponse listMetadataFields() throws Exception { + return callApi(HttpMethod.GET, Collections.singletonList("metadata_fields"), Collections.emptyMap(), Collections.emptyMap()); + } + + /** + * Get a metadata field definition by id + * @param fieldExternalId The id of the field to retrieve + * @return The fields definitions. + * @throws Exception + */ + public ApiResponse metadataFieldByFieldId(String fieldExternalId) throws Exception { + return callApi(HttpMethod.GET, Arrays.asList("metadata_fields", fieldExternalId), Collections.emptyMap(), Collections.emptyMap()); + } + + /** + * Update the definitions of a single metadata field. + * @param fieldExternalId The id of the field to update + * @param field The field definition + * @return The updated fields definition. + * @throws Exception + */ + public ApiResponse updateMetadataField(String fieldExternalId, MetadataField field) throws Exception { + List uri = Arrays.asList("metadata_fields", fieldExternalId); + return callApi(HttpMethod.PUT, uri, ObjectUtils.toMap(field), Collections.singletonMap("content_type", "json")); + } + + /** + * Update the datasource entries for a given field + * @param fieldExternalId The id of the field to update + * @param entries A list of datasource entries. Existing entries (according to entry id) will be updated, + * new entries will be added. + * @return The updated field definition. + * @throws Exception + */ + public ApiResponse updateMetadataFieldDatasource(String fieldExternalId, List entries) throws Exception { + List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource"); + return callApi(HttpMethod.PUT, uri, Collections.singletonMap("values", entries), Collections.singletonMap("content_type", "json")); + } + + /** + * Delete data source entries for a given field + * @param fieldExternalId The id of the field to update + * @param entriesExternalId The ids of all the entries to delete from the data source + * @return The remaining datasource entries. + * @throws Exception + */ + public ApiResponse deleteDatasourceEntries(String fieldExternalId, List entriesExternalId) throws Exception { + List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource"); + return callApi(HttpMethod.DELETE, uri,Collections.singletonMap("external_ids", entriesExternalId) , Collections.emptyMap()); + } + + /** + * Delete a field definition. + * @param fieldExternalId The id of the field to delete + * @return A map with a "message" key. "ok" value indicates a successful deletion. + * @throws Exception + */ + public ApiResponse deleteMetadataField(String fieldExternalId) throws Exception { + List uri = Arrays.asList("metadata_fields", fieldExternalId); + return callApi(HttpMethod.DELETE, uri, Collections.emptyMap(), Collections.emptyMap()); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index c6e17397..a93cfa0d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -516,4 +516,23 @@ public String imageUploadTag(String field, Map options, Map html public Map deleteByToken(String token) throws Exception { return callApi("delete_by_token", ObjectUtils.asMap("token", token), ObjectUtils.emptyMap(), null); } + + /** + * Populates metadata fields with the given values. Existing values will be overwritten. + * @param metadata a map of field name and value. + * @param publicIds the public IDs of the resources to update + * @param options additional options passed to the request + * @return a list of public IDs that were updated + * @throws IOException + */ + public Map updateMetadata(Map metadata, String[] publicIds, Map options) throws IOException { + if (options == null) + options = new HashMap(); + + Map params = new HashMap(); + params.put("metadata", Util.encodeContext(metadata)); + params.put("public_ids", Arrays.asList(publicIds)); + + return callApi("metadata", params, options, null); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 63f03d94..a6d97df7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -95,6 +95,8 @@ public static final void processWriteParameters(Map options, Map params.put("custom_coordinates", Coordinates.parseCoordinates(options.get("custom_coordinates")).toString()); if (options.get("context") != null) params.put("context", encodeContext(options.get("context"))); + if (options.get("metadata") != null) + params.put("metadata", encodeContext(options.get("metadata"))); if (options.get("access_control") != null) { params.put("access_control", encodeAccessControl(options.get("access_control"))); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java new file mode 100644 index 00000000..a4df9091 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java @@ -0,0 +1,40 @@ +package com.cloudinary.metadata; + +import com.cloudinary.utils.ObjectUtils; + +import java.text.ParseException; +import java.util.Date; + +/** + * Represents a metadata field with type 'date' + */ +public class DateMetadataField extends MetadataField { + + public DateMetadataField() { + super(MetadataFieldType.DATE); + } + + /** + * Sets the default date used for this field. + * @param defaultValue The date to set. Date only without a time component, UTC assumed. + */ + @Override + public void setDefaultValue(Date defaultValue) { + put(DEFAULT_VALUE, ObjectUtils.toISO8601DateOnly(defaultValue)); + } + + /** + * Get the default value of this date field. + * @return The date only without a time component, UTC. + * @throws ParseException When the underlying value is malformed. + */ + @Override + public Date getDefaultValue() throws ParseException { + Object value = get(DEFAULT_VALUE); + if (value == null) { + return null; + } + + return ObjectUtils.fromISO8601DateOnly(value.toString()); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java new file mode 100644 index 00000000..79f501c3 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java @@ -0,0 +1,10 @@ +package com.cloudinary.metadata; + +/** + * Represents a metadata field with 'Enum' type. + */ +public class EnumMetadataField extends MetadataField { + EnumMetadataField() { + super(MetadataFieldType.ENUM); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java new file mode 100644 index 00000000..23510210 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java @@ -0,0 +1,10 @@ +package com.cloudinary.metadata; + +/** + * Represents a metadata field with 'Int' type. + */ +public class IntMetadataField extends MetadataField { + public IntMetadataField() { + super(MetadataFieldType.INTEGER); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java new file mode 100644 index 00000000..043556cd --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java @@ -0,0 +1,70 @@ +package com.cloudinary.metadata; + +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; + +import java.util.List; + +/** + * Represent a data source for a given field. This is used in both 'Set' and 'Enum' field types. + * The datasource holds a list of the valid values to be used with the corresponding metadata field. + */ +public class MetadataDataSource extends JSONObject { + /** + * Creates a new instance of data source with the given list of entries. + * @param entries + */ + public MetadataDataSource(List entries) { + put("values", new JSONArray(entries.toArray())); + } + + /** + * Represents a single entry in a datasource definition for a field. + */ + public static class Entry extends JSONObject { + public Entry(String externalId, String value){ + setExternalId(externalId); + setValue(value); + } + + /** + * Create a new entry with a string value. + * @param value The value to use in the entry. + */ + public Entry(String value){ + this(null, value); + } + + /** + * Set the id of the entry. Will be auto-generated if left blank. + * @param externalId + */ + public void setExternalId(String externalId) { + put("external_id", externalId); + } + + /** + * Get the id of the entry. + * @return + */ + public String getExternalId() { + return optString("external_id"); + } + + /** + * Set the value of the entry. + * @param value The value to set. + */ + public void setValue(String value) { + put("value", value); + } + + /** + * Get the value of the entry. + * @return The value. + */ + public String getValue() { + return optString("value"); + } + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java new file mode 100644 index 00000000..bae2b6c7 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java @@ -0,0 +1,133 @@ +package com.cloudinary.metadata; + +import org.cloudinary.json.JSONObject; + +import java.text.ParseException; + +/** + * Represents a single metadata field. Use one of the derived classes in the metadata API calls. + * @param + */ +public class MetadataField extends JSONObject { + + public static final String DEFAULT_VALUE = "default_value"; + public static final String EXTERNAL_ID = "external_id"; + public static final String LABEL = "label"; + public static final String MANDATORY = "mandatory"; + public static final String TYPE = "type"; + public static final String VALIDATION = "validation"; + + public MetadataField(MetadataFieldType type) { + put(TYPE, type.toString()); + } + + public MetadataField(String type) { + put(TYPE, type); + } + + /** + * The type of the field. + * @return String with the name of the type. + */ + public MetadataFieldType getType() { + return MetadataFieldType.valueOf(optString(TYPE).toUpperCase()); + } + + /** + * Get the id of the field. + * @return String, field id. + */ + public String getExternalId() { + return optString(EXTERNAL_ID); + } + + /** + * Set the id of the string (auto-generated if this is left blank). + * @param externalId The id to set. + */ + public void setExternalId(String externalId) { + put(EXTERNAL_ID, externalId); + } + + /** + * Get the label of the field + * @return String, the label of the field. + */ + public String getLabel() { + return optString(LABEL); + } + + /** + * Sets the label of the field + * @param label The label to set. + */ + public void setLabel(String label) { + put(LABEL, label); + } + + /** + * Cehcks whether the field is mandatory. + * @return Boolean indicating whether the field is mandatory. + */ + public boolean isMandatory() { + return optBoolean(MANDATORY); + } + + /** + * Sets a boolean indicating whether this fields needs to be mandatory. + * @param mandatory The boolean to set. + */ + public void setMandatory(Boolean mandatory) { + put(MANDATORY, mandatory); + } + + /** + * Gets the default value of this field. + * @return The default value + * @throws ParseException If the stored value can't be parsed to the correct type. + */ + public T getDefaultValue() throws ParseException { + //noinspection unchecked + return (T)opt(DEFAULT_VALUE); + } + + /** + * Set the default value of the field + * @param defaultValue The value to set. + */ + public void setDefaultValue(T defaultValue) { + put(DEFAULT_VALUE, defaultValue); + } + + /** + * Get the validation rules of this field. + * @return The validation rules. + */ + public MetadataValidation getValidation() { + return (MetadataValidation) optJSONObject(VALIDATION); + } + + /** + * Set the validation rules of this field. + * @param validation The rules to set. + */ + public void setValidation(MetadataValidation validation) { + put(VALIDATION, validation); + } + + /** + * Get the data source definition of this field. + * @return The data source. + */ + public MetadataDataSource getDataSource() { + return (MetadataDataSource) optJSONObject("datasource"); + } + + /** + * Set the datasource for the field. + * @param dataSource The datasource to set. + */ + public void setDataSource(MetadataDataSource dataSource) { + put("datasource", dataSource); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java new file mode 100644 index 00000000..34362f27 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java @@ -0,0 +1,17 @@ +package com.cloudinary.metadata; + +/** + * Enum represneting all the valid field types. + */ +public enum MetadataFieldType { + STRING, + INTEGER, + DATE, + ENUM, + SET; + + @Override + public String toString() { + return super.toString().toLowerCase(); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java new file mode 100644 index 00000000..f38b732e --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java @@ -0,0 +1,177 @@ +package com.cloudinary.metadata; + +import com.cloudinary.utils.ObjectUtils; +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; + +import java.util.Date; +import java.util.List; + +/** + * Represents the base class for metadata fields validation mechanisms. + */ +public abstract class MetadataValidation extends JSONObject { + + public static final String TYPE = "type"; + public static final String MIN = "min"; + public static final String MAX = "max"; + public static final String STRLEN = "strlen"; + public static final String EQUALS = "equals"; + public static final String GREATER_THAN = "greater_than"; + public static final String LESS_THAN = "less_than"; + public static final String VALUE = "value"; + + /** + * An 'And' rule validation used to combine other rules with an 'AND' logic relation between them. + */ + public static class AndValidator extends MetadataValidation { + + public static final String AND = "and"; + + /** + * Create a new instance of the validator with the given rules. + * @param rules The rules to use. + */ + public AndValidator(List rules) { + put(TYPE, AND); + put("rules", new JSONArray(rules.toArray())); + } + } + + /** + * A validator to validate string lengths + */ + public static class StringLength extends MetadataValidation { + /** + * Create a new instance with the given min and max. + * @param min Minimum valid string length. + * @param max Maximum valid string length. + */ + public StringLength(Integer min, Integer max) { + put(TYPE, STRLEN); + put(MIN, min); + put(MAX, max); + } + } + + /** + * Base class for all comparison (greater than/less than) validation rules. + * @param + */ + public abstract static class ComparisonRule extends MetadataValidation { + public ComparisonRule(String type, T value) { + this(type, value, null); + } + + public ComparisonRule(String type, T value, Boolean equals) { + put(TYPE, type); + putValue(value); + if (equals != null) { + put(EQUALS, equals); + } + } + + protected void putValue(T value) { + put(VALUE, value); + } + } + + /** + * Great-than rule for integers. + */ + public static class IntGreaterThan extends ComparisonRule { + /** + * Create a new rule with the given integer. + * @param value The integer to reference in the rule + */ + public IntGreaterThan(Integer value) { + super(GREATER_THAN, value); + } + + /** + * Create a new rule with the given integer. + * @param value The integer to reference in the rule. + * @param equals Whether a field value equal to the rule value is considered valid. + */ + public IntGreaterThan(Integer value, Boolean equals) { + super(GREATER_THAN, value, equals); + } + } + + /** + * Great-than rule for dates. + */ + public static class DateGreaterThan extends ComparisonRule { + /** + * Create a new rule with the given date. + * @param value The integer to reference in the rule + */ + public DateGreaterThan(Date value) { + super(GREATER_THAN, value); + } + + /** + * Create a new rule with the given date. + * @param value The date to reference in the rule. + * @param equals Whether a field value equal to the rule value is considered valid. + */ + public DateGreaterThan(Date value, Boolean equals) { + super(GREATER_THAN, value, equals); + } + + @Override + protected void putValue(Date value) { + put(VALUE, ObjectUtils.toISO8601DateOnly(value)); + } + } + + /** + * Less-than rule for integers. + */ + public static class IntLessThan extends ComparisonRule { + /** + * Create a new rule with the given integer. + * @param value The integer to reference in the rule + */ + public IntLessThan(Integer value) { + super(LESS_THAN, value); + } + + /** + * Create a new rule with the given integer. + * @param value The integer to reference in the rule. + * @param equals Whether a field value equal to the rule value is considered valid. + */ + public IntLessThan(Integer value, Boolean equals) { + super(LESS_THAN, value, equals); + } + } + + /** + * Less-than rule for dates. + */ + public static class DateLessThan extends ComparisonRule { + /** + * Create a new rule with the given date. + * @param value The integer to reference in the rule + */ + public DateLessThan(Date value) { + super(LESS_THAN, value); + } + + /** + * Create a new rule with the given date. + * @param value The date to reference in the rule. + * @param equals Whether a field value equal to the rule value is considered valid. + */ + public DateLessThan(Date value, Boolean equals) { + super(LESS_THAN, value, equals); + } + + @Override + protected void putValue(Date value) { + put(VALUE, ObjectUtils.toISO8601DateOnly(value)); + } + } +} + diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java new file mode 100644 index 00000000..48d54823 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java @@ -0,0 +1,12 @@ +package com.cloudinary.metadata; + +import java.util.List; + +/** + * Represents a metadata field with 'Set' type. + */ +public class SetMetadataField extends MetadataField> { + public SetMetadataField() { + super(MetadataFieldType.SET); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java new file mode 100644 index 00000000..e7e03405 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java @@ -0,0 +1,10 @@ +package com.cloudinary.metadata; + +/** + * Represents a metadata field with 'String' type. + */ +public class StringMetadataField extends MetadataField { + public StringMetadataField() { + super(MetadataFieldType.STRING); + } +} \ No newline at end of file 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 ff358452..e2403372 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -1,11 +1,11 @@ package com.cloudinary.strategies; -import java.util.Map; - import com.cloudinary.Api; import com.cloudinary.Api.HttpMethod; import com.cloudinary.api.ApiResponse; +import java.util.Map; + public abstract class AbstractApiStrategy { protected Api api; 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 1725ad55..75bbc87c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -6,6 +6,7 @@ import java.io.*; import java.text.DateFormat; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -17,9 +18,19 @@ public class ObjectUtils { * @return The date formatted as ISO-8601 string */ public static String toISO8601(Date date){ + DateFormat dateFormat = getDateFormat(); + return dateFormat.format(date); + } + + public static Date fromISO8601(String date) throws ParseException { + DateFormat dateFormat = getDateFormat(); + return (Date) dateFormat.parseObject(date); + } + + private static DateFormat getDateFormat() { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - return dateFormat.format(date); + return dateFormat; } public static String asString(Object value) { @@ -204,4 +215,11 @@ public static Long asLong(Object value, Long defaultValue) { } } + public static String toISO8601DateOnly(Date date) { + return new SimpleDateFormat("yyyy-MM-dd").format(date); + } + + public static Date fromISO8601DateOnly(String string) throws ParseException { + return new SimpleDateFormat("yyyy-MM-dd").parse(string); + } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java new file mode 100644 index 00000000..900da239 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java @@ -0,0 +1,4 @@ +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/StructuredMetadataTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java new file mode 100644 index 00000000..900da239 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class StructuredMetadataTest extends AbstractStructuredMetadataTest { +} \ No newline at end of file diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java new file mode 100644 index 00000000..8cc186f4 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class StructuredMetadataTest extends AbstractStructuredMetadataTest { +} 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 new file mode 100644 index 00000000..d370ce5c --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -0,0 +1,252 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.BadRequest; +import com.cloudinary.metadata.*; +import org.junit.*; +import org.junit.rules.TestName; + +import java.io.IOException; +import java.util.*; + +import static com.cloudinary.utils.ObjectUtils.asMap; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeNotNull; + +public abstract class AbstractStructuredMetadataTest extends MockableTest { + private static final String METADATA_UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; + + protected Api api; + public static final List metadataFieldExternalIds = new ArrayList(); + + @BeforeClass + public static void setUpClass() throws IOException { + Cloudinary cloudinary = new Cloudinary(); + if (cloudinary.config.apiSecret == null) { + System.err.println("Please setup environment for Upload test to run"); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + Api api = new Cloudinary().api(); + + for (String externalId : metadataFieldExternalIds) { + try { + api.deleteMetadataField(externalId); + } catch (Exception ignored) { + } + } + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + } + + @Test + public void testCreateMetadata() throws Exception { + StringMetadataField stringField = newFieldInstance("testCreateMetadata_1"); + ApiResponse result = addFieldToAccount(stringField); + assertNotNull(result); + assertEquals(stringField.getLabel(), result.get("label")); + + SetMetadataField setField = createSetField("testCreateMetadata_2"); + result = cloudinary.api().addMetadataField(setField); + assertNotNull(result); + assertEquals(setField.getLabel(), result.get("label")); + } + + @Test + public void testDateFieldDefaultValueValidation() throws Exception { + // now minus 3 days hours. + Date max = new Date(); + Date min = new Date(max.getTime() - 72 * 60 * 60 * 1000); + + Date legalValue = new Date(min.getTime() + 36 * 60 * 60 * 1000); + Date illegalValue = new Date(max.getTime() + 36 * 60 * 60 * 1000); + + DateMetadataField dateMetadataField = new DateMetadataField(); + dateMetadataField.setLabel("Start date" + new Date().getTime()); + + List rules = new ArrayList(); + rules.add(new MetadataValidation.DateGreaterThan(min)); + rules.add(new MetadataValidation.DateLessThan(max)); + dateMetadataField.setValidation(new MetadataValidation.AndValidator(rules)); + + String message = null; + ApiResponse res = null; + try { + // should fail + dateMetadataField.setDefaultValue(illegalValue); + res = api.addMetadataField(dateMetadataField); + // this line should not be reached if all is working well, but when it's not we still want to clean it up: + metadataFieldExternalIds.add(res.get("external_id").toString()); + } catch (BadRequest e) { + message = e.getMessage(); + } + + assertEquals(message, "default_value is invalid"); + + // should work: + dateMetadataField.setDefaultValue(legalValue); + res = api.addMetadataField(dateMetadataField); + metadataFieldExternalIds.add(res.get("external_id").toString()); + } + + @Test + public void testListFields() throws Exception { + StringMetadataField stringField = newFieldInstance("testListFields"); + addFieldToAccount(stringField); + + ApiResponse result = cloudinary.api().listMetadataFields(); + assertNotNull(result); + assertNotNull(result.get("metadata_fields")); + assertTrue(((List)result.get("metadata_fields")).size() > 0); + } + + @Test + public void testGetMetadata() throws Exception { + ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testGetMetadata")); + ApiResponse result = api.metadataFieldByFieldId(fieldResult.get("external_id").toString()); + assertNotNull(result); + assertEquals(fieldResult.get("label"), result.get("label")); + } + + @Test + public void testUpdateField() throws Exception { + ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testUpdateField")); + 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); + assertNotNull(result); + assertEquals("new_def", result.get("default_value")); + } + + @Test + public void testDeleteField() throws Exception { + ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testDeleteField")); + ApiResponse result = api.deleteMetadataField(fieldResult.get("external_id").toString()); + assertNotNull(result); + assertEquals("ok", result.get("message")); + } + + @Test + public void testUpdateDatasource() throws Exception { + SetMetadataField setField = createSetField("testUpdateDatasource"); + ApiResponse fieldResult = addFieldToAccount(setField); + MetadataDataSource.Entry newEntry = new MetadataDataSource.Entry("id1", "new1"); + ApiResponse result = api.updateMetadataFieldDatasource(fieldResult.get("external_id").toString(), Collections.singletonList(newEntry)); + assertNotNull(result); + assertEquals("new1", ((Map) ((List) result.get("values")).get(0)).get("value")); + } + + @Test + public void testDeleteDatasourceEntries() throws Exception { + SetMetadataField setField = createSetField("testDeleteDatasourceEntries"); + ApiResponse fieldResult = addFieldToAccount(setField); + ApiResponse result = api.deleteDatasourceEntries(fieldResult.get("external_id").toString(), Collections.singletonList("id1")); + assertNotNull(result); + } + + @Test + public void testUploadWithMetadata() throws Exception { + StringMetadataField field = newFieldInstance("testUploadWithMetadata"); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + Map metadata = Collections.singletonMap(fieldId, "123456"); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("metadata", metadata, "tags", Arrays.asList(SDK_TEST_TAG, METADATA_UPLOADER_TAG))); + assertNotNull(result.get("metadata")); + assertEquals("123456", ((Map) result.get("metadata")).get(fieldId)); + } + + @Test + 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"); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + Map metadata = Collections.singletonMap(fieldId, "123456"); + Map result = cloudinary.uploader().explicit(publicId, asMap("type", "upload", "resource_type", "image", "metadata", metadata)); + assertNotNull(result.get("metadata")); + assertEquals("123456", ((Map) result.get("metadata")).get(fieldId)); + + // explicit with invalid data, should fail: + metadata = Collections.singletonMap(fieldId, "12"); + String message = ""; + try { + result = cloudinary.uploader().explicit(publicId, asMap("type", "upload", "resource_type", "image", "metadata", metadata)); + } catch (Exception e){ + message = e.getMessage(); + } + + assertTrue(message.contains("Value 12 is invalid for field") ); + } + + @Test + 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"); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + Map metadata = Collections.singletonMap(fieldId, "123456"); + Map result = cloudinary.api().update(publicId, asMap("type", "upload", "resource_type", "image", "metadata", metadata)); + assertNotNull(result.get("metadata")); + assertEquals("123456", ((Map) result.get("metadata")).get(fieldId)); + } + + @Test + public void testUploaderUpdateMetadata() throws Exception { + StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata"); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{"sample"}, null); + assertNotNull(result); + assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + } + + // Metadata test helpers + private SetMetadataField createSetField(String labelPrefix) { + SetMetadataField setField = new SetMetadataField(); + String label = labelPrefix + "_" + SUFFIX; + setField.setLabel(label); + setField.setMandatory(false); + setField.setValidation(new MetadataValidation.StringLength(3, 99)); + setField.setDefaultValue(Arrays.asList("id2", "id3")); + setField.setValidation(null); + List entries = new ArrayList(); + entries.add(new MetadataDataSource.Entry("id1", "first_value")); + entries.add(new MetadataDataSource.Entry("id2", "second_value")); + entries.add(new MetadataDataSource.Entry("id3", "third_value")); + MetadataDataSource dataSource = new MetadataDataSource(entries); + setField.setDataSource(dataSource); + return setField; + } + + private StringMetadataField newFieldInstance(String labelPrefix) throws Exception { + StringMetadataField field = new StringMetadataField(); + String label = labelPrefix + "_" + SUFFIX; + field.setLabel(label); + field.setMandatory(true); + field.setValidation(new MetadataValidation.StringLength(3, 9)); + field.setDefaultValue("val_test"); + return field; + } + + private ApiResponse addFieldToAccount(MetadataField field) throws Exception { + ApiResponse apiResponse = api.addMetadataField(field); + metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); + return apiResponse; + } +} 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 c213778e..1208c694 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,6 +1,8 @@ package com.cloudinary.test; import com.cloudinary.*; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.metadata.StringMetadataField; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; From 63bc21aad907165f84ad096779c43c9071134a91 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 8 Aug 2019 12:38:11 +0300 Subject: [PATCH 404/592] Add account API support (user and cloud management) (#176) --- .travis.yml | 2 +- .../src/main/java/com/cloudinary/Api.java | 4 + .../main/java/com/cloudinary/Cloudinary.java | 2 +- .../com/cloudinary/provisioning/Account.java | 412 ++++++++++++++++++ .../provisioning/AccountConfiguration.java | 35 ++ .../strategies/AbstractApiStrategy.java | 2 + .../com/cloudinary/http42/ApiStrategy.java | 51 ++- .../com/cloudinary/test/AccountApiTest.java | 4 + .../java/com/cloudinary/test/ContextTest.java | 4 + .../com/cloudinary/http43/ApiStrategy.java | 30 +- .../com/cloudinary/test/AccountApiTest.java | 4 + .../com/cloudinary/http44/ApiStrategy.java | 27 ++ .../com/cloudinary/test/AccountApiTest.java | 4 + .../test/AbstractAccountApiTest.java | 309 +++++++++++++ 14 files changed, 870 insertions(+), 20 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/provisioning/AccountConfiguration.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java diff --git a/.travis.yml b/.travis.yml index e5b7ffdf..4880270c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,5 +28,5 @@ branches: - staging-test # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${MODULE} -i +script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_PROVISIONING_CONFIG=$CLOUDINARY_PROVISIONING_CONFIG ciTest -p cloudinary-${MODULE} -i diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 74164173..0e89f1bf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -16,6 +16,10 @@ public class Api { + public AbstractApiStrategy getStrategy() { + return strategy; + } + public enum HttpMethod {GET, POST, PUT, DELETE;} public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 2aa14c2e..c128d4ee 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -21,7 +21,7 @@ public class Cloudinary { "com.cloudinary.http42.UploaderStrategy", "com.cloudinary.http43.UploaderStrategy", "com.cloudinary.http44.UploaderStrategy")); - private static List API_STRATEGIES = new ArrayList(Arrays.asList( + public static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", "com.cloudinary.http42.ApiStrategy", "com.cloudinary.http43.ApiStrategy", diff --git a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java new file mode 100644 index 00000000..cdab08b8 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -0,0 +1,412 @@ +package com.cloudinary.provisioning; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; + +import java.util.*; + +/** + * Entry point class for all account and provisioning API actions: Manage users, cloud names and user groups. + */ +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 USERS = "users"; + public static final String USER_GROUPS = "user_groups"; + + private final AccountConfiguration configuration; + private final String accountId; + private final String key; + private final String secret; + private final Api api; + + /** + * Create a new instance to use the account API. The account information will be extracted from + * an environment variable CLOUDINARY_ACCOUNT_URL. If it's missing an exception will be thrown. + * + * @param cloudinary A cloudinary instance. This is used to fetch the correct network configuration. + */ + public Account(Cloudinary cloudinary) { + String provisioningData = System.getProperty(CLOUDINARY_ACCOUNT_URL, System.getenv(CLOUDINARY_ACCOUNT_URL)); + if (provisioningData != null) { + this.configuration = AccountConfiguration.from(provisioningData); + this.accountId = configuration.accountId; + this.key = configuration.provisioningApiKey; + this.secret = configuration.provisioningApiSecret; + } else { + throw new IllegalArgumentException("Must provide configuration instance or set an ENV variable: " + + "CLOUDINARY_ACCOUNT_URL=account://provisioning_api_key:provisioning_api_secret@account_id"); + } + + this.api = cloudinary.api(); + } + + /** + * Create a new instance to use the account API. The account information will be extracted from + * + * @param accountConfiguration Account configuration to use in requests. + * @param cloudinary A cloudinary instance. This is used to fetch the correct network configuration. + */ + public Account(AccountConfiguration accountConfiguration, Cloudinary cloudinary) { + this.configuration = accountConfiguration; + this.api = cloudinary.api(); + this.accountId = accountConfiguration.accountId; + this.key = accountConfiguration.provisioningApiKey; + this.secret = accountConfiguration.provisioningApiSecret; + } + + private ApiResponse callAccountApi(Api.HttpMethod method, List uri, Map params, Map options) throws Exception { + options = verifyOptions(options); + + if (options.containsKey("provisioning_api_key")){ + if (!options.containsKey("provisioning_api_secret")){ + throw new IllegalArgumentException("When providing key or secret through options, both must be provided"); + } + } else { + if (options.containsKey("provisioning_api_secret")){ + throw new IllegalArgumentException("When providing key or secret through options, both must be provided"); + } + options.put("provisioning_api_key", key); + options.put("provisioning_api_secret", secret); + } + + return api.getStrategy().callAccountApi(method, uri, params, options); + } + + /** + * A user role to use in the user management API (create/update user). + */ + public enum Role { + MASTER_ADMIN("master_admin"), + ADMIN("admin"), + TECHNICAL_ADMIN("technical_admin"), + BILLING("billing"), + REPORTS("reports"), + MEDIA_LIBRARY_ADMIN("media_library_admin"), + MEDIA_LIBRARY_USER("media_library_user"); + + private final String serializedValue; + + Role(String serializedValue) { + this.serializedValue = serializedValue; + } + + @Override + public String toString() { + return serializedValue; + } + } + + // Sub accounts + + /** + * Get details of a specific sub account + * + * @param subAccountId The id of the sub account + * @param options Generic advanced options map, see online documentation. + * @return the sub account details. + * @throws Exception If the request fails + */ + public ApiResponse getSubAccount(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); + } + + /** + * Get a list of sub accounts. + * + * @param enabled Optional. Whether to fetch enabled or disabled accounts. Default is all. + * @param ids Optional. List of sub-account IDs. Up to 100. When provided, other filters are ignored. + * @param prefix Optional. Search by prefix of the sub-account name. Case-insensitive. + * @param options Generic advanced options map, see online documentation. + * @return the list of sub-accounts details. + * @throws Exception If the request fails + */ + public ApiResponse getSubAccounts(Boolean enabled, List ids, String prefix, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, "sub_accounts"); + return callAccountApi(Api.HttpMethod.GET, uri, + ObjectUtils.asMap("accountId", accountId, "enabled", enabled, "ids", ids, "prefix", prefix), options); + } + + /** + * @param name Required. The name displayed in the management console. + * @param cloudName Optional, unique (case insensitive) + * @param customAttributes Advanced custom attributes for the sub-account. + * @param enabled Optional. Whether to create the account as enabled (default is enabled). + * @param baseAccount Optional. ID of sub-account from which to copy settings + * @param options Generic advanced options map, see online documentation. + * @return details of the created sub-account + * @throws Exception If the request fails + */ + public ApiResponse createSubAccount(String name, String cloudName, Map customAttributes, boolean enabled, String baseAccount, Map options) throws Exception { + options = verifyOptions(options); + options.put("content_type", "json"); + + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, "sub_accounts"); + + return callAccountApi(Api.HttpMethod.POST, uri, ObjectUtils.asMap( + "cloud_name", cloudName, + "name", name, + "custom_attributes", customAttributes, + "enabled", enabled, + "base_sub_account_id", baseAccount), + options); + } + + /** + * @param subAccountId The id of the sub-account to update + * @param name The name displayed in the management console. + * @param cloudName The cloud name to set. + * @param customAttributes Advanced custom attributes for the sub-account. + * @param enabled Set the sub-account as enabled or not. + * @param options Generic advanced options map, see online documentation. + * @return details of the updated sub-account + * @throws Exception If the request fails + */ + public ApiResponse updateSubAccount(String subAccountId, String name, String cloudName, Map customAttributes, Boolean enabled, Map options) throws Exception { + options = verifyOptions(options); + options.put("content_type", "json"); + + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, "sub_accounts", subAccountId); + + return callAccountApi(Api.HttpMethod.PUT, uri, ObjectUtils.asMap( + "cloud_name", cloudName, + "name", name, + "custom_attributes", customAttributes, + "enabled", enabled), + options); + } + + /** + * Deletes the sub-account. + * + * @param subAccountId The id of the sub-account to delete + * @param options Generic advanced options map, see online documentation. + * @return result message. + * @throws Exception If the request fails. + */ + public ApiResponse deleteSubAccount(String subAccountId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, "sub_accounts", subAccountId); + return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + + // Users + + /** + * Get details of a specific user. + * + * @param userId The id of the user to fetch + * @param options Generic advanced options map, see online documentation. + * @return details of the user. + * @throws Exception If the request fails. + */ + public ApiResponse getUser(String userId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS, userId); + return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); + } + + /** + * Get a list of the users according to filters. + * + * @param pending Optional. Whether to fetch pending users. Default all. + * @param userIds Optionals. List of user IDs. Up to 100 + * @param prefix Optional. Search by prefix of the user's name or email. Case-insensitive + * @param subAccountId Optional. Return only users who have access to the given sub-account + * @param options Generic advanced options map, see online documentation. + * @return the users' details. + * @throws Exception If the request fails. + */ + public ApiResponse getUsers(Boolean pending, List userIds, String prefix, String subAccountId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS); + return callAccountApi(Api.HttpMethod.GET, uri, + ObjectUtils.asMap("accountId", accountId, + "pending", pending, + "user_ids", userIds, + "prefix", prefix, + "sub_account_id", subAccountId), options); + } + + /** + * Create a new user. + * + * @param name Required. Username. + * @param email Required. User's email. + * @param role Required. User's role. + * @param subAccountsIds Optional. Sub-accounts for which the user should have access. + * If not provided or empty, user should have access to all accounts. + * @param options Generic advanced options map, see online documentation. + * @return The newly created user details. + * @throws Exception If the request fails. + */ + public ApiResponse createUser(String name, String email, Role role, List subAccountsIds, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS); + return performUserAction(Api.HttpMethod.POST, uri, email, name, role, subAccountsIds, options); + } + + /** + * Update an existing user. + * + * @param userId The id of the user to update. + * @param name Username. + * @param email User's email. + * @param role User's role. + * @param subAccountsIds Sub-accounts for which the user should have access. + * * If not provided or empty, user should have access to all accounts. + * @param options Generic advanced options map, see online documentation. + * @return The updated user details + * @throws Exception If the request fails. + */ + public ApiResponse updateUser(String userId, String name, String email, Role role, List subAccountsIds, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS, userId); + return performUserAction(Api.HttpMethod.PUT, uri, email, name, role, subAccountsIds, options); + } + + /** + * Delete a user. + * + * @param userId Id of the user to delete. + * @param options Generic advanced options map, see online documentation. + * @return result message. + * @throws Exception + */ + public ApiResponse deleteUser(String userId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS, userId); + return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + + // Groups + + /** + * Create a new user group + * @param name Required. Name for the group. + * @param options Generic advanced options map, see online documentation. + * @return The newly created group. + * @throws Exception If the request fails + */ + public ApiResponse createUserGroup(String name, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS); + return callAccountApi(Api.HttpMethod.POST, uri, ObjectUtils.asMap("name", name), options); + } + + /** + * Update an existing user group + * + * @param groupId The id of the group to update + * @param name The name of the group. + * @param options Generic advanced options map, see online documentation. + * @return The updated group. + * @throws Exception If the request fails + */ + public ApiResponse updateUserGroup(String groupId, String name, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId); + return callAccountApi(Api.HttpMethod.PUT, uri, ObjectUtils.asMap("name", name), options); + } + + /** + * Delete a user group + * + * @param groupId The group id to delete + * @param options Generic advanced options map, see online documentation. + * @return A result message. + * @throws Exception if the request fails. + */ + public ApiResponse deleteUserGroup(String groupId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId); + return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + + /** + * Add an existing user to a group. + * @param groupId The group id. + * @param userId The user id to add. + * @param options Generic advanced options map, see online documentation. + * @throws Exception If the request fails + */ + public ApiResponse addUserToGroup(String groupId, String userId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId, USERS, userId); + return callAccountApi(Api.HttpMethod.POST, uri, Collections.emptyMap(), options); + } + + /** + * Removes a user from a group. + * @param groupId The group id. + * @param userId The id of the user to remove + * @param options Generic advanced options map, see online documentation. + * @return A result message + * @throws Exception If the request fails. + */ + public ApiResponse removeUserFromGroup(String groupId, String userId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId, USERS, userId); + return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + + /** + * Get details of a group. + * @param groupId The group id to fetch + * @param options Generic advanced options map, see online documentation. + * @return Details of the group. + * @throws Exception If the request fails. + */ + public ApiResponse getUserGroup(String groupId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId); + return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); + } + + /** + * Gets a list of all the user groups. + * @param options Generic advanced options map, see online documentation. + * @return The list of the groups. + * @throws Exception If the request fails. + */ + public ApiResponse listUserGroups(Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS); + return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); + } + + /** + * Lists the users belonging to this user group. + * @param groupId The id of the user group. + * @param options Generic advanced options map, see online documentation. + * @return The list of users in that group. + * @throws Exception If the request fails. + */ + public ApiResponse listUserGroupUsers(String groupId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId, USERS); + return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); + } + + /** + * Private helper method for users api calls + * @param method Http method + * @param uri Uri to call + * @param email user email + * @param name user name + * @param role user role + * @param subAccountsIds suv accounts ids the user has access to. + * @param options + * @return The response of the api call. + * @throws Exception If the request fails. + */ + private ApiResponse performUserAction(Api.HttpMethod method, List uri, String email, String name, Role role, List subAccountsIds, Map options) throws Exception { + options = verifyOptions(options); + options.put("content_type", "json"); + + return callAccountApi(method, uri, ObjectUtils.asMap( + "email", email, + "name", name, + "role", role == null ? null : role.serializedValue, + "sub_account_ids", subAccountsIds), + options); + } + + private Map verifyOptions(Map options) { + if (options == null) { + return new HashMap(2); // Two, since api key and secret will be populated later + } + + return options; + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/provisioning/AccountConfiguration.java b/cloudinary-core/src/main/java/com/cloudinary/provisioning/AccountConfiguration.java new file mode 100644 index 00000000..2d52ca43 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/AccountConfiguration.java @@ -0,0 +1,35 @@ +package com.cloudinary.provisioning; + +import com.cloudinary.utils.StringUtils; + +import java.net.URI; + +public class AccountConfiguration { + private static final String SEPARATOR = ":"; + String accountId; + String provisioningApiKey; + String provisioningApiSecret; + + public AccountConfiguration(String accountId, String provisioningApiKey, String provisioningApiSecret) { + this.accountId = accountId; + this.provisioningApiKey = provisioningApiKey; + this.provisioningApiSecret = provisioningApiSecret; + } + + public static AccountConfiguration from(String accountUrl) { + URI uri = URI.create(accountUrl); + + String accountId = uri.getHost(); + if (StringUtils.isBlank(accountId)) throw new IllegalArgumentException("Account id must be provided in account url"); + + if (uri.getUserInfo() == null) throw new IllegalArgumentException("Full credentials (key+secret) must be provided in account url"); + String[] credentials = uri.getUserInfo().split(":"); + if (credentials.length < 2 || + StringUtils.isBlank(credentials[0]) || + StringUtils.isBlank(credentials[1])) { + throw new IllegalArgumentException("Full credentials (key+secret) must be provided in account url"); + } + + return new AccountConfiguration(accountId, credentials[0], credentials[1]); + } +} 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 e2403372..c5b68906 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -15,4 +15,6 @@ public void init(Api api) { @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; } 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 98c3a93b..a0a9e0bf 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -1,12 +1,15 @@ package com.cloudinary.http42; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.util.Arrays; -import java.util.Map; - +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; @@ -19,15 +22,11 @@ import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; -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.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; +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 { @@ -50,6 +49,28 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map 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, contentType, timeout, apiUrl); + } + + private ApiResponse getApiResponse(HttpMethod method, Map params, String apiKey, String apiSecret, String contentType, int timeout, String apiUrl) throws Exception { URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); if (!contentType.equals("json")) { for (Map.Entry param : params.entrySet()) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java new file mode 100644 index 00000000..573a12e5 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class AccountApiTest extends AbstractAccountApiTest { +} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java new file mode 100644 index 00000000..1c126299 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java @@ -0,0 +1,4 @@ +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 index 39721ebe..aa753d91 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -11,7 +11,6 @@ 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.*; @@ -22,7 +21,6 @@ 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.message.BasicNameValuePair; import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; @@ -32,7 +30,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; -import java.util.List; import java.util.HashMap; import java.util.Map; @@ -95,6 +92,10 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map 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 * diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java new file mode 100644 index 00000000..573a12e5 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class AccountApiTest extends AbstractAccountApiTest { +} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index 1f7a4854..aa16c031 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -94,6 +94,10 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map 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 * diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java new file mode 100644 index 00000000..573a12e5 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class AccountApiTest extends AbstractAccountApiTest { +} 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 new file mode 100644 index 00000000..84256507 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java @@ -0,0 +1,309 @@ +package com.cloudinary.test; + + +import com.cloudinary.Cloudinary; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.provisioning.Account; +import org.junit.*; +import org.junit.rules.TestName; + +import java.util.*; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public abstract class AbstractAccountApiTest extends MockableTest { + private static Random rand = new Random(); + protected Account account; + private static Set createdSubAccountIds = new HashSet(); + private static Set createdUserIds = new HashSet(); + private static Set createdGroupIds = new HashSet(); + + @BeforeClass + public static void setUpClass() { + + } + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() throws Exception { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.account = new Account(new Cloudinary()); + } + + @AfterClass + public static void tearDownClass() { + Account account = new Account(new Cloudinary()); + for (String createdSubAccountId : createdSubAccountIds) { + try { + account.deleteSubAccount(createdSubAccountId, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + for (String userId : createdUserIds) { + try { + account.deleteUser(userId, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + for (String groupId : createdGroupIds) { + try { + account.deleteUserGroup(groupId, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void testPassingCredentialsThroughOptions() throws Exception { + int exceptions = 0; + + Map map = singletonMap("provisioning_api_secret", new Object()) ; + try { + this.account.getSubAccounts(true, null, null, map); + } catch (IllegalArgumentException ignored){ + exceptions++; + } + + map = singletonMap("provisioning_api_key", new Object()) ; + try { + this.account.getSubAccounts(true, null, null, map); + } catch (IllegalArgumentException ignored){ + exceptions++; + } + + map = new HashMap(); + map.put("provisioning_api_key", "abc"); + map.put("provisioning_api_secret", "def"); + + try { + this.account.getSubAccounts(true, null, null, map); + } catch (Exception ex){ + assertTrue(ex.getMessage().contains("Invalid credentials")); + exceptions++; + } + + assertEquals(3, exceptions); + } + + // Sub accounts tests + @Test + public void testGetSubAccount() throws Exception { + ApiResponse accountResponse = createSubAccount(); + ApiResponse account = this.account.getSubAccount(accountResponse.get("id").toString(), null); + assertNotNull(account); + } + + @Test + public void testGetSubAccounts() throws Exception { + createSubAccount(); + ApiResponse accounts = account.getSubAccounts(null, null, null, null); + assertNotNull(accounts); + assertTrue(((ArrayList) accounts.get("sub_accounts")).size() >= 1); + } + + @Test + public void testCreateSubAccount() throws Exception { + ApiResponse result = createSubAccount(); + assertNotNull(result); + + String message = ""; + try { + // test that the parameters are passed correctly - throws exception since the from-account id doesn't exist: + account.createSubAccount(randomLetters(), null, emptyMap(), true, "non-existing-id", null); + } catch (Exception ex){ + message = ex.getMessage(); + } + + assertTrue(message.contains("cannot find sub account")); + } + + @Test + public void testUpdateSubAccount() throws Exception { + ApiResponse subAccount = createSubAccount(); + String newCloudName = randomLetters(); + ApiResponse result = account.updateSubAccount(subAccount.get("id").toString(), null, newCloudName, Collections.emptyMap(), null, null); + assertNotNull(result); + assertEquals(result.get("cloud_name"), newCloudName); + } + + @Test + public void testDeleteSubAccount() throws Exception { + ApiResponse createResult = createSubAccount(); + String id = createResult.get("id").toString(); + ApiResponse result = account.deleteSubAccount(id, null); + assertNotNull(result); + assertEquals(result.get("message"), "ok"); + createdSubAccountIds.remove(id); + } + + // Users test + @Test + public void testGetUser() throws Exception { + ApiResponse user = createUser(); + ApiResponse result = account.getUser(user.get("id").toString(), null); + assertNotNull(result); + } + + @Test + public void testGetUsers() throws Exception { + createUser(Account.Role.MASTER_ADMIN); + ApiResponse result = account.getUsers(null, null, null, null, null); + assertNotNull(result); + assertTrue(((ArrayList) result.get("users")).size() >= 1); + } + + @Test + public void testCreateUser() throws Exception { + ApiResponse createResult = createSubAccount(); + ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString())); + assertNotNull(result); + } + + @Test + public void testUpdateUser() throws Exception { + ApiResponse user = createUser(Account.Role.ADMIN); + + String newName = randomLetters(); + ApiResponse result = account.updateUser(user.get("id").toString(), newName, null, null, null, null); + assertNotNull(result); + assertEquals(result.get("name"), newName); + } + + @Test + public void testDeleteUser() throws Exception { + ApiResponse user = createUser(Collections.emptyList()); + String id = user.get("id").toString(); + ApiResponse result = account.deleteUser(id, null); + assertEquals(result.get("message"), "ok"); + createdUserIds.remove(id); + } + + // groups + @Test + public void testCreateUserGroup() throws Exception { + ApiResponse group = createGroup(); + assertNotNull(group); + } + + @Test + public void testUpdateUserGroup() throws Exception { + ApiResponse group = createGroup(); + String newName = randomLetters(); + ApiResponse result = account.updateUserGroup(group.get("id").toString(), newName, null); + assertNotNull(result); + } + + @Test + public void testDeleteUserGroup() throws Exception { + ApiResponse group = createGroup(); + String id = group.get("id").toString(); + ApiResponse result = account.deleteUserGroup(id, null); + assertNotNull(result); + assertEquals(result.get("ok"), true); + createdGroupIds.remove(id); + } + + @Test + public void testAddUserToUserGroup() throws Exception { + ApiResponse user = createUser(); + ApiResponse group = createGroup(); + ApiResponse result = account.addUserToGroup(group.get("id").toString(), user.get("id").toString(), null); + assertNotNull(result); + } + + @Test + public void testRemoveUserFromUserGroup() throws Exception { + ApiResponse user = createUser(Account.Role.MEDIA_LIBRARY_ADMIN); + ApiResponse group = createGroup(); + String groupId = group.get("id").toString(); + String userId = user.get("id").toString(); + account.addUserToGroup(groupId, userId, null); + ApiResponse result = account.removeUserFromGroup(groupId, userId, null); + assertNotNull(result); + } + + @Test + public void testListUserGroups() throws Exception { + createGroup(); + ApiResponse result = account.listUserGroups(null); + assertNotNull(result); + assertTrue(((List) result.get("user_groups")).size() >= 1); + } + + @Test + public void testListUserGroup() throws Exception { + ApiResponse group = createGroup(); + ApiResponse result = account.getUserGroup(group.get("id").toString(), null); + assertNotNull(result); + } + + @Test + public void testListUsersInGroup() throws Exception { + ApiResponse user1 = createUser(); + ApiResponse user2 = createUser(); + ApiResponse group = createGroup(); + String groupId = group.get("id").toString(); + String user1Id = user1.get("id").toString(); + String user2Id = user2.get("id").toString(); + account.addUserToGroup(groupId, user1Id, null); + account.addUserToGroup(groupId, user2Id, null); + ApiResponse result = account.listUserGroupUsers(groupId, null); + assertNotNull(result); + assertTrue(((List) result.get("users")).size() >= 2); + } + + + // Helpers + private ApiResponse createGroup() throws Exception { + String name = randomLetters(); + ApiResponse userGroup = account.createUserGroup(name, null); + createdGroupIds.add(userGroup.get("id").toString()); + return userGroup; + + } + + private ApiResponse createUser(Account.Role role) throws Exception { + return createUser(Collections.emptyList(), role); + } + + private ApiResponse createUser() throws Exception { + return createUser(Collections.emptyList()); + } + + private ApiResponse createUser(List subAccountsIds) throws Exception { + return createUser(subAccountsIds, Account.Role.BILLING); + } + + private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { + String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + ApiResponse user = account.createUser("TestName", email, role, subAccountsIds, null); + createdUserIds.add(user.get("id").toString()); + return user; + } + + private ApiResponse createSubAccount() throws Exception { + ApiResponse subAccount = account.createSubAccount(randomLetters(), null, emptyMap(), true, null, null); + createdSubAccountIds.add(subAccount.get("id").toString()); + return subAccount; + } + + private static String randomLetters() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + sb.append((char) ('a' + rand.nextInt('z' - 'a' + 1))); + } + + return sb.toString(); + } +} From 37af8db5a37972584f7cbe8f0c156dde721572de Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 15 Aug 2019 12:23:59 +0300 Subject: [PATCH 405/592] Add travis parameter pass-through for account_url (#177) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4880270c..d0afbc87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,5 +28,5 @@ branches: - staging-test # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_PROVISIONING_CONFIG=$CLOUDINARY_PROVISIONING_CONFIG ciTest -p cloudinary-${MODULE} -i +script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_ACCOUNT_URL=CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i From bf03becea4f268919cc685080e8c726b663e5ede Mon Sep 17 00:00:00 2001 From: asisayag Date: Thu, 15 Aug 2019 12:26:57 +0300 Subject: [PATCH 406/592] Version 1.23.0 --- CHANGELOG.md | 19 +++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fba4c2..2dbdc96d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ +1.23.0 / 2019-08-15 +=================== + +New functionality +----------------- + * Add account API support (user and cloud management) (#176) + * Add structured metadata APIs and entities (#171) + * Add duration to conditions in video (#172) + * Add support for `live` parameter to Upload Preset (#173) + * Add support for folder deletion (#170) + * Add support for forcing a version when generating URLs. + * Add support for custom pre-functions in transformation (wasm/remote). (#162) + +Other changes +------------- + * Fix base64 url validation (accept parameters). (#165) + * Fix build script and travis.yml to support more java versions. + * Remove test for similarity search (#163) + 1.22.1 / 2019-02-13 =================== diff --git a/README.md b/README.md index 347e447c..8bbcf71a 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.22.1 + 1.23.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.22.1/cloudinary-core-1.22.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.22.1/cloudinary-http44-1.22.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.23.0/cloudinary-core-1.23.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.23.0/cloudinary-http44-1.23.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c128d4ee..8b22f7fc 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.22.1"; + public final static String VERSION = "1.23.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index a2bab305..afd267a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.22.1 +version=1.23.0 From e4e3ec249dc6660ac4d775f66d87882ff97caa26 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 11 Sep 2019 13:19:46 +0300 Subject: [PATCH 407/592] Rename Account API methos, add convenience overloads. (#181) --- .../com/cloudinary/provisioning/Account.java | 227 +++++++++++++++++- .../test/AbstractAccountApiTest.java | 24 +- 2 files changed, 228 insertions(+), 23 deletions(-) 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 cdab08b8..76b56fd0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -101,6 +101,16 @@ public String toString() { } // Sub accounts + /** + * Get details of a specific sub account + * + * @param subAccountId The id of the sub account + * @return the sub account details. + * @throws Exception If the request fails + */ + public ApiResponse subAccount(String subAccountId) throws Exception { + return subAccount(subAccountId, Collections.emptyMap()); + } /** * Get details of a specific sub account @@ -110,11 +120,24 @@ public String toString() { * @return the sub account details. * @throws Exception If the request fails */ - public ApiResponse getSubAccount(String subAccountId, Map options) throws Exception { + public ApiResponse subAccount(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); } + /** + * Get a list of sub accounts. + * + * @param enabled Optional. Whether to fetch enabled or disabled accounts. Default is all. + * @param ids Optional. List of sub-account IDs. Up to 100. When provided, other filters are ignored. + * @param prefix Optional. Search by prefix of the sub-account name. Case-insensitive. + * @return the list of sub-accounts details. + * @throws Exception If the request fails + */ + public ApiResponse subAccounts(Boolean enabled, List ids, String prefix) throws Exception { + return subAccounts(enabled, ids, prefix, Collections.emptyMap()); + } + /** * Get a list of sub accounts. * @@ -125,7 +148,7 @@ public ApiResponse getSubAccount(String subAccountId, Map option * @return the list of sub-accounts details. * @throws Exception If the request fails */ - public ApiResponse getSubAccounts(Boolean enabled, List ids, String prefix, Map options) throws Exception { + public ApiResponse subAccounts(Boolean enabled, List ids, String prefix, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, "sub_accounts"); return callAccountApi(Api.HttpMethod.GET, uri, ObjectUtils.asMap("accountId", accountId, "enabled", enabled, "ids", ids, "prefix", prefix), options); @@ -134,7 +157,20 @@ public ApiResponse getSubAccounts(Boolean enabled, List ids, String pref /** * @param name Required. The name displayed in the management console. * @param cloudName Optional, unique (case insensitive) - * @param customAttributes Advanced custom attributes for the sub-account. + * @param customAttributes Custom attributes associated with the sub-account, as a map of key/value pairs. + * @param enabled Optional. Whether to create the account as enabled (default is enabled). + * @param baseAccount Optional. ID of sub-account from which to copy settings + * @return details of the created sub-account + * @throws Exception If the request fails + */ + public ApiResponse createSubAccount(String name, String cloudName, Map customAttributes, boolean enabled, String baseAccount) throws Exception { + return createSubAccount(name, cloudName, customAttributes, enabled, baseAccount, Collections.emptyMap()); + } + + /** + * @param name Required. The name displayed in the management console. + * @param cloudName Optional, unique (case insensitive) + * @param customAttributes Custom attributes associated with the sub-account, as a map of key/value pairs. * @param enabled Optional. Whether to create the account as enabled (default is enabled). * @param baseAccount Optional. ID of sub-account from which to copy settings * @param options Generic advanced options map, see online documentation. @@ -160,7 +196,20 @@ public ApiResponse createSubAccount(String name, String cloudName, Map customAtt * @param subAccountId The id of the sub-account to update * @param name The name displayed in the management console. * @param cloudName The cloud name to set. - * @param customAttributes Advanced custom attributes for the sub-account. + * @param customAttributes ACustom attributes associated with the sub-account, as a map of key/value pairs. + * @param enabled Set the sub-account as enabled or not. + * @return details of the updated sub-account + * @throws Exception If the request fails + */ + public ApiResponse updateSubAccount(String subAccountId, String name, String cloudName, Map customAttributes, Boolean enabled) throws Exception { + return updateSubAccount(subAccountId, name, cloudName, customAttributes, enabled, Collections.emptyMap()); + } + + /** + * @param subAccountId The id of the sub-account to update + * @param name The name displayed in the management console. + * @param cloudName The cloud name to set. + * @param customAttributes ACustom attributes associated with the sub-account, as a map of key/value pairs. * @param enabled Set the sub-account as enabled or not. * @param options Generic advanced options map, see online documentation. * @return details of the updated sub-account @@ -180,6 +229,17 @@ public ApiResponse updateSubAccount(String subAccountId, String name, String clo options); } + /** + * Deletes the sub-account. + * + * @param subAccountId The id of the sub-account to delete + * @return result message. + * @throws Exception If the request fails. + */ + public ApiResponse deleteSubAccount(String subAccountId) throws Exception { + return deleteSubAccount(subAccountId, Collections.emptyMap()); + } + /** * Deletes the sub-account. * @@ -194,6 +254,16 @@ public ApiResponse deleteSubAccount(String subAccountId, Map opt } // Users + /** + * Get details of a specific user. + * + * @param userId The id of the user to fetch + * @return details of the user. + * @throws Exception If the request fails. + */ + public ApiResponse user(String userId) throws Exception { + return user(userId,null); + } /** * Get details of a specific user. @@ -203,11 +273,25 @@ public ApiResponse deleteSubAccount(String subAccountId, Map opt * @return details of the user. * @throws Exception If the request fails. */ - public ApiResponse getUser(String userId, Map options) throws Exception { + public ApiResponse user(String userId, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS, userId); return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); } + /** + * Get a list of the users according to filters. + * + * @param pending Optional. Whether to fetch pending users. Default all. + * @param userIds Optionals. List of user IDs. Up to 100 + * @param prefix Optional. Search by prefix of the user's name or email. Case-insensitive + * @param subAccountId Optional. Return only users who have access to the given sub-account + * @return the users' details. + * @throws Exception If the request fails. + */ + public ApiResponse users(Boolean pending, List userIds, String prefix, String subAccountId) throws Exception { + return users(pending, userIds, prefix, subAccountId,null); + } + /** * Get a list of the users according to filters. * @@ -219,7 +303,7 @@ public ApiResponse getUser(String userId, Map options) throws Ex * @return the users' details. * @throws Exception If the request fails. */ - public ApiResponse getUsers(Boolean pending, List userIds, String prefix, String subAccountId, Map options) throws Exception { + public ApiResponse users(Boolean pending, List userIds, String prefix, String subAccountId, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS); return callAccountApi(Api.HttpMethod.GET, uri, ObjectUtils.asMap("accountId", accountId, @@ -229,6 +313,21 @@ public ApiResponse getUsers(Boolean pending, List userIds, String prefix "sub_account_id", subAccountId), options); } + /** + * Create a new user. + * + * @param name Required. Username. + * @param email Required. User's email. + * @param role Required. User's role. + * @param subAccountsIds Optional. Sub-accounts for which the user should have access. + * If not provided or empty, user should have access to all accounts. + * @return The newly created user details. + * @throws Exception If the request fails. + */ + public ApiResponse createUser(String name, String email, Role role, List subAccountsIds) throws Exception { + return createUser(name, email, role, subAccountsIds); + } + /** * Create a new user. * @@ -254,7 +353,23 @@ public ApiResponse createUser(String name, String email, Role role, List * @param email User's email. * @param role User's role. * @param subAccountsIds Sub-accounts for which the user should have access. - * * If not provided or empty, user should have access to all accounts. + * If not provided or empty, user should have access to all accounts. + * @return The updated user details + * @throws Exception If the request fails. + */ + public ApiResponse updateUser(String userId, String name, String email, Role role, List subAccountsIds) throws Exception { + return updateUser(userId, name, email, role, subAccountsIds,null); + } + + /** + * Update an existing user. + * + * @param userId The id of the user to update. + * @param name Username. + * @param email User's email. + * @param role User's role. + * @param subAccountsIds Sub-accounts for which the user should have access. + * If not provided or empty, user should have access to all accounts. * @param options Generic advanced options map, see online documentation. * @return The updated user details * @throws Exception If the request fails. @@ -264,6 +379,17 @@ public ApiResponse updateUser(String userId, String name, String email, Role rol return performUserAction(Api.HttpMethod.PUT, uri, email, name, role, subAccountsIds, options); } + /** + * Delete a user. + * + * @param userId Id of the user to delete. + * @return result message. + * @throws Exception + */ + public ApiResponse deleteUser(String userId) throws Exception { + return deleteUser(userId,null); + } + /** * Delete a user. * @@ -278,6 +404,15 @@ public ApiResponse deleteUser(String userId, Map options) throws } // Groups + /** + * Create a new user group + * @param name Required. Name for the group. + * @return The newly created group. + * @throws Exception If the request fails + */ + public ApiResponse createUserGroup(String name) throws Exception { + return createUserGroup(name,null); + } /** * Create a new user group @@ -291,6 +426,18 @@ public ApiResponse createUserGroup(String name, Map options) thr return callAccountApi(Api.HttpMethod.POST, uri, ObjectUtils.asMap("name", name), options); } + /** + * Update an existing user group + * + * @param groupId The id of the group to update + * @param name The name of the group. + * @return The updated group. + * @throws Exception If the request fails + */ + public ApiResponse updateUserGroup(String groupId, String name) throws Exception { + return updateUserGroup(groupId, name,null); + } + /** * Update an existing user group * @@ -305,6 +452,17 @@ public ApiResponse updateUserGroup(String groupId, String name, Map options) return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); } + /** + * Add an existing user to a group. + * @param groupId The group id. + * @param userId The user id to add. + * @throws Exception If the request fails + */ + public ApiResponse addUserToGroup(String groupId, String userId) throws Exception { + return addUserToGroup(groupId, userId,null); + } /** * Add an existing user to a group. * @param groupId The group id. @@ -330,6 +497,16 @@ public ApiResponse addUserToGroup(String groupId, String userId, MapemptyMap(), options); } + /** + * Removes a user from a group. + * @param groupId The group id. + * @param userId The id of the user to remove + * @return A result message + * @throws Exception If the request fails. + */ + public ApiResponse removeUserFromGroup(String groupId, String userId) throws Exception { + return removeUserFromGroup(groupId, userId,null); + } /** * Removes a user from a group. * @param groupId The group id. @@ -343,6 +520,16 @@ public ApiResponse removeUserFromGroup(String groupId, String userId, MapemptyMap(), options); } + /** + * Get details of a group. + * @param groupId The group id to fetch + * @return Details of the group. + * @throws Exception If the request fails. + */ + public ApiResponse userGroup(String groupId) throws Exception { + return userGroup(groupId,null); + } + /** * Get details of a group. * @param groupId The group id to fetch @@ -350,22 +537,40 @@ public ApiResponse removeUserFromGroup(String groupId, String userId, Map options) throws Exception { + public ApiResponse userGroup(String groupId, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId); return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); } + /** + * Gets a list of all the user groups. + * @return The list of the groups. + * @throws Exception If the request fails. + */ + public ApiResponse userGroups() throws Exception { + return userGroups(Collections.emptyMap()); + } + /** * Gets a list of all the user groups. * @param options Generic advanced options map, see online documentation. * @return The list of the groups. * @throws Exception If the request fails. */ - public ApiResponse listUserGroups(Map options) throws Exception { + public ApiResponse userGroups(Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS); return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); } + /** + * Lists the users belonging to this user group. + * @param groupId The id of the user group. + * @return The list of users in that group. + * @throws Exception If the request fails. + */ + public ApiResponse userGroupUsers(String groupId) throws Exception { + return userGroupUsers(groupId,null); + } /** * Lists the users belonging to this user group. * @param groupId The id of the user group. @@ -373,7 +578,7 @@ public ApiResponse listUserGroups(Map options) throws Exception * @return The list of users in that group. * @throws Exception If the request fails. */ - public ApiResponse listUserGroupUsers(String groupId, Map options) throws Exception { + public ApiResponse userGroupUsers(String groupId, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USER_GROUPS, groupId, USERS); return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); } @@ -403,7 +608,7 @@ private ApiResponse performUserAction(Api.HttpMethod method, List uri, S } private Map verifyOptions(Map options) { - if (options == null) { + if (options == null || options == Collections.EMPTY_MAP) { return new HashMap(2); // Two, since api key and secret will be populated later } 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 84256507..17289400 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 @@ -70,14 +70,14 @@ public void testPassingCredentialsThroughOptions() throws Exception { Map map = singletonMap("provisioning_api_secret", new Object()) ; try { - this.account.getSubAccounts(true, null, null, map); + this.account.subAccounts(true, null, null, map); } catch (IllegalArgumentException ignored){ exceptions++; } map = singletonMap("provisioning_api_key", new Object()) ; try { - this.account.getSubAccounts(true, null, null, map); + this.account.subAccounts(true, null, null, map); } catch (IllegalArgumentException ignored){ exceptions++; } @@ -87,7 +87,7 @@ public void testPassingCredentialsThroughOptions() throws Exception { map.put("provisioning_api_secret", "def"); try { - this.account.getSubAccounts(true, null, null, map); + this.account.subAccounts(true, null, null, map); } catch (Exception ex){ assertTrue(ex.getMessage().contains("Invalid credentials")); exceptions++; @@ -100,14 +100,14 @@ public void testPassingCredentialsThroughOptions() throws Exception { @Test public void testGetSubAccount() throws Exception { ApiResponse accountResponse = createSubAccount(); - ApiResponse account = this.account.getSubAccount(accountResponse.get("id").toString(), null); + ApiResponse account = this.account.subAccount(accountResponse.get("id").toString(), null); assertNotNull(account); } @Test public void testGetSubAccounts() throws Exception { createSubAccount(); - ApiResponse accounts = account.getSubAccounts(null, null, null, null); + ApiResponse accounts = account.subAccounts(null, null, null, null); assertNotNull(accounts); assertTrue(((ArrayList) accounts.get("sub_accounts")).size() >= 1); } @@ -151,14 +151,14 @@ public void testDeleteSubAccount() throws Exception { @Test public void testGetUser() throws Exception { ApiResponse user = createUser(); - ApiResponse result = account.getUser(user.get("id").toString(), null); + ApiResponse result = account.user(user.get("id").toString(), null); assertNotNull(result); } @Test public void testGetUsers() throws Exception { createUser(Account.Role.MASTER_ADMIN); - ApiResponse result = account.getUsers(null, null, null, null, null); + ApiResponse result = account.users(null, null, null, null, null); assertNotNull(result); assertTrue(((ArrayList) result.get("users")).size() >= 1); } @@ -236,7 +236,7 @@ public void testRemoveUserFromUserGroup() throws Exception { @Test public void testListUserGroups() throws Exception { createGroup(); - ApiResponse result = account.listUserGroups(null); + ApiResponse result = account.userGroups(); assertNotNull(result); assertTrue(((List) result.get("user_groups")).size() >= 1); } @@ -244,7 +244,7 @@ public void testListUserGroups() throws Exception { @Test public void testListUserGroup() throws Exception { ApiResponse group = createGroup(); - ApiResponse result = account.getUserGroup(group.get("id").toString(), null); + ApiResponse result = account.userGroup(group.get("id").toString(), null); assertNotNull(result); } @@ -258,7 +258,7 @@ public void testListUsersInGroup() throws Exception { String user2Id = user2.get("id").toString(); account.addUserToGroup(groupId, user1Id, null); account.addUserToGroup(groupId, user2Id, null); - ApiResponse result = account.listUserGroupUsers(groupId, null); + ApiResponse result = account.userGroupUsers(groupId, null); assertNotNull(result); assertTrue(((List) result.get("users")).size() >= 2); } @@ -267,7 +267,7 @@ public void testListUsersInGroup() throws Exception { // Helpers private ApiResponse createGroup() throws Exception { String name = randomLetters(); - ApiResponse userGroup = account.createUserGroup(name, null); + ApiResponse userGroup = account.createUserGroup(name); createdGroupIds.add(userGroup.get("id").toString()); return userGroup; @@ -293,7 +293,7 @@ private ApiResponse createUser(List subAccountsIds, Account.Role role) t } private ApiResponse createSubAccount() throws Exception { - ApiResponse subAccount = account.createSubAccount(randomLetters(), null, emptyMap(), true, null, null); + ApiResponse subAccount = account.createSubAccount(randomLetters(), null, emptyMap(), true, null); createdSubAccountIds.add(subAccount.get("id").toString()); return subAccount; } From 9d9d266920d1b726db8734529ebecdb78ebb5e4c Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 12 Sep 2019 11:35:49 +0300 Subject: [PATCH 408/592] Add support for `cinemagraph_analysis` param. (#182) --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../main/java/com/cloudinary/test/AbstractApiTest.java | 7 +++++++ .../java/com/cloudinary/test/AbstractUploaderTest.java | 9 +++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 0e89f1bf..0fe6c180 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -125,7 +125,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"), options); + "image_metadata", "pages", "phash", "max_results", "quality_analysis", "cinemagraph_analysis"), options); return response; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index a6d97df7..51d88c8d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -8,7 +8,7 @@ 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"}; + "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async", "quality_analysis", "cinemagraph_analysis"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map 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 0383505d..068768c6 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 @@ -951,4 +951,11 @@ public void testDeleteFolder() throws Exception { // should throw exception (folder not found): api.deleteFolder(cloudinary.randomPublicId(), emptyMap()); } + + + @Test + public void testCinemagraphAnalysisResource() throws Exception { + ApiResponse res = api.resource(API_TEST, Collections.singletonMap("cinemagraph_analysis", true)); + assertNotNull(res.get("cinemagraph_analysis")); + } } \ No newline at end of file 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 1208c694..64243894 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 @@ -680,6 +680,15 @@ public void testQualityAnalysis() throws IOException { } + @Test + public void testCinemagraphAnalysisUpload() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("cinemagraph_analysis", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + assertNotNull(result.get("cinemagraph_analysis")); + result = cloudinary.uploader().explicit(result.get("public_id").toString(), ObjectUtils.asMap("type", "upload", "resource_type", "image", "cinemagraph_analysis", true)); + assertNotNull(result.get("cinemagraph_analysis")); + + } + private void addToDeleteList(String type, String id) { Set ids = toDelete.get(type); if (ids == null) { From 48e64f7d563821d5c6a642cd86900862b989228b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 12 Sep 2019 13:02:53 +0300 Subject: [PATCH 409/592] Version 1.24.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dbdc96d..043fea28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ +1.24.0 / 2019-09-12 +=================== + + * Add support for `cinemagraph_analysis` parameter. (#182) + * Rename Account API methods, add convenience overloads. (#181) + 1.23.0 / 2019-08-15 =================== diff --git a/README.md b/README.md index 8bbcf71a..765dba0f 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.23.0 + 1.24.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.23.0/cloudinary-core-1.23.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.23.0/cloudinary-http44-1.23.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.24.0/cloudinary-core-1.24.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.24.0/cloudinary-http44-1.24.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 8b22f7fc..5826743e 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.23.0"; + public final static String VERSION = "1.24.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index afd267a6..6b7fa147 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.23.0 +version=1.24.0 From e94c23bb41f3b5a0e6b518d2eda9a828ebaaf609 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Mon, 25 Nov 2019 11:05:54 +0200 Subject: [PATCH 410/592] Ignore `URL` in AuthToken generation if `ACL` is provided (#184) --- .../src/test/java/com/cloudinary/AuthTokenTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 86ce6164..90b278ee 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -116,4 +116,14 @@ public void testUrlInTag() { assertThat(url, Matchers.matchesPattern("")); } -} \ No newline at end of file + + @Test + public void testIgnoreUrlIfAclIsProvided() { + String user = "foobar"; // username taken from elsewhere + AuthToken token = new AuthToken(KEY).duration(300).acl("/*/t_" + user).startTime(222222222); + String cookieToken = token.generate(); + AuthToken aclToken = new AuthToken(KEY).duration(300).acl("/*/t_" + user).startTime(222222222); + String cookieAclToken = aclToken.generate("http://res.cloudinary.com/test123/image/upload/v1486020273/sample.jpg"); + assertEquals(cookieToken, cookieAclToken); + } +} From 358bfbf3f47ee055a8a2ce381f7949fcc27bf3f2 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Thu, 28 Nov 2019 14:21:55 +0200 Subject: [PATCH 411/592] Add validation for `CLOUDINARY_URL` scheme (#185) --- .../java/com/cloudinary/Configuration.java | 5 ++++- .../com/cloudinary/test/CloudinaryTest.java | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index c9e68b2e..da5f5e26 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -186,6 +186,9 @@ public static Configuration from(String cloudinaryUrl) { static protected Map parseConfigUrl(String cloudinaryUrl) { Map params = new HashMap(); URI cloudinaryUri = URI.create(cloudinaryUrl); + if (cloudinaryUri.getScheme() == null || !cloudinaryUri.getScheme().equalsIgnoreCase("cloudinary")){ + throw new IllegalArgumentException("Invalid CLOUDINARY_URL scheme. Expecting to start with 'cloudinary://'"); + } params.put("cloud_name", cloudinaryUri.getHost()); if (cloudinaryUri.getUserInfo() != null) { String[] creds = cloudinaryUri.getUserInfo().split(":"); @@ -427,4 +430,4 @@ public Builder from(Configuration other) { return this; } } -} \ No newline at end of file +} 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 4f8f6339..dabb1f4d 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1230,6 +1230,24 @@ public void testConfiguration() throws IllegalAccessException { assertFieldsEqual(config, copy); } + @Test + public void testCloudinaryUrlValidScheme() { + String cloudinaryUrl = "cloudinary://123456789012345:ALKJdjklLJAjhkKJ45hBK92baj3@test"; + Configuration.from(cloudinaryUrl); + } + + @Test(expected = IllegalArgumentException.class) + public void testCloudinaryUrlInvalidScheme() { + String cloudinaryUrl = "https://123456789012345:ALKJdjklLJAjhkKJ45hBK92baj3@test"; + Configuration.from(cloudinaryUrl); + } + + @Test(expected = IllegalArgumentException.class) + public void testCloudinaryUrlEmptyScheme() { + String cloudinaryUrl = " "; + Configuration.from(cloudinaryUrl); + } + 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 659249e39847356fe8fb4dfe41ab8d1e8169fec9 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Mon, 2 Dec 2019 11:01:53 +0200 Subject: [PATCH 412/592] Support create folder API (#188) --- .../src/main/java/com/cloudinary/Api.java | 15 ++++++++--- .../com/cloudinary/test/AbstractApiTest.java | 26 +++++++++++++++---- 2 files changed, 32 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 0fe6c180..1627c997 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -244,7 +244,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", "live")); return callApi(HttpMethod.PUT, Arrays.asList("upload_presets", name), params, options); } @@ -252,7 +252,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", "live")); return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); } @@ -268,6 +268,13 @@ public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); } + //Creates an empty folder + public ApiResponse createFolder(String folderName, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + return callApi(HttpMethod.POST, Arrays.asList("folders", folderName), ObjectUtils.emptyMap(), options); + } + public ApiResponse restore(Iterable publicIds, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); @@ -583,7 +590,7 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O */ public ApiResponse addMetadataField(MetadataField field) throws Exception { return callApi(HttpMethod.POST, Collections.singletonList("metadata_fields"), - ObjectUtils.toMap(field), ObjectUtils.asMap ("content_type", "json")); + ObjectUtils.toMap(field), ObjectUtils.asMap("content_type", "json")); } /** @@ -639,7 +646,7 @@ public ApiResponse updateMetadataFieldDatasource(String fieldExternalId, List entriesExternalId) throws Exception { List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource"); - return callApi(HttpMethod.DELETE, uri,Collections.singletonMap("external_ids", entriesExternalId) , Collections.emptyMap()); + return callApi(HttpMethod.DELETE, uri, Collections.singletonMap("external_ids", entriesExternalId), Collections.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 068768c6..5ae9598c 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 @@ -46,6 +46,7 @@ abstract public class AbstractApiTest extends MockableTest { public static final Transformation DELETE_TRANSFORMATION = new Transformation().width(100).crop("scale").overlay(new TextLayer().text(SUFFIX + "_delete").fontFamily("Arial").fontSize(60)); public static final String TEST_KEY = "test-key" + SUFFIX; public static final String API_TEST_RESTORE = "api_test_restore" + SUFFIX; + public static final Set createdFolders = new HashSet(); protected Api api; @@ -119,6 +120,12 @@ public static void tearDownClass() { api.deleteUploadPreset(API_TEST_UPLOAD_PRESET_4, ObjectUtils.emptyMap()); } catch (Exception ignored) { } + try { + for (String folder : createdFolders) { + api.deleteFolder(folder, ObjectUtils.emptyMap()); + } + } catch (Exception ignored) { + } } @@ -484,7 +491,8 @@ public void testListTransformationByNamed() throws Exception { } finally { try { api.deleteTransformation(name, null); - } catch (Exception ignored){} + } catch (Exception ignored) { + } } } @@ -653,7 +661,7 @@ 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)); + context, "live", true)); String name = result.get("name").toString(); Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); assertEquals(preset.get("name"), name); @@ -693,7 +701,7 @@ 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)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true, "live", true)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); preset = api.uploadPreset(name, ObjectUtils.emptyMap()); @@ -758,6 +766,14 @@ public void testFolderApi() throws Exception { api.deleteResourcesByPrefix("test_folder", ObjectUtils.emptyMap()); } + @Test + public void testCreateFolder() throws Exception { + String apTestCreateFolder = "api_test_create_folder" + "_" + SUFFIX; + createdFolders.add(apTestCreateFolder); + Map result = api.createFolder("apTestCreateFolder", null); + assertTrue((Boolean) result.get("success")); + } + @Test public void testRestore() throws Exception { // should support restoring resources @@ -946,7 +962,7 @@ public void testDeleteFolder() throws Exception { Thread.sleep(5000); api.deleteResources(Collections.singletonList(uploadResult.get("public_id").toString()), emptyMap()); ApiResponse result = api.deleteFolder(toDelete, emptyMap()); - assertTrue(((ArrayList)result.get("deleted")).contains(toDelete)); + assertTrue(((ArrayList) result.get("deleted")).contains(toDelete)); // should throw exception (folder not found): api.deleteFolder(cloudinary.randomPublicId(), emptyMap()); @@ -958,4 +974,4 @@ public void testCinemagraphAnalysisResource() throws Exception { ApiResponse res = api.resource(API_TEST, Collections.singletonMap("cinemagraph_analysis", true)); assertNotNull(res.get("cinemagraph_analysis")); } -} \ No newline at end of file +} From a231072fa073a7c2e9559316afada0904f213a2e Mon Sep 17 00:00:00 2001 From: Aditi Madan <32554955+aditimadan-Cloudinary@users.noreply.github.com> Date: Sun, 15 Dec 2019 23:51:14 -0800 Subject: [PATCH 413/592] Allow generating archive with multiple resource types (#174) --- .../src/main/java/com/cloudinary/ArchiveParams.java | 13 +++++++++++++ .../src/main/java/com/cloudinary/Util.java | 1 + .../com/cloudinary/test/AbstractUploaderTest.java | 10 ++++++++++ .../main/java/com/cloudinary/test/MockableTest.java | 2 ++ 4 files changed, 26 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java index 3b998a88..8c094f36 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java +++ b/cloudinary-core/src/main/java/com/cloudinary/ArchiveParams.java @@ -26,6 +26,7 @@ public class ArchiveParams { private String[] targetTags = null; private String[] tags = null; private String[] publicIds = null; + private String[] fullyQualifiedPublicIds = null; private String[] prefixes = null; private Transformation[] transformations = null; private Long expiresAt = null; @@ -176,6 +177,15 @@ public ArchiveParams publicIds(String[] publicIds) { return this; } + public String[] fully_qualified_public_ids() { + return fullyQualifiedPublicIds; + } + + public ArchiveParams fullyQualifiedPublicIds(String[] fullyQualifiedPublicIds) { + this.fullyQualifiedPublicIds = fullyQualifiedPublicIds; + return this; + } + public String[] prefixes() { return prefixes; } @@ -225,6 +235,9 @@ public Map toMap() { params.put("tags", tags); if (publicIds != null) params.put("public_ids", publicIds); + if(fullyQualifiedPublicIds !=null){ + params.put("fully_qualified_public_ids", fullyQualifiedPublicIds); + } if (prefixes != null) params.put("prefixes", prefixes); if (transformations != null) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 51d88c8d..a3af8833 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -179,6 +179,7 @@ public static final Map buildArchiveParams(Map options, String t putArray("target_tags", options, params); putArray("tags", options, params); putArray("public_ids", options, params); + putArray("fully_qualified_public_ids", options, params); putArray("prefixes", options, params); putEager("transformations", options, params); putObject("timestamp", options, params, Util.timestamp()); 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 64243894..b111836b 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 @@ -31,6 +31,8 @@ abstract public class AbstractUploaderTest extends MockableTest { public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; private static Map> toDelete = new HashMap>(); + public static final String SRC_FULLY_QUALIFIED_IMAGE="image/upload/sample"; + public static final String SRC_FULLY_QUALIFIED_VIDEO="video/upload/dog"; @BeforeClass public static void setUpClass() throws IOException { @@ -40,6 +42,7 @@ public static void setUpClass() throws IOException { } cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_VIDEO, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "public_id", "dog", "resource_type", "video")); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "resource_type", "raw")); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, @@ -593,6 +596,13 @@ public void testCreateArchiveRaw() throws Exception { } + @Test + public void testCreateZipMultipleResourceTypes() throws Exception { + Map result = cloudinary.uploader().createZip(ObjectUtils.asMap("fully_qualified_public_ids",(new String[]{SRC_FULLY_QUALIFIED_IMAGE,SRC_FULLY_QUALIFIED_VIDEO}),"resource_type","auto")); + assertEquals(2, result.get("file_count")); + cloudinary.api().deleteResources(Arrays.asList(result.get("public_id").toString()), asMap("resource_type", "raw")); + } + @Test public void testDownloadArchive() throws Exception { String result = cloudinary.downloadArchive(new ArchiveParams().tags(new String[]{ARCHIVE_TAG}).targetTags(new String[]{UPLOADER_TAG})); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 13bc82ef..18043d3e 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -11,6 +11,8 @@ public class MockableTest { public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; + public static final String SRC_TEST_VIDEO = "http://res.cloudinary.com/demo/video/upload/dog.mp4"; + public static final String SRC_TEST_RAW = "../cloudinary-test-common/src/main/resources/docx.docx"; public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; protected static String SUFFIX = StringUtils.isNotBlank(System.getenv("TRAVIS_JOB_ID")) ? System.getenv("TRAVIS_JOB_ID") : String.valueOf(new Random().nextInt(99999)); protected static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; From 8b30a474b7ff985036c3a6aad3cf2934e864d824 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Mon, 16 Dec 2019 10:04:54 +0200 Subject: [PATCH 414/592] minor: Fixed typo in Javadoc (#189) --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- 1 file changed, 1 insertion(+), 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 1627c997..83ad486a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -585,7 +585,7 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O /** * Add a new metadata field definition * @param field The field to add. - * @return A map representing the newlay added field. + * @return A map representing the newly added field. * @throws Exception */ public ApiResponse addMetadataField(MetadataField field) throws Exception { From 4a4d6ca999775e665ef29102e30b9e851c46d02f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 16 Dec 2019 12:03:15 +0200 Subject: [PATCH 415/592] Automate publishing (#190) --- .travis.yml | 1 - build.gradle | 20 +--- cloudinary-core/build.gradle | 127 ++++++++++++++++------- cloudinary-http42/build.gradle | 121 ++++++++++++++------- cloudinary-http43/build.gradle | 121 ++++++++++++++------- cloudinary-http44/build.gradle | 121 ++++++++++++++------- cloudinary-taglib/build.gradle | 123 +++++++++++++++------- cloudinary-test-common/build.gradle | 123 +++++++++++++++------- gradle.properties | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- java_shared.gradle | 26 +++-- 11 files changed, 527 insertions(+), 259 deletions(-) diff --git a/.travis.yml b/.travis.yml index d0afbc87..8a83789d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ jdk: - oraclejdk8 - oraclejdk9 - oraclejdk11 - - openjdk7 - openjdk8 - openjdk10 diff --git a/build.gradle b/build.gradle index ef4017cd..ae4b253b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,9 @@ -buildscript { -} - allprojects { - apply plugin: 'maven' - apply plugin: 'signing' repositories { jcenter() mavenCentral() } - signing { - sign configurations.archives - } project.ext.set("publishGroupId", group) -} - -subprojects { - tasks.withType(Test) { - environment 'CLOUDINARY_URL', System.getProperty('CLOUDINARY_URL') - maxParallelForks = Runtime.runtime.availableProcessors() - - // show standard out and standard error of the test JVM(s) on the console - testLogging.showStandardStreams = true - } -} +} \ No newline at end of file diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index b3ad2026..fa305516 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -1,58 +1,111 @@ -apply from: "../java_shared.gradle" - -task ciTest( type: Test ) { +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" } +task ciTest( type: Test ) + dependencies { - 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' + testImplementation "org.hamcrest:java-hamcrest:2.0.0.0" + testImplementation group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' + testImplementation group: 'junit', name: 'junit', version: '4.12' } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } +apply from: "../java_shared.gradle" - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } +if (hasProperty("ossrhPassword")) { + signing { + sign configurations.archives + } - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-core' - name 'Cloudinary Core Library' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + 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 + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } } + + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } + } + } + + 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 diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index a6903dd6..6b1a0cba 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -1,3 +1,11 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + apply from: "../java_shared.gradle" task ciTest( type: Test ) { @@ -18,59 +26,96 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } +if (hasProperty("ossrhPassword")) { - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } + signing { + sign configurations.archives + } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-http42' - name 'Cloudinary Apache HTTP 4.2 Library' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + 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 + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } + } - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" } } } + + 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 diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 246cd070..53a0f238 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -1,3 +1,11 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + apply from: "../java_shared.gradle" task ciTest( type: Test ) { @@ -17,59 +25,96 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } +if (hasProperty("ossrhPassword")) { - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } + signing { + sign configurations.archives + } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-http43' - name 'Cloudinary Apache HTTP 4.3 Library' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + pom { + name = 'Cloudinary Apache HTTP 4.3 Library' + packaging = 'jar' + groupId = publishGroupId + artifactId = 'cloudinary-http43' + description = publishDescription + url = githubUrl + licenses { + license { + name = licenseName + url = licenseUrl + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } + } - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" } } } + + 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 diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index 470ed770..1312d2b9 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -1,3 +1,11 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + apply from: "../java_shared.gradle" task ciTest( type: Test ) { @@ -17,59 +25,96 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } +if (hasProperty("ossrhPassword")) { - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } - - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } + signing { + sign configurations.archives + } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-http44' - name 'Cloudinary Apache HTTP 4.4 Library' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + 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 + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } + } - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" } } } + + 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 diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 366ea0f7..9d2313be 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -1,5 +1,15 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + apply from: "../java_shared.gradle" +task ciTest( type: Test ) + dependencies { compile project(':cloudinary-core') compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.1' @@ -13,59 +23,96 @@ dependencies { } } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } +if (hasProperty("ossrhPassword")) { - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } + signing { + sign configurations.archives + } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-taglib' - name 'Cloudinary Taglib Library' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + 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 + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } + } - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" } } } + + 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 diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 17fe3d5d..c374de6d 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -1,5 +1,15 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + apply from: "../java_shared.gradle" +task ciTest( type: Test ) + dependencies { compile project(':cloudinary-core') compile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' @@ -7,59 +17,96 @@ dependencies { testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' } -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: publishRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } +if (hasProperty("ossrhPassword")) { - snapshotRepository(url: snapshotRepo) { - authentication(userName: project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "", password: project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "") - } + signing { + sign configurations.archives + } - pom.project { - groupId publishGroupId - artifactId 'cloudinary-test-common' - name 'Cloudinary Test Common' - description publishDescription - packaging jar - version version + nexusStaging { + packageGroup = group + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } - url githubUrl + 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 + } + } - scm { - connection scmConnection - developerConnection scmDeveloperConnection - url scmUrl + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } } - licenses { - license { - name licenseName - url licenseUrl + 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' } } - developers { - developer { - id developerId - name developerName - email developerEmail + // 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' } } } + } - pom.whenConfigured { pom -> - pom.dependencies.forEach { dep -> - if (dep.getVersion() == "unspecified") { - dep.setGroupId(publishGroupId) - dep.setVersion(version) - } + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" } } } + + 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 diff --git a/gradle.properties b/gradle.properties index 6b7fa147..9ae5bd27 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,3 +14,4 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary version=1.24.0 +gnsp.disableApplyOnlyOnRootProjectEnforcement=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb5158de..30b572c7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip diff --git a/java_shared.gradle b/java_shared.gradle index 6e570564..b9396ad2 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -1,10 +1,5 @@ -apply plugin: 'java' - sourceCompatibility = 1.6 targetCompatibility = 1.6 -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} javadoc { options.encoding = 'UTF-8' @@ -26,12 +21,21 @@ task javadocJar(type: Jar, dependsOn: javadoc) { } artifacts { - archives sourcesJar - archives javadocJar + archives javadocJar, sourcesJar +} + +tasks.withType(GenerateModuleMetadata) { + enabled = false } -tasks.each { task -> - if (!project.hasProperty("ossrhPassword") && "signArchives" == task.name) { - task.enabled = false - } +tasks.withType(Test) { + environment 'CLOUDINARY_URL', System.getProperty('CLOUDINARY_URL') + maxParallelForks = Runtime.runtime.availableProcessors() + + // show standard out and standard error of the test JVM(s) on the console + testLogging.showStandardStreams = true +} + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' } \ No newline at end of file From aa8f8ce9ad5566ad4d2c858d4806a302af15d3f3 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 17 Dec 2019 12:08:30 +0200 Subject: [PATCH 416/592] Fix issue with maven central and gradle signatures. (#191) See https://github.com/gradle/gradle/issues/11308 --- gradle.properties | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle.properties b/gradle.properties index 9ae5bd27..adaa0ea5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,9 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary version=1.24.0 + gnsp.disableApplyOnlyOnRootProjectEnforcement=true + +# see https://github.com/gradle/gradle/issues/11308 +systemProp.org.gradle.internal.publish.checksums.insecure=true + From e4cf92ff56a46d58955abd105358bfffa8cde0ee Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 14 Jan 2020 14:34:06 +0200 Subject: [PATCH 417/592] Improve support for modifying `set` type metadata fields. (#194) --- .../src/main/java/com/cloudinary/Util.java | 47 +++++++++++++++---- .../test/AbstractStructuredMetadataTest.java | 15 ++++++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index a3af8833..67e83195 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -119,12 +119,19 @@ protected static String encodeAccessControl(Object accessControl) { } protected static String encodeContext(Object context) { - if (context != null && context instanceof Map) { - Map mapArg = (Map) context; + if (context instanceof Map) { + Map mapArg = (Map) context; HashSet out = new HashSet(); - for (Map.Entry entry : mapArg.entrySet()) { - final String value = entry.getValue().replaceAll("([=\\|])","\\\\$1"); - out.add(entry.getKey() + "=" + value); + for (Map.Entry entry : mapArg.entrySet()) { + final String value; + if (entry.getValue() instanceof List) { + value = encodeList(((List) entry.getValue()).toArray()); + } else if (entry.getValue() instanceof String[]) { + value = encodeList((String[]) entry.getValue()); + } else { + value = entry.getValue().toString(); + } + out.add(entry.getKey() + "=" + encodeSingleContextString(value)); } return StringUtils.join(out.toArray(), "|"); } else if (context == null) { @@ -134,6 +141,26 @@ protected static String encodeContext(Object context) { } } + private static String encodeList(Object[] list) { + StringBuilder builder = new StringBuilder("["); + + boolean first = true; + for (Object s : list) { + if (!first) { + builder.append(","); + } + + builder.append("\"").append(encodeSingleContextString(s.toString())).append("\""); + first = false; + } + + return builder.append("]").toString(); + } + + private static String encodeSingleContextString(String value) { + return value.replaceAll("([=\\|])", "\\\\$1"); + } + @SuppressWarnings("unchecked") protected static final String buildCustomHeaders(Object headers) { if (headers == null) { @@ -165,7 +192,7 @@ public static void clearEmpty(Map params) { @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildArchiveParams(Map options, String targetFormat) { Map params = new HashMap(); - if (options != null && options.size() > 0){ + if (options != null && options.size() > 0) { params.put("type", options.get("type")); params.put("mode", options.get("mode")); params.put("target_format", targetFormat); @@ -198,7 +225,7 @@ private static void putEager(String name, Map from, Map to) { private static void putBoolean(String name, Map from, Map to) { final Object value = from.get(name); - if(value != null){ + if (value != null) { to.put(name, ObjectUtils.asBoolean(value)); } } @@ -209,16 +236,16 @@ private static void putObject(String name, Map from, Map to) { private static void putObject(String name, Map from, Map to, Object defaultValue) { final Object value = from.get(name); - if (value != null){ + if (value != null) { to.put(name, value); - } else if(defaultValue != null){ + } else if (defaultValue != null) { to.put(name, defaultValue); } } private static void putArray(String name, Map from, Map to) { final Object value = from.get(name); - if (value != null){ + if (value != null) { to.put(name, ObjectUtils.asArray(value)); } } 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 d370ce5c..39c3b6ce 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 @@ -216,6 +216,21 @@ public void testUploaderUpdateMetadata() throws Exception { assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); } + @Test + public void testSetField() throws Exception { + SetMetadataField field = createSetField("test123"); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + Map result = cloudinary.uploader().updateMetadata(asMap(fieldId, new String[]{"id2", "id3"}), new String[]{"sample"}, null); + assertNotNull(result); + assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + List list = new ArrayList(2); + list.add("id1"); + list.add("id2"); + result = cloudinary.uploader().updateMetadata(asMap(fieldId, list), new String[]{"sample"}, null); + assertNotNull(result); + assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + } // Metadata test helpers private SetMetadataField createSetField(String labelPrefix) { SetMetadataField setField = new SetMetadataField(); From dc3c5f1ab0bfdcbf3143a53d9317b770a5deb584 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Sun, 19 Jan 2020 12:13:48 +0200 Subject: [PATCH 418/592] Encode URLs in API calls (#186) --- .../strategies/AbstractApiStrategy.java | 16 +++++++++++++++- .../java/com/cloudinary/http42/ApiStrategy.java | 6 +----- .../java/com/cloudinary/http43/ApiStrategy.java | 5 +---- .../java/com/cloudinary/http44/ApiStrategy.java | 7 ++----- .../com/cloudinary/test/AbstractApiTest.java | 8 ++++++++ 5 files changed, 27 insertions(+), 15 deletions(-) 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 c5b68906..65d0970d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -2,10 +2,13 @@ import com.cloudinary.Api; import com.cloudinary.Api.HttpMethod; +import com.cloudinary.SmartUrlEncoder; import com.cloudinary.api.ApiResponse; - +import com.cloudinary.utils.StringUtils; +import java.util.Arrays; import java.util.Map; + public abstract class AbstractApiStrategy { protected Api api; @@ -13,6 +16,17 @@ 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), "/"); + 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; 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 a0a9e0bf..7c19aeb9 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -43,11 +43,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map uri, Map Date: Thu, 6 Feb 2020 09:39:15 +0200 Subject: [PATCH 419/592] Fix/provisioning api params (#195) --- .travis.yml | 2 +- .../com/cloudinary/provisioning/Account.java | 4 +++- .../cloudinary/test/AbstractAccountApiTest.java | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a83789d..a1e91f8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,5 +27,5 @@ branches: - staging-test # ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_ACCOUNT_URL=CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i +script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_ACCOUNT_URL=$CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i 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 76b56fd0..8c0f1eca 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -2,6 +2,7 @@ import com.cloudinary.Api; import com.cloudinary.Cloudinary; +import com.cloudinary.Util; import com.cloudinary.api.ApiResponse; import com.cloudinary.utils.ObjectUtils; @@ -73,6 +74,7 @@ private ApiResponse callAccountApi(Api.HttpMethod method, List uri, Map< options.put("provisioning_api_secret", secret); } + Util.clearEmpty(params); return api.getStrategy().callAccountApi(method, uri, params, options); } @@ -308,7 +310,7 @@ public ApiResponse users(Boolean pending, List userIds, String prefix, S return callAccountApi(Api.HttpMethod.GET, uri, ObjectUtils.asMap("accountId", accountId, "pending", pending, - "user_ids", userIds, + "ids", userIds, "prefix", prefix, "sub_account_id", subAccountId), options); } 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 17289400..549f4543 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 @@ -157,10 +157,20 @@ public void testGetUser() throws Exception { @Test public void testGetUsers() throws Exception { - createUser(Account.Role.MASTER_ADMIN); - ApiResponse result = account.users(null, null, null, null, null); + String id1 = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); + String id2 = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); + ApiResponse result = account.users(null, Arrays.asList(id1, id2), null, null, null); assertNotNull(result); - assertTrue(((ArrayList) result.get("users")).size() >= 1); + final ArrayList users = (ArrayList) result.get("users"); + ArrayList returnedIds = new ArrayList(2); + + assertEquals("Should return two users", 2, users.size()); + + returnedIds.add(((Map) users.get(0)).get("id").toString()); + returnedIds.add(((Map) users.get(1)).get("id").toString()); + + assertTrue("User1 id should be in the result set", returnedIds.contains(id1)); + assertTrue("User2 id should be in the result set", returnedIds.contains(id2)); } @Test From 7dee39e1763de0b7d33a5cbbb7ad1516853d2aef Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 6 Feb 2020 11:39:24 +0200 Subject: [PATCH 420/592] Version 1.25.0 --- CHANGELOG.md | 16 ++++++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 043fea28..ae6da799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ +1.25.0 / 2020-02-06 +=================== + +New functionality +----------------- + * Allow generating archive with multiple resource types (#174) + * Add validation for `CLOUDINARY_URL` scheme (#185) + * Support create folder API (#188) + +Other changes +------------- + * Fix/provisioning api params (#195) + * Encode URLs in API calls (#186) + * Improve support for modifying `set` type metadata fields. (#194) + * Ignore `URL` in AuthToken generation if `ACL` is provided (#184) + 1.24.0 / 2019-09-12 =================== diff --git a/README.md b/README.md index 765dba0f..3669338f 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.24.0 + 1.25.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.24.0/cloudinary-core-1.24.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.24.0/cloudinary-http44-1.24.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.25.0/cloudinary-core-1.25.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.25.0/cloudinary-http44-1.25.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 5826743e..1c8aa7c1 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.24.0"; + public final static String VERSION = "1.25.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index adaa0ea5..079550e3 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.24.0 +version=1.25.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 50099c330aa54a8771ad53be10210230b916a735 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Mon, 17 Feb 2020 09:39:49 +0200 Subject: [PATCH 421/592] Add signature checking methods (#193) --- .../main/java/com/cloudinary/Cloudinary.java | 73 +++++++++++-------- .../src/main/java/com/cloudinary/Url.java | 4 +- .../src/main/java/com/cloudinary/Util.java | 53 ++++++++++++++ .../signing/ApiResponseSignatureVerifier.java | 46 ++++++++++++ .../NotificationRequestSignatureVerifier.java | 63 ++++++++++++++++ .../api/signing/SignedPayloadValidator.java | 39 ++++++++++ .../cloudinary/api/signing/package-info.java | 7 ++ .../com/cloudinary/utils/StringUtils.java | 10 +++ .../ApiResponseSignatureVerifierTest.java | 26 +++++++ ...ificationRequestSignatureVerifierTest.java | 71 ++++++++++++++++++ 10 files changed, 359 insertions(+), 33 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1c8aa7c1..cb86e768 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -1,5 +1,7 @@ package com.cloudinary; +import com.cloudinary.api.signing.ApiResponseSignatureVerifier; +import com.cloudinary.api.signing.NotificationRequestSignatureVerifier; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.StrategyLoader; @@ -8,8 +10,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.*; @@ -127,27 +127,46 @@ public String signedPreloadedImage(Map result) { } public String apiSignRequest(Map paramsToSign, String apiSecret) { - 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()); - } - } - } - String to_sign = StringUtils.join(params, "&"); - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); - return StringUtils.encodeHexString(digest); + return Util.produceSignature(paramsToSign, apiSecret); + } + + /** + * Verifies that Cloudinary notification request is genuine by checking its signature. + * + * Cloudinary can asynchronously process your e.g. image uploads requests. This is achieved by calling back API you + * specified during preparing of upload request as soon as it has been processed. See Upload Notifications in + * Cloudinary documentation for more details. In order to make sure it is Cloudinary calling your API back, hashed + * message authentication codes (HMAC's) based on SHA-1 hashing function and configured Cloudinary API secret key + * are used for signing the requests. + * + * The following method serves as a convenient utility to perform the verification procedure. + * + * @param body Cloudinary Notification request body represented as string + * @param timestamp Cloudinary Notification request custom X-Cld-Timestamp HTTP header value + * @param signature Cloudinary Notification request custom X-Cld-Signature HTTP header value, i.e. the HMAC + * @param validFor desired period of request validity since issued, in seconds, for protection against replay attacks + * @return whether request signature is valid or not + */ + public boolean verifyNotificationSignature(String body, String timestamp, String signature, long validFor) { + return new NotificationRequestSignatureVerifier(config.apiSecret).verifySignature(body, timestamp, signature, validFor); + } + + /** + * Verifies that Cloudinary API response is genuine by checking its signature. + * + * Cloudinary can add a signature value in the response to API methods returning public id's and versions. In order + * to make sure it is genuine Cloudinary response, hashed message authentication codes (HMAC's) based on SHA-1 hashing + * function and configured Cloudinary API secret key are used for signing the responses. + * + * The following method serves as a convenient utility to perform the verification procedure. + * + * @param publicId publicId response field value + * @param version version response field value + * @param signature signature response field value, i.e. the HMAC + * @return whether response signature is valid or not + */ + public boolean verifyApiResponseSignature(String publicId, String version, String signature) { + return new ApiResponseSignatureVerifier(config.apiSecret).verifySignature(publicId, version, signature); } public void signRequest(Map params, Map options) { @@ -236,14 +255,6 @@ private String buildUrl(String base, Map params) throws Unsuppor return urlBuilder.toString(); } - byte[] getUTF8Bytes(String string) { - try { - return string.getBytes("UTF-8"); - } catch (java.io.UnsupportedEncodingException e) { - throw new RuntimeException("Unexpected exception", e); - } - } - @Deprecated public static Map asMap(Object... values) { return ObjectUtils.asMap(values); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 17a95d1c..24bfdce1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -393,7 +393,7 @@ public String generate(String source) { toSign = StringUtils.removeStartingChars(toSign, '/'); toSign = StringUtils.mergeSlashesInUrl(toSign); - byte[] digest = md.digest(cloudinary.getUTF8Bytes(toSign + this.config.apiSecret)); + byte[] digest = md.digest(Util.getUTF8Bytes(toSign + this.config.apiSecret)); signature = Base64Coder.encodeURLSafeString(digest); signature = "s--" + signature.substring(0, 8) + "--"; } @@ -536,7 +536,7 @@ public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean private String shard(String input) { CRC32 crc32 = new CRC32(); - crc32.update(cloudinary.getUTF8Bytes(input)); + crc32.update(Util.getUTF8Bytes(input)); return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 67e83195..fb436fc7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -4,6 +4,8 @@ import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONObject; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; public class Util { @@ -253,4 +255,55 @@ private static void putArray(String name, Map from, Map to) { protected static String timestamp() { return Long.toString(System.currentTimeMillis() / 1000L); } + + /** + * Encodes passed string value into a sequence of bytes using the UTF-8 charset. + * + * @param string string value to encode + * @return byte array representing passed string value + */ + public static byte[] getUTF8Bytes(String string) { + try { + return string.getBytes("UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("Unexpected exception", e); + } + } + + /** + * Calculates signature, or hashed message authentication code (HMAC) of provided parameters name-value pairs and + * secret value using SHA-1 hashing algorithm. + *

        + * Argument for hashing function is built by joining sorted parameter name-value pairs into single string in the + * same fashion as HTTP GET method uses, and concatenating the result with secret value in the end. Method supports + * arrays/collections as parameter values. In this case, the elements of array/collection are joined into single + * comma-delimited string prior to inclusion into the result. + * + * @param paramsToSign parameter name-value pairs list represented as instance of {@link Map} + * @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) { + 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()); + } + } + } + String to_sign = StringUtils.join(params, "&"); + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); + return StringUtils.encodeHexString(digest); + } } 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 new file mode 100644 index 00000000..33224204 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java @@ -0,0 +1,46 @@ +package com.cloudinary.api.signing; + +import com.cloudinary.Util; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + +import static com.cloudinary.utils.StringUtils.emptyIfNull; + +/** + * The {@code ApiResponseSignatureVerifier} class is responsible for verifying Cloudinary Upload API response signatures. + */ +public class ApiResponseSignatureVerifier { + private final String secretKey; + + /** + * Initializes new instance of {@code ApiResponseSignatureVerifier} class with a secret key required to perform + * API response signatures verification. + * + * @param secretKey shared secret key string which is used to sign and verify authenticity of API responses + */ + public ApiResponseSignatureVerifier(String secretKey) { + if (StringUtils.isBlank(secretKey)) { + throw new IllegalArgumentException("Secret key is required"); + } + + this.secretKey = secretKey; + } + + /** + * Checks whether particular Cloudinary Upload API response signature matches expected signature. + * + * The task is performed by generating signature using same hashing algorithm as used on Cloudinary servers and + * comparing the result with provided actual signature. + * + * @param publicId public id of uploaded resource as stated in upload API response + * @param version version of uploaded resource as stated in upload API response + * @param signature signature of upload API response, usually passed via X-Cld-Signature custom HTTP response header + * + * @return true if response signature passed verification procedure + */ + public boolean verifySignature(String publicId, String version, String signature) { + return Util.produceSignature(ObjectUtils.asMap( + "public_id", emptyIfNull(publicId), + "version", emptyIfNull(version)), secretKey).equals(signature); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java new file mode 100644 index 00000000..497e491e --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java @@ -0,0 +1,63 @@ +package com.cloudinary.api.signing; + +import static com.cloudinary.utils.StringUtils.emptyIfNull; + +/** + * The {@code NotificationRequestSignatureVerifier} class is responsible for verifying authenticity and integrity + * of Cloudinary Upload notifications. + */ +public class NotificationRequestSignatureVerifier { + private final SignedPayloadValidator signedPayloadValidator; + + /** + * Initializes new instance of {@code NotificationRequestSignatureVerifier} with secret key value. + * + * @param secretKey shared secret key string which is used to sign and verify authenticity of notifications + */ + public NotificationRequestSignatureVerifier(String secretKey) { + this.signedPayloadValidator = new SignedPayloadValidator(secretKey); + } + + /** + * Verifies signature of Cloudinary Upload notification. + * + * @param body notification message body, represented as string + * @param timestamp value of X-Cld-Timestamp custom HTTP header of notification message, representing notification + * issue timestamp + * @param signature actual signature value, usually passed via X-Cld-Signature custom HTTP header of notification + * message + * @return true if notification passed verification procedure + */ + public boolean verifySignature(String body, String timestamp, String signature) { + return signedPayloadValidator.validateSignedPayload( + emptyIfNull(body) + emptyIfNull(timestamp), + signature); + } + + /** + * Verifies signature of Cloudinary Upload notification. + *

        + * Differs from {@link #verifySignature(String, String, String)} in additional validation which consists of making + * sure the notification being verified is still not expired based on timestamp parameter value. + * + * @param body notification message body, represented as string + * @param timestamp value of X-Cld-Timestamp custom HTTP header of notification message, representing notification + * issue timestamp + * @param signature actual signature value, usually passed via X-Cld-Signature custom HTTP header of notification + * message + * @param secondsValidFor the amount of time, in seconds, the notification message is considered valid by client + * @return true if notification passed verification procedure + */ + public boolean verifySignature(String body, String timestamp, String signature, long secondsValidFor) { + long parsedTimestamp; + try { + parsedTimestamp = Long.parseLong(timestamp); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Provided timestamp is not a valid number", e); + } + + return verifySignature(body, timestamp, signature) && + (System.currentTimeMillis() - parsedTimestamp <= secondsValidFor * 1000L); + } + +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java new file mode 100644 index 00000000..40339db6 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java @@ -0,0 +1,39 @@ +package com.cloudinary.api.signing; + +import com.cloudinary.Util; +import com.cloudinary.utils.StringUtils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static com.cloudinary.utils.StringUtils.emptyIfNull; + +class SignedPayloadValidator { + private final String secretKey; + private final MessageDigest messageDigest; + + SignedPayloadValidator(String secretKey) { + if (StringUtils.isBlank(secretKey)) { + throw new IllegalArgumentException("Secret key is required"); + } + + this.secretKey = secretKey; + this.messageDigest = acquireMessageDigest(); + } + + boolean validateSignedPayload(String signedPayload, String signature) { + String expectedSignature = + StringUtils.encodeHexString( + messageDigest.digest(Util.getUTF8Bytes(emptyIfNull(signedPayload) + secretKey))); + + return expectedSignature.equals(signature); + } + + private static MessageDigest acquireMessageDigest() { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Unexpected exception", e); + } + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java new file mode 100644 index 00000000..a95f5e74 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java @@ -0,0 +1,7 @@ +/** + * The package holds classes used internally to implement verification procedures of authenticity and integrity of + * client communication with Cloudinary servers. Verification is in most cases based on calculating and comparing so called + * signatures, or hashed message authentication codes (HMAC) - string values calculated based on message payload, some + * secret key value shared between communicating parties and SHA-1 hashing function. + */ +package com.cloudinary.api.signing; \ No newline at end of file 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 e3bec84b..f12a33cb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -398,4 +398,14 @@ public static String mergeSlashesInUrl(String url) { return builder.toString(); } + + /** + * Returns empty string value when passed string value is null or empty, the passed string itself otherwise. + * + * @param str string value to evaluate + * @return passed string value or empty string, if the passed string is null or empty + */ + public static String emptyIfNull(String str) { + return isEmpty(str) ? "" : str; + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java b/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java new file mode 100644 index 00000000..a198b8fc --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java @@ -0,0 +1,26 @@ +package com.cloudinary.api.signing; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ApiResponseSignatureVerifierTest { + @Test + public void testVerifySignature() { + ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU"); + + boolean actual = verifier.verifySignature("tests/logo.png", "1", "08d3107a5b2ad82e7d82c0b972218fbf20b5b1e0"); + + assertTrue(actual); + } + + @Test + public void testVerifySignatureFail() { + ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU"); + + boolean actual = verifier.verifySignature("tests/logo.png", "1", "doesNotMatchForSure"); + + assertFalse(actual); + } +} diff --git a/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java b/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java new file mode 100644 index 00000000..c38bf47a --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java @@ -0,0 +1,71 @@ +package com.cloudinary.api.signing; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class NotificationRequestSignatureVerifierTest { + @Test + public void testVerifySignature() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "f9aa4471d2a88ff244424cca2444edf7d7ac3596"); + + assertTrue(actual); + } + + @Test + public void testVerifySignatureFailWhenSignatureDoesntMatch() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "notMatchingForSure"); + + assertFalse(actual); + } + + @Test + public void testVerifySignatureFailWhenTooOld() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "f9aa4471d2a88ff244424cca2444edf7d7ac3596", + 1000L); + + assertFalse(actual); + } + + @Test + public void testVerifySignaturePassWhenStillValid() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "f9aa4471d2a88ff244424cca2444edf7d7ac3596", + Long.MAX_VALUE / 1000L); + + assertTrue(actual); + } + + @Test + public void testVerifySignatureFailWhenStillValidButSignatureDoesntMatch() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "notMatchingForSure", + Long.MAX_VALUE / 1000L); + + assertFalse(actual); + } +} From 84b9d5a661595a90604cf7cfb9e82c120d666f1b Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Sun, 23 Feb 2020 17:45:33 +0200 Subject: [PATCH 422/592] Add support for pow operator in expressions (#198) --- .../transformation/BaseExpression.java | 26 ++++++++++++++++++- .../com/cloudinary/TransformationTest.java | 8 ++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index 993be1e7..cd5f4433 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -26,7 +26,8 @@ public abstract class BaseExpression { "*", "mul", "/", "div", "+", "add", - "-", "sub" + "-", "sub", + "^", "pow" ); public static final Map PREDEFINED_VARS = ObjectUtils.asMap( "width", "w", @@ -245,6 +246,29 @@ public T sub() { return (T) this; } + /** + * Utility shortcut method which invokes on this Expression instance {@link #pow()} method, takes its result and + * invokes {@link #value(Object)} method on it. Effectively, invocation of this shortcut results in + * "to the power of value" sub-expression added to the end of current expression instance. + * + * @param value argument for {@link #value(Object)} call + * @return result of {@link #value(Object)} call + */ + public T pow(Object value) { + return (T) pow().value(value); + } + + /** + * Adds "to the power of" sub-expression to the end of the list of already present sub-expressions in this + * expression instance. + * + * @return this expression instance + */ + public T pow() { + expressions.add("pow"); + return (T) this; + } + public T value(Object value) { expressions.add(String.valueOf(value)); return (T) this; diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 9e41deef..18356791 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -210,4 +210,12 @@ public void testSupportStringInterpolation() { ).fontFamily("Arial").fontSize(18)); assertEquals("c_scale,l_text:Arial_18:$(start)Hello%20$(name)$(ext)%252C%20%24%28no%20%29%20%24%28%20no%29$(end)", t.generate()); } + + @Test + public void testShouldSupportPowOperator() { + Transformation t = new Transformation() + .variables(variable("$small", 150), variable("$big", "$small ^ 1.5")); + + assertEquals("$small_150,$big_$small_pow_1.5", t.generate()); + } } \ No newline at end of file From 349ec6dbb1733102f8227bd186a0dd45d36b3676 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Sun, 15 Mar 2020 20:41:12 +0200 Subject: [PATCH 423/592] Add handling of "max_results"/"next_cursor" parameters (#203) --- .../src/main/java/com/cloudinary/Api.java | 20 ++++- .../com/cloudinary/test/FoldersApiTest.java | 4 + .../com/cloudinary/test/FoldersApiTest.java | 4 + .../com/cloudinary/test/FoldersApiTest.java | 4 + .../test/AbstractFoldersApiTest.java | 88 +++++++++++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java create mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java create mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 83ad486a..c85192df 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -259,13 +259,17 @@ public ApiResponse createUploadPreset(Map options) throws Exception { public ApiResponse rootFolders(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders"), ObjectUtils.emptyMap(), options); + return callApi(HttpMethod.GET, Arrays.asList("folders"), + extractParams(options, Arrays.asList("max_results", "next_cursor")), + options); } public ApiResponse subFolders(String ofFolderPath, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), ObjectUtils.emptyMap(), options); + return callApi(HttpMethod.GET, Arrays.asList("folders", ofFolderPath), + extractParams(options, Arrays.asList("max_results", "next_cursor")), + options); } //Creates an empty folder @@ -659,4 +663,16 @@ public ApiResponse deleteMetadataField(String fieldExternalId) throws Exception List uri = Arrays.asList("metadata_fields", fieldExternalId); return callApi(HttpMethod.DELETE, uri, Collections.emptyMap(), Collections.emptyMap()); } + + private Map extractParams(Map options, List keys) { + Map result = new HashMap(); + for (String key : keys) { + Object option = options.get(key); + + if (option != null) { + result.put(key, option); + } + } + return result; + } } diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java new file mode 100644 index 00000000..971bcf39 --- /dev/null +++ b/cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class FoldersApiTest extends AbstractFoldersApiTest { +} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java new file mode 100644 index 00000000..971bcf39 --- /dev/null +++ b/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class FoldersApiTest extends AbstractFoldersApiTest { +} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java new file mode 100644 index 00000000..971bcf39 --- /dev/null +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java @@ -0,0 +1,4 @@ +package com.cloudinary.test; + +public class FoldersApiTest extends AbstractFoldersApiTest { +} 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 new file mode 100644 index 00000000..4ccf2903 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java @@ -0,0 +1,88 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.util.List; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeNotNull; + +@SuppressWarnings({"rawtypes"}) +abstract public class AbstractFoldersApiTest extends MockableTest { + protected Api api; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary(); + assumeNotNull(cloudinary.config.apiSecret); + this.api = cloudinary.api(); + } + + @Test + public void testRootFolderWithParams() throws Exception { + String rootFolder1Name = "rootFolderWithParamsTest1" + SUFFIX; + assertTrue((Boolean) api.createFolder(rootFolder1Name, null).get("success")); + + String rootFolder2Name = "rootFolderWithParamsTest2" + SUFFIX; + assertTrue((Boolean) api.createFolder(rootFolder2Name, null).get("success")); + + Thread.sleep(500); + + ApiResponse rootResponse1 = api.rootFolders(ObjectUtils.asMap("max_results", 1)); + List rootFolders1 = (List) rootResponse1.get("folders"); + assertNotNull(rootFolders1); + assertEquals(1, rootFolders1.size()); + + String nextCursor = (String) rootResponse1.get("next_cursor"); + assertNotNull(nextCursor); + + ApiResponse rootResponse2 = api.rootFolders(ObjectUtils.asMap("max_results", 1, "next_cursor", nextCursor)); + List folders2 = (List) rootResponse2.get("folders"); + assertNotNull(folders2); + assertEquals(1, folders2.size()); + + assertTrue(((List) api.deleteFolder(rootFolder1Name, null).get("deleted")).contains(rootFolder1Name)); + assertTrue(((List) api.deleteFolder(rootFolder2Name, null).get("deleted")).contains(rootFolder2Name)); + } + + @Test + public void testSubFolderWithParams() throws Exception { + String rootFolderName = "subfolderWithParamsTest" + SUFFIX; + assertTrue((Boolean) api.createFolder(rootFolderName, null).get("success")); + + String subFolder1Name = rootFolderName + "/subfolder1" + SUFFIX; + assertTrue((Boolean) api.createFolder(subFolder1Name, null).get("success")); + + String subFolder2Name = rootFolderName + "/subfolder2" + SUFFIX; + assertTrue((Boolean) api.createFolder(subFolder2Name, null).get("success")); + + Thread.sleep(500); + + ApiResponse response = api.subFolders(rootFolderName, ObjectUtils.asMap("max_results", 1)); + List folders = (List) response.get("folders"); + assertNotNull(folders); + assertEquals(1, folders.size()); + + String nextCursor = (String) response.get("next_cursor"); + assertNotNull(nextCursor); + + ApiResponse response2 = api.subFolders(rootFolderName, ObjectUtils.asMap("max_results", 1, "next_cursor", nextCursor)); + List folders2 = (List) response2.get("folders"); + assertNotNull(folders2); + assertEquals(1, folders2.size()); + + ApiResponse result = api.deleteFolder(rootFolderName, null); + assertTrue(((List) result.get("deleted")).contains(rootFolderName)); + } +} From 8b47123a93b1081fedfd4743ead1c8c75d108074 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 16 Mar 2020 11:58:55 +0200 Subject: [PATCH 424/592] Add support for 32 char SHA-256 URL signatures. --- .../main/java/com/cloudinary/Configuration.java | 17 +++++++++++++++-- .../src/main/java/com/cloudinary/Url.java | 13 ++++++++++--- .../com/cloudinary/test/CloudinaryTest.java | 9 +++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index da5f5e26..0400276f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -19,6 +19,7 @@ public class Configuration { public final static String SHARED_CDN = AKAMAI_SHARED_CDN; public final static String VERSION = "1.0.2"; public final static String USER_AGENT = "cld-android-" + VERSION; + public static final boolean DEFAULT_IS_LONG_SIGNATURE = false; public String cloudName; public String apiKey; @@ -41,11 +42,12 @@ public class Configuration { public boolean clientHints = false; public AuthToken authToken; public boolean forceVersion = true; + public boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; public Configuration() { } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies, boolean forceVersion) { + private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies, boolean forceVersion, boolean longUrlSignature) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -64,6 +66,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.timeout = 0; this.loadStrategies = loadStrategies; this.forceVersion = forceVersion; + this.longUrlSignature = longUrlSignature; } @SuppressWarnings("rawtypes") @@ -100,6 +103,7 @@ public void update(Map config) { if (properties != null) { this.properties.putAll(properties); } + this.longUrlSignature = ObjectUtils.asBoolean(config.get("long_url_signature"), DEFAULT_IS_LONG_SIGNATURE); } @SuppressWarnings("rawtypes") @@ -128,6 +132,7 @@ public Map asMap() { } map.put("force_version", forceVersion); map.put("properties", new HashMap(properties)); + map.put("long_url_signature", longUrlSignature); return map; } @@ -156,6 +161,7 @@ public Configuration(Configuration other) { this.forceVersion = other.forceVersion; this.loadStrategies = other.loadStrategies; this.properties.putAll(other.properties); + this.longUrlSignature = other.longUrlSignature; } /** @@ -265,6 +271,7 @@ public static class Builder { private boolean clientHints = false; private AuthToken authToken; private boolean forceVersion = true; + private boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; /** * Set the HTTP connection timeout. @@ -281,7 +288,7 @@ public Builder setTimeout(int timeout) { * Creates a {@link Configuration} with the arguments supplied to this builder */ public Configuration build() { - final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies, forceVersion); + final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies, forceVersion, longUrlSignature); configuration.clientHints = clientHints; return configuration; } @@ -400,6 +407,11 @@ public Builder setForceVersion(boolean forceVersion) { return this; } + public Builder setIsLongUrlSignature(boolean isLong) { + this.longUrlSignature = isLong; + return this; + } + /** * Initialize builder from existing {@link Configuration} * @@ -427,6 +439,7 @@ public Builder from(Configuration other) { this.clientHints = other.clientHints; this.authToken = other.authToken == null ? null : other.authToken.copy(); this.forceVersion = other.forceVersion; + this.longUrlSignature = other.longUrlSignature; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 24bfdce1..3d9733c9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -22,6 +22,7 @@ public class Url { private final Cloudinary cloudinary; private final Configuration config; + private boolean longUrlSignature; String publicId = null; String type = null; String resourceType = null; @@ -47,6 +48,7 @@ public class Url { public Url(Cloudinary cloudinary) { this.cloudinary = cloudinary; this.config = new Configuration(cloudinary.config); + this.longUrlSignature = config.longUrlSignature; this.authToken = config.authToken; } @@ -73,7 +75,7 @@ public Url clone() { cloned.sourceTypes = this.sourceTypes; cloned.urlSuffix = this.urlSuffix; cloned.useRootPath = this.useRootPath; - + cloned.longUrlSignature = this.longUrlSignature; return cloned; } @@ -240,6 +242,11 @@ public Url authToken(AuthToken authToken) { return this; } + public Url longUrlSignature(boolean isLong) { + this.longUrlSignature = isLong; + return this; + } + public Url sourceTransformation(Map sourceTransformation) { this.sourceTransformation = sourceTransformation; return this; @@ -384,7 +391,7 @@ public String generate(String source) { if (signUrl && (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN))) { MessageDigest md = null; try { - md = MessageDigest.getInstance("SHA-1"); + md = MessageDigest.getInstance(longUrlSignature ? "SHA-256" : "SHA-1"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unexpected exception", e); } @@ -395,7 +402,7 @@ public String generate(String source) { byte[] digest = md.digest(Util.getUTF8Bytes(toSign + this.config.apiSecret)); signature = Base64Coder.encodeURLSafeString(digest); - signature = "s--" + signature.substring(0, 8) + "--"; + signature = "s--" + signature.substring(0, longUrlSignature ? 32 : 8) + "--"; } String resourceType = this.resourceType; 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 dabb1f4d..99ba9af6 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -251,6 +251,15 @@ public void testNotSignTheUrlSuffix() { assertEquals("http://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); + + url = cloudinary.url().signed(true).longUrlSignature(true).generate("sample.jpg"); + assertEquals("http://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"); From bd0ff6f9a20168afddf2e8abaf78d310e507ba4e Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 19 Mar 2020 10:54:00 +0200 Subject: [PATCH 425/592] Improve test reliability. --- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 7 ++++--- .../java/com/cloudinary/test/AbstractFoldersApiTest.java | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) 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 bc7be26b..3c14be5e 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,10 +181,11 @@ public void test03ResourcesCursor() throws Exception { @Test public void test04ResourcesByType() throws Exception { // should allow listing resources by type - Map resource = preloadResource(ObjectUtils.asMap("tags", UPLOAD_TAGS)); - Map result = api.resources(ObjectUtils.asMap("type", "upload", "max_results", 500)); + Map result = api.resources(ObjectUtils.asMap("type", "upload", "max_results", 10)); List resources = (List) result.get("resources"); - assertThat(resources, hasItem(hasEntry("public_id", (String) resource.get("public_id")))); + + // beforeClass hook uploads several type:upload resources, we can rely on it. + assertTrue(resources.size() > 0); } @Test 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 4ccf2903..a0478bad 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 @@ -37,7 +37,7 @@ public void testRootFolderWithParams() throws Exception { String rootFolder2Name = "rootFolderWithParamsTest2" + SUFFIX; assertTrue((Boolean) api.createFolder(rootFolder2Name, null).get("success")); - Thread.sleep(500); + Thread.sleep(2000); ApiResponse rootResponse1 = api.rootFolders(ObjectUtils.asMap("max_results", 1)); List rootFolders1 = (List) rootResponse1.get("folders"); @@ -67,7 +67,7 @@ public void testSubFolderWithParams() throws Exception { String subFolder2Name = rootFolderName + "/subfolder2" + SUFFIX; assertTrue((Boolean) api.createFolder(subFolder2Name, null).get("success")); - Thread.sleep(500); + Thread.sleep(2000); ApiResponse response = api.subFolders(rootFolderName, ObjectUtils.asMap("max_results", 1)); List folders = (List) response.get("folders"); From 827ea3d8999548c0eeab1ec8ca44a879a7d60835 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Thu, 26 Mar 2020 10:35:28 +0300 Subject: [PATCH 426/592] Fix `normalize_expression` when a keyword is used in a variable name (#205) --- .../com/cloudinary/transformation/BaseExpression.java | 2 +- .../test/java/com/cloudinary/TransformationTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index cd5f4433..43415e4d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -107,7 +107,7 @@ private static Pattern getPattern() { sb.append(Pattern.quote(op)).append("|"); } sb.deleteCharAt(sb.length() - 1); - sb.append(")(?=[ _])|").append(StringUtils.join(PREDEFINED_VARS.keySet(), "|")).append(")"); + sb.append(")(?=[ _])|(? Date: Thu, 16 Apr 2020 12:11:08 +0300 Subject: [PATCH 427/592] Add support for restoring deleted datasource entries (#207) --- .../src/main/java/com/cloudinary/Api.java | 12 ++++++++++++ .../test/AbstractStructuredMetadataTest.java | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index c85192df..252416e7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -653,6 +653,18 @@ public ApiResponse deleteDatasourceEntries(String fieldExternalId, List return callApi(HttpMethod.DELETE, uri, Collections.singletonMap("external_ids", entriesExternalId), Collections.emptyMap()); } + /** + * Restore deleted data source entries for a given field + * @param fieldExternalId The id of the field to operate + * @param entriesExternalId The ids of all the entries to restore from the data source + * @return The datasource entries state after restore + * @throws Exception + */ + public ApiResponse restoreDatasourceEntries(String fieldExternalId, List entriesExternalId) throws Exception { + List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource_restore"); + return callApi(HttpMethod.POST, uri, Collections.singletonMap("external_ids", entriesExternalId), Collections.singletonMap("content_type", "json")); + } + /** * Delete a field definition. * @param fieldExternalId The id of the field to delete 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 39c3b6ce..1dac0970 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 @@ -158,6 +158,16 @@ public void testDeleteDatasourceEntries() throws Exception { assertNotNull(result); } + @Test + public void testRestoreDatasourceEntries() throws Exception { + SetMetadataField setField = createSetField("testRestoreDatasourceEntries"); + ApiResponse fieldResult = addFieldToAccount(setField); + String fieldExternalId = fieldResult.get("external_id").toString(); + api.deleteDatasourceEntries(fieldExternalId, Collections.singletonList("id1")); + ApiResponse result = api.restoreDatasourceEntries(fieldExternalId, Collections.singletonList("id1")); + assertNotNull(result); + } + @Test public void testUploadWithMetadata() throws Exception { StringMetadataField field = newFieldInstance("testUploadWithMetadata"); From 1c47a3351d55a8b10221e0ed232d618f1e684da6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 19 Apr 2020 15:18:16 +0300 Subject: [PATCH 428/592] Add issue templates (#208) --- .github/ISSUE_TEMPLATE/bug_report.md | 48 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 21 ++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..2fe58725 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,48 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +## Bug report for Cloudinary Java SDK +Before proceeding, please update to latest version and test if the issue persists + +## Describe the bug in a sentence or two. +… + +## Issue Type (Can be multiple) +[ ] Build - Can’t install or import the SDK +[ ] Performance - Performance issues +[ ] Behaviour - Functions aren’t working as expected (Such as generate URL) +[ ] Documentation - Inconsistency between the docs and behaviour +[ ] Other (Specify) + +## Steps to reproduce +… if applicable + +## Error screenshots or Stack Trace (if applicable) +… + +## Build System +[ ] Maven +[ ] Gradle +[ ] Other (Specify) + +## OS (Please specify version) +[ ] Windows +[ ] Linux +[ ] Mac +[ ] Other (specify) + +## Versions and Libraries (fill in the version numbers) +Cloudinary Java SDK version - 0.0.0 +JVM (dev environment) - 0.0.0 +JVM (production environment) - 0.0.0 +Maven - 0.0.0 / N/A +Gradle - 0.0.0 / N/A + +## Repository +If possible, please provide a link to a reproducible repository that showcases the problem diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..9ac6e086 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +## Feature request for Cloudinary Java SDK +…(If your feature is for other SDKs, please request them there) + + +## Explain your use case +… (A high level explanation of why you need this feature) + +## Describe the problem you’re trying to solve +… (A more technical view of what you’d like to accomplish, and how this feature will help you achieve it) + +## Do you have a proposed solution? +… (yes, no? Please elaborate if needed) From 739e386544119d01046ce7355695abdc0bfa571c Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 28 Apr 2020 12:34:49 +0300 Subject: [PATCH 429/592] Add variable support to `Transformation.opacity()` (#209) --- .../src/main/java/com/cloudinary/Transformation.java | 2 +- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index bc9a1042..7ff5d93c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -202,7 +202,7 @@ public T delay(Object value) { return param("delay", value); } - public T opacity(int value) { + public T opacity(Object value) { return param("opacity", value); } 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 99ba9af6..ea52194a 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -527,6 +527,10 @@ public void testOpacity() { Transformation transformation = new Transformation().opacity(50); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "o_50/test", result); + + transformation = new Transformation().opacity("$var"); + result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "o_$var/test", result); } @SuppressWarnings("unchecked") From fbdaf1cb42b5bb61e2931992b6e25a798bc0b935 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 5 May 2020 13:23:42 +0300 Subject: [PATCH 430/592] Version 1.26.0 --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae6da799..3bf063d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ +1.26.0 / 2020-05-05 +=================== + +New functionality +----------------- + + * Add variable support to `Transformation.opacity()` (#209) + * Add support for restoring deleted datasource entries (#207) + * Add support for 32 char SHA-256 URL signatures. + * Add support for `pow` operator in expressions (#198) + * Add signature checking methods (#193) + +Other changes +------------- + + * Fix handling of `max_results` and `next_cursor` parameters for folders api (#203) + * Fix `normalize_expression` when a keyword is used in a variable name (#205) + 1.25.0 / 2020-02-06 =================== diff --git a/README.md b/README.md index 3669338f..58079075 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.25.0 + 1.26.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.25.0/cloudinary-core-1.25.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.25.0/cloudinary-http44-1.25.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.26.0/cloudinary-core-1.26.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.26.0/cloudinary-http44-1.26.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index cb86e768..3d8c2e0d 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.25.0"; + public final static String VERSION = "1.26.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 079550e3..bf43c39f 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.25.0 +version=1.26.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From af1d7408c0dc8db5fa1a7c9a11b05b809a30595b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 12 May 2020 13:57:34 +0300 Subject: [PATCH 431/592] Add support for `date` param in `Api.usage()` (#210) --- .../src/main/java/com/cloudinary/Api.java | 38 +++++++++++++++---- .../com/cloudinary/utils/ObjectUtils.java | 4 ++ .../com/cloudinary/test/AbstractApiTest.java | 14 ++++++- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 252416e7..266f7962 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -55,9 +55,24 @@ public ApiResponse ping(Map options) throws Exception { public ApiResponse usage(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("usage"), ObjectUtils.emptyMap(), options); + + final List uri = new ArrayList(); + uri.add("usage"); + + Object date = options.get("date"); + + if (date != null) { + if (date instanceof Date) { + date = ObjectUtils.toUsageApiDateFormat((Date) date); + } + + uri.add(date.toString()); + } + + return callApi(HttpMethod.GET, uri, ObjectUtils.emptyMap(), options); } + public ApiResponse resourceTypes(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); return callApi(HttpMethod.GET, Arrays.asList("resources"), ObjectUtils.emptyMap(), options); @@ -543,7 +558,8 @@ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, /** * Delete a folder (must be empty). - * @param folder The full path of the folder to delete + * + * @param folder The full path of the folder to delete * @param options additional options. * @return The operation result. * @throws Exception When the folder isn't empty or doesn't exist. @@ -588,6 +604,7 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O /** * Add a new metadata field definition + * * @param field The field to add. * @return A map representing the newly added field. * @throws Exception @@ -599,6 +616,7 @@ public ApiResponse addMetadataField(MetadataField field) throws Exception { /** * List all the metadata field definitions (structure, not values) + * * @return A map containing the list of field definitions maps. * @throws Exception */ @@ -608,6 +626,7 @@ public ApiResponse listMetadataFields() throws Exception { /** * Get a metadata field definition by id + * * @param fieldExternalId The id of the field to retrieve * @return The fields definitions. * @throws Exception @@ -618,8 +637,9 @@ public ApiResponse metadataFieldByFieldId(String fieldExternalId) throws Excepti /** * Update the definitions of a single metadata field. + * * @param fieldExternalId The id of the field to update - * @param field The field definition + * @param field The field definition * @return The updated fields definition. * @throws Exception */ @@ -630,9 +650,10 @@ public ApiResponse updateMetadataField(String fieldExternalId, MetadataField fie /** * Update the datasource entries for a given field + * * @param fieldExternalId The id of the field to update - * @param entries A list of datasource entries. Existing entries (according to entry id) will be updated, - * new entries will be added. + * @param entries A list of datasource entries. Existing entries (according to entry id) will be updated, + * new entries will be added. * @return The updated field definition. * @throws Exception */ @@ -643,7 +664,8 @@ public ApiResponse updateMetadataFieldDatasource(String fieldExternalId, List /** * Restore deleted data source entries for a given field - * @param fieldExternalId The id of the field to operate + * + * @param fieldExternalId The id of the field to operate * @param entriesExternalId The ids of all the entries to restore from the data source * @return The datasource entries state after restore * @throws Exception @@ -667,6 +690,7 @@ public ApiResponse restoreDatasourceEntries(String fieldExternalId, List /** * Delete a field definition. + * * @param fieldExternalId The id of the field to delete * @return A map with a "message" key. "ok" value indicates a successful deletion. * @throws Exception 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 75bbc87c..437c04db 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -215,6 +215,10 @@ public static Long asLong(Object value, Long defaultValue) { } } + public static String toUsageApiDateFormat(Date date){ + return new SimpleDateFormat("dd-MM-yyy").format(date); + } + public static String toISO8601DateOnly(Date date) { return new SimpleDateFormat("yyyy-MM-dd").format(date); } 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 3c14be5e..7e05d517 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 @@ -512,8 +512,20 @@ public void test20ResourcesContext() throws Exception { @Test public void test18Usage() throws Exception { // should support usage API call - Map result = api.usage(ObjectUtils.emptyMap()); + final Date yesterday = yesterday(); + + Map result = api.usage(ObjectUtils.asMap("date", yesterday)); + assertNotNull(result.get("last_updated")); + + result = api.usage(ObjectUtils.asMap("date", ObjectUtils.toUsageApiDateFormat(yesterday))); assertNotNull(result.get("last_updated")); + + result = api.usage(ObjectUtils.emptyMap()); + assertNotNull(result.get("last_updated")); + } + + private Date yesterday() { + return new Date(new Date().getTime() - 24 * 60 * 60 * 1000); } @Test From 7a4562436f0ab3eb5c63c1898716e2877f48bf44 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Sun, 7 Jun 2020 13:43:04 +0300 Subject: [PATCH 432/592] Detect data URLs with suffix in mime type (#213) --- .../src/main/java/com/cloudinary/utils/StringUtils.java | 2 +- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 f12a33cb..73ddf45d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -209,7 +209,7 @@ public static String read(InputStream in) throws IOException { } public static boolean isRemoteUrl(String file) { - return file.matches("ftp:.*|https?:.*|s3:.*|gs:.*|data:([\\w-]+/[\\w-]+)?(;[\\w-]+=[\\w-]+)*;base64,([a-zA-Z0-9/+\n=]+)"); + return file.matches("ftp:.*|https?:.*|s3:.*|gs:.*|data:([\\w-]+/[\\w-]+(\\+[\\w-]+)?)?(;[\\w-]+=[\\w-]+)*;base64,([a-zA-Z0-9/+\n=]+)"); } /** 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 b111836b..6d25cfc2 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 @@ -137,7 +137,8 @@ public void testIsRemoteUrl() { "gs://cloudinary/images/old_logo.png", "data:image/gif;charset=utf8;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", "data:image/gif;param1=value1;param2=value2;base64," + - "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}; + "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", + "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg"}; for (String url : urls) { assertTrue(isRemoteUrl(url)); From 75c9e74497b8fc866c79b9c9f5dc14acf7552adc Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 Jun 2020 09:15:12 +0300 Subject: [PATCH 433/592] Use automatically generated test accounts in tests. (#214) --- .travis.yml | 5 +-- build.gradle | 35 +++++++++++++++++++ .../test/AbstractStructuredMetadataTest.java | 16 +++++---- .../cloudinary/test/AbstractUploaderTest.java | 8 +++-- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1e91f8e..72169c8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ branches: except: - staging-test -# ciTest is configured to skip the various timeout tests that don't work in travis -script: ./gradlew clean -DCLOUDINARY_URL=$CLOUDINARY_URL -DCLOUDINARY_ACCOUNT_URL=$CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i +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 -DCLOUDINARY_ACCOUNT_URL=$CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i diff --git a/build.gradle b/build.gradle index ae4b253b..4a3b9f77 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import groovy.json.JsonSlurper + allprojects { repositories { @@ -6,4 +8,37 @@ allprojects { } project.ext.set("publishGroupId", group) +} + +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 succesfully!") + } } \ 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 1dac0970..1256545b 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 @@ -17,7 +17,7 @@ public abstract class AbstractStructuredMetadataTest extends MockableTest { private static final String METADATA_UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; - + private static final String PUBLIC_ID = "before_class_public_id" + SUFFIX; protected Api api; public static final List metadataFieldExternalIds = new ArrayList(); @@ -27,6 +27,8 @@ public static void setUpClass() throws IOException { if (cloudinary.config.apiSecret == null) { System.err.println("Please setup environment for Upload test to run"); } + + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("public_id", PUBLIC_ID)); } @AfterClass @@ -221,9 +223,9 @@ public void testUploaderUpdateMetadata() throws Exception { StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata"); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); - Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{"sample"}, null); + Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{PUBLIC_ID}, null); assertNotNull(result); - assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + assertEquals(PUBLIC_ID, ((List) result.get("public_ids")).get(0).toString()); } @Test @@ -231,15 +233,15 @@ public void testSetField() throws Exception { SetMetadataField field = createSetField("test123"); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); - Map result = cloudinary.uploader().updateMetadata(asMap(fieldId, new String[]{"id2", "id3"}), new String[]{"sample"}, null); + Map result = cloudinary.uploader().updateMetadata(asMap(fieldId, new String[]{"id2", "id3"}), new String[]{PUBLIC_ID}, null); assertNotNull(result); - assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + assertEquals(PUBLIC_ID, ((List) result.get("public_ids")).get(0).toString()); List list = new ArrayList(2); list.add("id1"); list.add("id2"); - result = cloudinary.uploader().updateMetadata(asMap(fieldId, list), new String[]{"sample"}, null); + result = cloudinary.uploader().updateMetadata(asMap(fieldId, list), new String[]{PUBLIC_ID}, null); assertNotNull(result); - assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); + assertEquals(PUBLIC_ID, ((List) result.get("public_ids")).get(0).toString()); } // Metadata test helpers private SetMetadataField createSetField(String labelPrefix) { 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 6d25cfc2..c31c12f4 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 @@ -31,7 +31,8 @@ abstract public class AbstractUploaderTest extends MockableTest { public static final int SRC_TEST_IMAGE_W = 241; public static final int SRC_TEST_IMAGE_H = 51; private static Map> toDelete = new HashMap>(); - public static final String SRC_FULLY_QUALIFIED_IMAGE="image/upload/sample"; + private static final String UPLOADER_TEST_PUBLIC_ID = "uploader_test"; + public static final String SRC_FULLY_QUALIFIED_IMAGE="image/upload/" + UPLOADER_TEST_PUBLIC_ID; public static final String SRC_FULLY_QUALIFIED_VIDEO="video/upload/dog"; @BeforeClass @@ -42,6 +43,7 @@ public static void setUpClass() throws IOException { } cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG})); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG}, "public_id", UPLOADER_TEST_PUBLIC_ID, "transformation", "f_jpg")); cloudinary.uploader().upload(SRC_TEST_VIDEO, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "public_id", "dog", "resource_type", "video")); cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{SDK_TEST_TAG, UPLOADER_TAG, ARCHIVE_TAG}, "resource_type", "raw")); cloudinary.uploader().upload(SRC_TEST_IMAGE, @@ -225,8 +227,8 @@ public void testUniqueFilename() throws Exception { @Test public void testExplicit() throws IOException { - Map result = cloudinary.uploader().explicit("sample", asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload", "moderation", "manual")); - String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg").version(result.get("version")).generate("sample"); + Map result = cloudinary.uploader().explicit(UPLOADER_TEST_PUBLIC_ID, asMap("eager", Collections.singletonList(new Transformation().crop("scale").width(2.0)), "type", "upload", "moderation", "manual")); + String url = cloudinary.url().transformation(new Transformation().crop("scale").width(2.0)).format("jpg").version(result.get("version")).generate(UPLOADER_TEST_PUBLIC_ID); String eagerUrl = (String) ((Map) ((List) result.get("eager")).get(0)).get("url"); String cloudName = cloudinary.config.cloudName; assertEquals(eagerUrl.substring(eagerUrl.indexOf(cloudName)), url.substring(url.indexOf(cloudName))); From 4b98fd0851436f0bf1ae20067d4b1240980db271 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 Jun 2020 11:33:24 +0300 Subject: [PATCH 434/592] Support new parameter and modes in `generateSprite()` and `multi()` API cals. * Add support for `urls` parameter. * Add `downloadGeneratedSprite()` method. * Add `downloadMulti` method. --- .../main/java/com/cloudinary/Cloudinary.java | 61 ++++++++++++++++ .../main/java/com/cloudinary/Uploader.java | 72 +++++++++---------- .../src/main/java/com/cloudinary/Util.java | 51 +++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 51 +++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 34 ++++++--- 5 files changed, 223 insertions(+), 46 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3d8c2e0d..85748a95 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -8,11 +8,14 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.SecureRandom; import java.util.*; +import static com.cloudinary.Util.buildMultiParams; + @SuppressWarnings({"rawtypes", "unchecked"}) public class Cloudinary { @@ -224,6 +227,62 @@ public String downloadZip(Map options) throws UnsupportedEncodin return downloadArchive(options, "zip"); } + public String downloadGeneratedSprite(String tag, Map options) throws IOException { + if (StringUtils.isEmpty(tag)) throw new IllegalArgumentException("Tag cannot be empty"); + + if (options == null) + options = new HashMap(); + + options.put("tag", tag); + options.put("mode", ArchiveParams.MODE_DOWNLOAD); + + Map params = Util.buildGenerateSpriteParams(options); + signRequest(params, options); + + return buildUrl(cloudinaryApiUrl("sprite", options), params); + } + + public String downloadGeneratedSprite(String[] urls, Map options) throws IOException { + if (urls.length < 1) throw new IllegalArgumentException("Request must contain at least one URL."); + if (options == null) + options = new HashMap(); + + options.put("urls", urls); + options.put("mode", ArchiveParams.MODE_DOWNLOAD); + + Map params = Util.buildGenerateSpriteParams(options); + signRequest(params, options); + + return buildUrl(cloudinaryApiUrl("sprite", options), params); + } + + public String downloadMulti(String tag, Map options) throws IOException { + if (StringUtils.isEmpty(tag)) throw new IllegalArgumentException("Tag cannot be empty"); + if (options == null) + options = new HashMap(); + + options.put("tag", tag); + options.put("mode", ArchiveParams.MODE_DOWNLOAD); + + Map params = buildMultiParams(options); + signRequest(params, options); + + return buildUrl(cloudinaryApiUrl("multi", options), params); + } + + public String downloadMulti(String[] urls, Map options) throws IOException { + if (urls.length < 1) throw new IllegalArgumentException("Request must contain at least one URL."); + if (options == null) + options = new HashMap(); + + options.put("urls", urls); + options.put("mode", ArchiveParams.MODE_DOWNLOAD); + + Map params = buildMultiParams(options); + signRequest(params, options); + + return buildUrl(cloudinaryApiUrl("multi", options), params); + } private String buildUrl(String base, Map params) throws UnsupportedEncodingException { StringBuilder urlBuilder = new StringBuilder(); @@ -233,6 +292,8 @@ private String buildUrl(String base, Map params) throws Unsuppor } boolean first = true; for (Map.Entry param : params.entrySet()) { + if (param.getValue() == null) continue; + String keyValue = null; Object value = param.getValue(); if (!first) urlBuilder.append("&"); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index a93cfa0d..c4ee5d59 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -6,10 +6,10 @@ import org.cloudinary.json.JSONObject; import java.io.*; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import java.util.*; + +import static com.cloudinary.Util.buildGenerateSpriteParams; +import static com.cloudinary.Util.buildMultiParams; @SuppressWarnings({"rawtypes", "unchecked"}) public class Uploader { @@ -266,44 +266,44 @@ public Map generate_sprite(String tag, Map options) throws IOException { public Map generateSprite(String tag, Map options) throws IOException { if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - Object transParam = options.get("transformation"); - Transformation transformation = null; - if (transParam instanceof Transformation) { - transformation = new Transformation((Transformation) transParam); - } else if (transParam instanceof String) { - transformation = new Transformation().rawTransformation((String) transParam); + options = Collections.singletonMap("tag", tag); + else + options.put("tag", tag); + + return callApi("sprite", buildGenerateSpriteParams(options), options, null); + } + + public Map generateSprite(String[] urls, Map options) throws IOException { + if (options == null) + options = Collections.singletonMap("urls", urls); + else + options.put("urls", urls); + + return callApi("sprite", buildGenerateSpriteParams(options), options, null); + } + + public Map multi(String[] urls, Map options) throws IOException { + if (options == null) { + options = Collections.singletonMap("urls", urls); } else { - transformation = new Transformation(); - } - String format = (String) options.get("format"); - if (format != null) { - transformation.fetchFormat(format); + options.put("urls", urls); } - params.put("transformation", transformation.generate()); - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); - return callApi("sprite", params, options, null); + + return multi(options); } public Map multi(String tag, Map options) throws IOException { - if (options == null) - options = ObjectUtils.emptyMap(); - Map params = new HashMap(); - Object transformation = options.get("transformation"); - if (transformation != null) { - if (transformation instanceof Transformation) { - transformation = ((Transformation) transformation).generate(); - } - params.put("transformation", transformation.toString()); + if (options == null) { + options = Collections.singletonMap("tag", tag); + } else { + options.put("tag", tag); } - params.put("tag", tag); - params.put("notification_url", (String) options.get("notification_url")); - params.put("format", (String) options.get("format")); - params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); - return callApi("multi", params, options, null); + + return multi(options); + } + + private Map multi(Map options) throws IOException { + return callApi("multi", buildMultiParams(options), options, null); } public Map explode(String public_id, Map options) throws IOException { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index fb436fc7..dead1129 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -69,6 +69,57 @@ public static final Map buildUploadParams(Map options) { return params; } + public static Map buildMultiParams(Map options) { + Map params = new HashMap(); + + Object transformation = options.get("transformation"); + if (transformation != null) { + if (transformation instanceof Transformation) { + transformation = ((Transformation) transformation).generate(); + } + params.put("transformation", transformation.toString()); + } + params.put("tag", options.get("tag")); + if (options.containsKey("urls")) { + params.put("urls", Arrays.asList((String[]) options.get("urls"))); + } + params.put("notification_url", (String) options.get("notification_url")); + params.put("format", (String) options.get("format")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + params.put("mode", options.get("mode")); + putObject("timestamp", options, params, Util.timestamp()); + + return params; + } + + public static Map buildGenerateSpriteParams(Map options) { + HashMap params = new HashMap(); + Object transParam = options.get("transformation"); + Transformation transformation = null; + if (transParam instanceof Transformation) { + transformation = new Transformation((Transformation) transParam); + } else if (transParam instanceof String) { + transformation = new Transformation().rawTransformation((String) transParam); + } else { + transformation = new Transformation(); + } + String format = (String) options.get("format"); + if (format != null) { + transformation.fetchFormat(format); + } + params.put("transformation", transformation.generate()); + params.put("tag", options.get("tag")); + if (options.containsKey("urls")) { + params.put("urls", Arrays.asList((String[]) options.get("urls"))); + } + params.put("notification_url", (String) options.get("notification_url")); + params.put("async", ObjectUtils.asBoolean(options.get("async"), false).toString()); + params.put("mode", options.get("mode")); + putObject("timestamp", options, params, Util.timestamp()); + + return params; + } + protected static final String buildEager(List transformations) { if (transformations == null) { return null; 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 ea52194a..3f03fef8 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -20,6 +20,7 @@ import java.lang.reflect.Type; import java.net.URI; import java.net.URLDecoder; +import java.net.URLEncoder; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -651,6 +652,56 @@ public void testZipDownload() throws Exception { assertEquals("/v1_1/test123/image/download_tag.zip", uri.getPath()); } + @Test + public void testDownloadSprite() throws Exception{ + final String spriteTestTag = "sprite_tag"; + final String url1 = "https://res.cloudinary.com/demo/image/upload/sample"; + final String url2 = "https://res.cloudinary.com/demo/image/upload/car"; + + String urlFromTag = cloudinary.downloadGeneratedSprite(spriteTestTag, null); + String urlFromUrls = cloudinary.downloadGeneratedSprite(new String[]{url1, url2}, null); + + assertTrue(urlFromTag.startsWith("https://api.cloudinary.com/v1_1/" + cloudinary.config.cloudName + "/image/sprite?mode=download")); + assertTrue(urlFromUrls.startsWith("https://api.cloudinary.com/v1_1/" + cloudinary.config.cloudName + "/image/sprite?mode=download")); + assertTrue(urlFromUrls.contains("urls[]=" + URLEncoder.encode(url1, "UTF-8"))); + assertTrue(urlFromUrls.contains("urls[]=" + URLEncoder.encode(url2, "UTF-8"))); + + Map parameters = getUrlParameters(new URI(urlFromTag)); + assertEquals(spriteTestTag, parameters.get("tag")); + assertNotNull(parameters.get("timestamp")); + assertNotNull(parameters.get("signature")); + + parameters = getUrlParameters(new URI(urlFromUrls)); + assertNotNull(parameters.get("timestamp")); + assertNotNull(parameters.get("signature")); + } + + @Test + public void testDownloadMulti() throws Exception{ + cloudinary = new Cloudinary("cloudinary://571927874334573:yABWqlfSV2d5pRW4ujHJYA7SD34@nitzanj?load_strategies=false"); + + final String multiTestTag = "multi_test_tag"; + final String url1 = "https://res.cloudinary.com/demo/image/upload/sample"; + final String url2 = "https://res.cloudinary.com/demo/image/upload/car"; + + String urlFromTag = cloudinary.downloadMulti(multiTestTag, null); + String urlFromUrls = cloudinary.downloadMulti(new String[]{url1, url2}, null); + + assertTrue(urlFromTag.startsWith("https://api.cloudinary.com/v1_1/" + cloudinary.config.cloudName + "/image/multi?mode=download")); + assertTrue(urlFromUrls.startsWith("https://api.cloudinary.com/v1_1/" + cloudinary.config.cloudName + "/image/multi?mode=download")); + assertTrue(urlFromUrls.contains("urls[]=" + URLEncoder.encode(url1, "UTF-8"))); + assertTrue(urlFromUrls.contains("urls[]=" + URLEncoder.encode(url2, "UTF-8"))); + + Map parameters = getUrlParameters(new URI(urlFromTag)); + assertEquals(multiTestTag, parameters.get("tag")); + assertNotNull(parameters.get("timestamp")); + assertNotNull(parameters.get("signature")); + + parameters = getUrlParameters(new URI(urlFromUrls)); + assertNotNull(parameters.get("timestamp")); + assertNotNull(parameters.get("signature")); + + } @Test public void testSpriteCss() { String result = cloudinary.url().generateSpriteCss("test"); 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 c31c12f4..72c28420 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 @@ -12,13 +12,13 @@ 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.*; import java.util.zip.ZipInputStream; -import static com.cloudinary.utils.ObjectUtils.asArray; -import static com.cloudinary.utils.ObjectUtils.asMap; +import static com.cloudinary.utils.ObjectUtils.*; import static com.cloudinary.utils.StringUtils.isRemoteUrl; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; @@ -259,7 +259,6 @@ public void testText() throws Exception { assertTrue(((Integer) result.get("height")) > 1); } - @Test public void testImageUploadTag() { String tag = cloudinary.uploader().imageUploadTag("test-field", asMap("callback", "http://localhost/cloudinary_cors.html"), asMap("htmlattr", "htmlvalue")); @@ -271,13 +270,19 @@ public void testImageUploadTag() { assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } - @Test public void testSprite() throws Exception { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); - Map result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + Map uploadResult1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_1" + SUFFIX)); + Map uploadResult2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", new String[]{sprite_test_tag, SDK_TEST_TAG, UPLOADER_TAG}, "public_id", "sprite_test_tag_2" + SUFFIX)); + + String[] urls = new String[]{uploadResult1.get("url").toString(), uploadResult2.get("url").toString()}; + + Map result = cloudinary.uploader().generateSprite(urls, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + addToDeleteList("sprite", result.get("public_id").toString()); + assertEquals(2, ((Map) result.get("image_infos")).size()); + + result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); addToDeleteList("sprite", result.get("public_id").toString()); assertEquals(2, ((Map) result.get("image_infos")).size()); result = cloudinary.uploader().generateSprite(sprite_test_tag, asMap("transformation", "w_100")); @@ -292,10 +297,19 @@ public void testSprite() throws Exception { public void testMulti() throws Exception { final String MULTI_TEST_TAG = "multi_test_tag" + SUFFIX; final Map options = asMap("tags", new String[]{MULTI_TEST_TAG, SDK_TEST_TAG, UPLOADER_TAG}); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + Map uploadResult1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + Map uploadResult2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + + String[] urls = new String[]{uploadResult1.get("url").toString(), uploadResult2.get("url").toString()}; + + Map result = cloudinary.uploader().multi(urls, asMap("transformation", "c_crop,w_0.5")); + addToDeleteList("multi", result.get("public_id").toString()); + + assertTrue(((String) result.get("url")).endsWith(".gif")); + assertTrue(((String) result.get("url")).contains("w_0.5")); + List ids = new ArrayList(); - Map result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5")); + result = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", "c_crop,w_0.5")); addToDeleteList("multi", result.get("public_id").toString()); Map pdfResult = cloudinary.uploader().multi(MULTI_TEST_TAG, asMap("transformation", new Transformation().width(111), "format", "pdf")); addToDeleteList("multi", pdfResult.get("public_id").toString()); From 6a7fa23c4f492ff2b6cb74f2c7190883337768c8 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Tue, 9 Jun 2020 16:38:05 +0300 Subject: [PATCH 435/592] Support different radius for each corner (#212) --- .../java/com/cloudinary/Transformation.java | 84 ++++++++++++++++++- .../com/cloudinary/TransformationTest.java | 48 +++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 7ff5d93c..78330c2f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -126,8 +126,70 @@ public T y(Object value) { return param("y", value); } + /** + * Add rounding transformation. + *

        + * Radius can be specified either as value in pixels or expression. Specify 0 to keep corner untouched. + * + * @param value rounding radius for all four corners + * @return updated transformation instance for chaining + */ public T radius(Object value) { - return param("radius", value); + return radius(new Object[]{value}); + } + + /** + * Add rounding transformation. + *

        + * Radius can be specified either as value in pixels or expression. Specify 0 to keep corner untouched. + * + * @param topLeftBottomRight rounding radius for top-left and bottom-right corners + * @param topRightBottomLeft rounding radius for top-right and bottom-left corners + * @return updated transformation instance for chaining + */ + public T radius(Object topLeftBottomRight, Object topRightBottomLeft) { + return radius(new Object[]{topLeftBottomRight, topRightBottomLeft}); + } + + /** + * Add rounding transformation. + *

        + * Radius can be specified either as value in pixels or expression. Specify 0 to keep corner untouched. + * + * @param topLeft rounding radius for top-left corner + * @param topRightBottomLeft rounding radius for top-right and bottom-left corners + * @param bottomRight rounding radius for bottom-right corner + * @return updated transformation instance for chaining + */ + public T radius(Object topLeft, Object topRightBottomLeft, Object bottomRight) { + return radius(new Object[]{topLeft, topRightBottomLeft, bottomRight}); + } + + /** + * Add rounding transformation. + *

        + * Radius can be specified either as value in pixels or expression. Specify 0 to keep corner untouched. + * + * @param topLeft rounding radius for top-left corner + * @param topRight rounding radius for top-right corner + * @param bottomRight rounding radius for bottom-right corner + * @param bottomLeft rounding radius for bottom-left corner + * @return updated transformation instance for chaining + */ + public T radius(Object topLeft, Object topRight, Object bottomRight, Object bottomLeft) { + return radius(new Object[]{topLeft, topRight, bottomRight, bottomLeft}); + } + + /** + * Add rounding transformation. + *

        + * Radius can be specified either as value in pixels or expression. Specify 0 to keep corner untouched. + * + * @param cornerRadiuses rounding radiuses for corners as array + * @return updated transformation instance for chaining + */ + public T radius(Object[] cornerRadiuses) { + return param("radius", cornerRadiuses); } public T quality(Object value) { @@ -694,7 +756,7 @@ public String generate(Map options) { params.put("h", Expression.normalize(height)); params.put("o", Expression.normalize(options.get("opacity"))); params.put("q", Expression.normalize(options.get("quality"))); - params.put("r", Expression.normalize(options.get("radius"))); + params.put("r", Expression.normalize(radiusToExpression((Object[]) options.get("radius")))); params.put("so", startOffset); params.put("t", namedTransformation); params.put("vc", videoCodec); @@ -902,4 +964,22 @@ public T customFunction(CustomFunction action) { public T customPreFunction(CustomFunction action) { return param("custom_function", "pre:" + action.toString()); } + + private String radiusToExpression(Object[] radiusOption) { + if (radiusOption == null) { + return null; + } + + if (radiusOption.length == 0 || radiusOption.length > 4) { + throw new IllegalArgumentException("Radius array should contain between 1 and 4 values"); + } + + for (Object o : radiusOption) { + if (o == null) { + throw new IllegalArgumentException("Radius options array should not contain nulls"); + } + } + + return StringUtils.join(radiusOption, ":"); + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index db7ebd36..cadb8a97 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -228,4 +228,52 @@ public void testShouldNotChangeVariableNamesWhenTheyNamedAfterKeyword() { assertEquals("$width_10/w_$width_add_10_add_w", t.generate()); } + + @Test + public void testRadiusTwoCornersAsValues() { + Transformation t = new Transformation() + .radius(10, 20); + + assertEquals("r_10:20", t.generate()); + } + + @Test + public void testRadiusTwoCornersAsExpressions() { + Transformation t = new Transformation() + .radius("10", "$v"); + + assertEquals("r_10:$v", t.generate()); + } + + @Test + public void testRadiusThreeCorners() { + Transformation t = new Transformation() + .radius(10, "$v", "30"); + + assertEquals("r_10:$v:30", t.generate()); + } + + @Test + public void testRadiusFourCorners() { + Transformation t = new Transformation() + .radius(10, "$v", "30", 40); + + assertEquals("r_10:$v:30:40", t.generate()); + } + + @Test + public void testRadiusArray1() { + Transformation t = new Transformation() + .radius(new Object[]{10}); + + assertEquals("r_10", t.generate()); + } + + @Test + public void testRadiusArray2() { + Transformation t = new Transformation() + .radius(new Object[]{10, "$v"}); + + assertEquals("r_10:$v", t.generate()); + } } \ No newline at end of file From 6be288be3b0635da288f2d2d1a03231fc49ee619 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Sun, 2 Aug 2020 12:13:19 +0300 Subject: [PATCH 436/592] Add support of SHA-256 algorithm in calculation of auth signatures (#215) --- .../main/java/com/cloudinary/Cloudinary.java | 10 ++-- .../java/com/cloudinary/Configuration.java | 59 ++++++++++++++++++- .../com/cloudinary/SignatureAlgorithm.java | 19 ++++++ .../src/main/java/com/cloudinary/Url.java | 15 ++--- .../src/main/java/com/cloudinary/Util.java | 41 ++++++++++--- .../signing/ApiResponseSignatureVerifier.java | 21 ++++++- .../NotificationRequestSignatureVerifier.java | 14 ++++- .../api/signing/SignedPayloadValidator.java | 22 ++----- .../cloudinary/api/signing/package-info.java | 2 +- .../ApiResponseSignatureVerifierTest.java | 10 ++++ ...ificationRequestSignatureVerifierTest.java | 13 ++++ .../com/cloudinary/test/CloudinaryTest.java | 28 +++++++++ 12 files changed, 211 insertions(+), 43 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/SignatureAlgorithm.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 85748a95..d855902a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -130,7 +130,7 @@ public String signedPreloadedImage(Map result) { } public String apiSignRequest(Map paramsToSign, String apiSecret) { - return Util.produceSignature(paramsToSign, apiSecret); + return Util.produceSignature(paramsToSign, apiSecret, config.signatureAlgorithm); } /** @@ -139,7 +139,7 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) * Cloudinary can asynchronously process your e.g. image uploads requests. This is achieved by calling back API you * specified during preparing of upload request as soon as it has been processed. See Upload Notifications in * Cloudinary documentation for more details. In order to make sure it is Cloudinary calling your API back, hashed - * message authentication codes (HMAC's) based on SHA-1 hashing function and configured Cloudinary API secret key + * message authentication codes (HMAC's) based on agreed hashing function and configured Cloudinary API secret key * are used for signing the requests. * * The following method serves as a convenient utility to perform the verification procedure. @@ -151,14 +151,14 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) * @return whether request signature is valid or not */ public boolean verifyNotificationSignature(String body, String timestamp, String signature, long validFor) { - return new NotificationRequestSignatureVerifier(config.apiSecret).verifySignature(body, timestamp, signature, validFor); + return new NotificationRequestSignatureVerifier(config.apiSecret, config.signatureAlgorithm).verifySignature(body, timestamp, signature, validFor); } /** * Verifies that Cloudinary API response is genuine by checking its signature. * * Cloudinary can add a signature value in the response to API methods returning public id's and versions. In order - * to make sure it is genuine Cloudinary response, hashed message authentication codes (HMAC's) based on SHA-1 hashing + * to make sure it is genuine Cloudinary response, hashed message authentication codes (HMAC's) based on agreed hashing * function and configured Cloudinary API secret key are used for signing the responses. * * The following method serves as a convenient utility to perform the verification procedure. @@ -169,7 +169,7 @@ public boolean verifyNotificationSignature(String body, String timestamp, String * @return whether response signature is valid or not */ public boolean verifyApiResponseSignature(String publicId, String version, String signature) { - return new ApiResponseSignatureVerifier(config.apiSecret).verifySignature(publicId, version, signature); + return new ApiResponseSignatureVerifier(config.apiSecret, config.signatureAlgorithm).verifySignature(publicId, version, signature); } public void signRequest(Map params, Map options) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 0400276f..b21aa799 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -20,6 +20,9 @@ public class Configuration { public final static String VERSION = "1.0.2"; 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; + + private static final String CONFIG_PROP_SIGNATURE_ALGORITHM = "signature_algorithm"; public String cloudName; public String apiKey; @@ -43,11 +46,32 @@ public class Configuration { public AuthToken authToken; public boolean forceVersion = true; public boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; + public SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; public Configuration() { } - private Configuration(String cloudName, String apiKey, String apiSecret, String secureDistribution, String cname, String uploadPrefix, boolean secure, boolean privateCdn, boolean cdnSubdomain, boolean shorten, String callback, String proxyHost, int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, int timeout, boolean loadStrategies, boolean forceVersion, boolean longUrlSignature) { + private Configuration( + String cloudName, + String apiKey, + String apiSecret, + String secureDistribution, + String cname, + String uploadPrefix, + boolean secure, + boolean privateCdn, + boolean cdnSubdomain, + boolean shorten, + String callback, + String proxyHost, + int proxyPort, + Boolean secureCdnSubdomain, + boolean useRootPath, + int timeout, + boolean loadStrategies, + boolean forceVersion, + boolean longUrlSignature, + SignatureAlgorithm signatureAlgorithm) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -67,6 +91,7 @@ private Configuration(String cloudName, String apiKey, String apiSecret, String this.loadStrategies = loadStrategies; this.forceVersion = forceVersion; this.longUrlSignature = longUrlSignature; + this.signatureAlgorithm = signatureAlgorithm; } @SuppressWarnings("rawtypes") @@ -104,6 +129,7 @@ public void update(Map config) { this.properties.putAll(properties); } 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())); } @SuppressWarnings("rawtypes") @@ -133,6 +159,7 @@ public Map asMap() { map.put("force_version", forceVersion); map.put("properties", new HashMap(properties)); map.put("long_url_signature", longUrlSignature); + map.put(CONFIG_PROP_SIGNATURE_ALGORITHM, signatureAlgorithm.toString()); return map; } @@ -162,6 +189,7 @@ public Configuration(Configuration other) { this.loadStrategies = other.loadStrategies; this.properties.putAll(other.properties); this.longUrlSignature = other.longUrlSignature; + this.signatureAlgorithm = other.signatureAlgorithm; } /** @@ -272,6 +300,7 @@ public static class Builder { private AuthToken authToken; private boolean forceVersion = true; private boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; + private SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; /** * Set the HTTP connection timeout. @@ -288,7 +317,27 @@ public Builder setTimeout(int timeout) { * Creates a {@link Configuration} with the arguments supplied to this builder */ public Configuration build() { - final Configuration configuration = new Configuration(cloudName, apiKey, apiSecret, secureDistribution, cname, uploadPrefix, secure, privateCdn, cdnSubdomain, shorten, callback, proxyHost, proxyPort, secureCdnSubdomain, useRootPath, timeout, loadStrategies, forceVersion, longUrlSignature); + final Configuration configuration = new Configuration( + cloudName, + apiKey, + apiSecret, + secureDistribution, + cname, + uploadPrefix, + secure, + privateCdn, + cdnSubdomain, + shorten, + callback, + proxyHost, + proxyPort, + secureCdnSubdomain, + useRootPath, + timeout, + loadStrategies, + forceVersion, + longUrlSignature, + signatureAlgorithm); configuration.clientHints = clientHints; return configuration; } @@ -412,6 +461,11 @@ public Builder setIsLongUrlSignature(boolean isLong) { return this; } + public Builder setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { + this.signatureAlgorithm = signatureAlgorithm; + return this; + } + /** * Initialize builder from existing {@link Configuration} * @@ -440,6 +494,7 @@ public Builder from(Configuration other) { this.authToken = other.authToken == null ? null : other.authToken.copy(); this.forceVersion = other.forceVersion; this.longUrlSignature = other.longUrlSignature; + this.signatureAlgorithm = other.signatureAlgorithm; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/SignatureAlgorithm.java b/cloudinary-core/src/main/java/com/cloudinary/SignatureAlgorithm.java new file mode 100644 index 00000000..c96fb1b3 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/SignatureAlgorithm.java @@ -0,0 +1,19 @@ +package com.cloudinary; + +/** + * Defines supported algorithms for generating/verifying hashed message authentication codes (HMAC). + */ +public enum SignatureAlgorithm { + SHA1("SHA-1"), + SHA256("SHA-256"); + + private final String algorithmId; + + SignatureAlgorithm(String algorithmId) { + this.algorithmId = algorithmId; + } + + public String getAlgorithmId() { + return algorithmId; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 3d9733c9..fad9ece8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -4,8 +4,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -19,6 +17,8 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import static com.cloudinary.SignatureAlgorithm.SHA256; + public class Url { private final Cloudinary cloudinary; private final Configuration config; @@ -389,19 +389,14 @@ public String generate(String source) { if (signUrl && (authToken == null || authToken.equals(AuthToken.NULL_AUTH_TOKEN))) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance(longUrlSignature ? "SHA-256" : "SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } + SignatureAlgorithm signatureAlgorithm = longUrlSignature ? SHA256 : config.signatureAlgorithm; String toSign = StringUtils.join(new String[]{transformationStr, sourceToSign}, "/"); toSign = StringUtils.removeStartingChars(toSign, '/'); toSign = StringUtils.mergeSlashesInUrl(toSign); - byte[] digest = md.digest(Util.getUTF8Bytes(toSign + this.config.apiSecret)); - signature = Base64Coder.encodeURLSafeString(digest); + byte[] hash = Util.hash(toSign + this.config.apiSecret, signatureAlgorithm); + signature = Base64Coder.encodeURLSafeString(hash); signature = "s--" + signature.substring(0, longUrlSignature ? 32 : 8) + "--"; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index dead1129..2c9d217c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -323,18 +323,36 @@ public static byte[] getUTF8Bytes(String string) { /** * Calculates signature, or hashed message authentication code (HMAC) of provided parameters name-value pairs and - * secret value using SHA-1 hashing algorithm. + * secret value using default hashing algorithm (SHA1). *

        * Argument for hashing function is built by joining sorted parameter name-value pairs into single string in the * same fashion as HTTP GET method uses, and concatenating the result with secret value in the end. Method supports * arrays/collections as parameter values. In this case, the elements of array/collection are joined into single * comma-delimited string prior to inclusion into the result. * - * @param paramsToSign parameter name-value pairs list represented as instance of {@link Map} - * @param apiSecret secret value + * @param paramsToSign parameter name-value pairs list represented as instance of {@link Map} + * @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); + } + + /** + * Calculates signature, or hashed message authentication code (HMAC) of provided parameters name-value pairs and + * secret value using specified hashing algorithm. + *

        + * Argument for hashing function is built by joining sorted parameter name-value pairs into single string in the + * same fashion as HTTP GET method uses, and concatenating the result with secret value in the end. Method supports + * arrays/collections as parameter values. In this case, the elements of array/collection are joined into single + * comma-delimited string prior to inclusion into the result. + * + * @param paramsToSign parameter name-value pairs list represented as instance of {@link Map} + * @param apiSecret secret value + * @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) { @@ -348,13 +366,22 @@ public static String produceSignature(Map paramsToSign, String a } } String to_sign = StringUtils.join(params, "&"); - MessageDigest md = null; + byte[] hash = Util.hash(to_sign + apiSecret, signatureAlgorithm); + return StringUtils.encodeHexString(hash); + } + + /** + * Computes hash from input string using specified algorithm. + * + * @param input string which to compute hash from + * @param signatureAlgorithm algorithm to use for computing hash + * @return array of bytes of computed hash value + */ + public static byte[] hash(String input, SignatureAlgorithm signatureAlgorithm) { try { - md = MessageDigest.getInstance("SHA-1"); + return MessageDigest.getInstance(signatureAlgorithm.getAlgorithmId()).digest(Util.getUTF8Bytes(input)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unexpected exception", e); } - byte[] digest = md.digest(getUTF8Bytes(to_sign + apiSecret)); - return StringUtils.encodeHexString(digest); } } 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 33224204..1dbae00d 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 @@ -1,5 +1,6 @@ package com.cloudinary.api.signing; +import com.cloudinary.SignatureAlgorithm; import com.cloudinary.Util; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -11,6 +12,7 @@ */ public class ApiResponseSignatureVerifier { private final String secretKey; + private final SignatureAlgorithm signatureAlgorithm; /** * Initializes new instance of {@code ApiResponseSignatureVerifier} class with a secret key required to perform @@ -24,6 +26,23 @@ public ApiResponseSignatureVerifier(String secretKey) { } this.secretKey = secretKey; + this.signatureAlgorithm = SignatureAlgorithm.SHA1; + } + + /** + * Initializes new instance of {@code ApiResponseSignatureVerifier} class with a secret key required to perform + * API response signatures verification. + * + * @param secretKey shared secret key string which is used to sign and verify authenticity of API responses + * @param signatureAlgorithm type of hashing algorithm to use for calculation of HMACs + */ + public ApiResponseSignatureVerifier(String secretKey, SignatureAlgorithm signatureAlgorithm) { + if (StringUtils.isBlank(secretKey)) { + throw new IllegalArgumentException("Secret key is required"); + } + + this.secretKey = secretKey; + this.signatureAlgorithm = signatureAlgorithm; } /** @@ -41,6 +60,6 @@ public ApiResponseSignatureVerifier(String secretKey) { public boolean verifySignature(String publicId, String version, String signature) { return Util.produceSignature(ObjectUtils.asMap( "public_id", emptyIfNull(publicId), - "version", emptyIfNull(version)), secretKey).equals(signature); + "version", emptyIfNull(version)), secretKey, signatureAlgorithm).equals(signature); } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java index 497e491e..62ad579b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java @@ -1,5 +1,7 @@ package com.cloudinary.api.signing; +import com.cloudinary.SignatureAlgorithm; + import static com.cloudinary.utils.StringUtils.emptyIfNull; /** @@ -15,7 +17,17 @@ public class NotificationRequestSignatureVerifier { * @param secretKey shared secret key string which is used to sign and verify authenticity of notifications */ public NotificationRequestSignatureVerifier(String secretKey) { - this.signedPayloadValidator = new SignedPayloadValidator(secretKey); + this.signedPayloadValidator = new SignedPayloadValidator(secretKey, SignatureAlgorithm.SHA1); + } + + /** + * Initializes new instance of {@code NotificationRequestSignatureVerifier} with secret key value. + * + * @param secretKey shared secret key string which is used to sign and verify authenticity of notifications + * @param signatureAlgorithm type of hashing algorithm to use for calculation of HMACs + */ + public NotificationRequestSignatureVerifier(String secretKey, SignatureAlgorithm signatureAlgorithm) { + this.signedPayloadValidator = new SignedPayloadValidator(secretKey, signatureAlgorithm); } /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java index 40339db6..771bdbe0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java @@ -1,39 +1,29 @@ package com.cloudinary.api.signing; +import com.cloudinary.SignatureAlgorithm; import com.cloudinary.Util; import com.cloudinary.utils.StringUtils; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - import static com.cloudinary.utils.StringUtils.emptyIfNull; class SignedPayloadValidator { private final String secretKey; - private final MessageDigest messageDigest; + private final SignatureAlgorithm signatureAlgorithm; - SignedPayloadValidator(String secretKey) { + SignedPayloadValidator(String secretKey, SignatureAlgorithm signatureAlgorithm) { if (StringUtils.isBlank(secretKey)) { throw new IllegalArgumentException("Secret key is required"); } this.secretKey = secretKey; - this.messageDigest = acquireMessageDigest(); + this.signatureAlgorithm = signatureAlgorithm; } boolean validateSignedPayload(String signedPayload, String signature) { String expectedSignature = - StringUtils.encodeHexString( - messageDigest.digest(Util.getUTF8Bytes(emptyIfNull(signedPayload) + secretKey))); + StringUtils.encodeHexString(Util.hash(emptyIfNull(signedPayload) + secretKey, + signatureAlgorithm)); return expectedSignature.equals(signature); } - - private static MessageDigest acquireMessageDigest() { - try { - return MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unexpected exception", e); - } - } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java index a95f5e74..9c75e3bd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java @@ -2,6 +2,6 @@ * The package holds classes used internally to implement verification procedures of authenticity and integrity of * client communication with Cloudinary servers. Verification is in most cases based on calculating and comparing so called * signatures, or hashed message authentication codes (HMAC) - string values calculated based on message payload, some - * secret key value shared between communicating parties and SHA-1 hashing function. + * secret key value shared between communicating parties and agreed hashing function. */ package com.cloudinary.api.signing; \ No newline at end of file diff --git a/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java b/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java index a198b8fc..5449f555 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java @@ -1,5 +1,6 @@ package com.cloudinary.api.signing; +import com.cloudinary.SignatureAlgorithm; import org.junit.Test; import static org.junit.Assert.assertFalse; @@ -23,4 +24,13 @@ public void testVerifySignatureFail() { assertFalse(actual); } + + @Test + public void testVerifySignatureSHA256() { + ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU", SignatureAlgorithm.SHA256); + + boolean actual = verifier.verifySignature("tests/logo.png", "1", "cc69ae4ed73303fbf4a55f2ae5fc7e34ad3a5c387724bfcde447a2957cacdfea"); + + assertTrue(actual); + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java b/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java index c38bf47a..a5d2e096 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java @@ -1,5 +1,6 @@ package com.cloudinary.api.signing; +import com.cloudinary.SignatureAlgorithm; import org.junit.Test; import static org.junit.Assert.assertFalse; @@ -68,4 +69,16 @@ public void testVerifySignatureFailWhenStillValidButSignatureDoesntMatch() { assertFalse(actual); } + + @Test + public void testVerifySignatureSHA256() { + NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret", SignatureAlgorithm.SHA256); + + boolean actual = verifier.verifySignature( + "{}", + "0", + "d5497e1a206ad0ba29ad09a7c0c5f22e939682d15009c15ab3199f62fefbd14b"); + + assertTrue(actual); + } } 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 3f03fef8..9c4e508c 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -738,6 +738,14 @@ public void testSignedUrl() { assertEquals(expected, actual); } + @Test + public void testSignedUrlSHA256() { + cloudinary.config.signatureAlgorithm = SignatureAlgorithm.SHA256; + + String url = cloudinary.url().signed(true).generate("sample.jpg"); + assertEquals(DEFAULT_UPLOAD_PATH + "s--2hbrSMPO--/sample.jpg", url); + } + @Test public void testResponsiveWidth() { // should support responsive width @@ -1312,6 +1320,20 @@ public void testCloudinaryUrlEmptyScheme() { Configuration.from(cloudinaryUrl); } + @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"); + 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"); + assertEquals("45ddaa4fa01f0c2826f32f669d2e4514faf275fe6df053f1a150e7beae58a3bd", signature); + } + 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(); @@ -1351,8 +1373,14 @@ private void setRandomValue(Random rand, Field field, Object instance) throws Il Map map = new HashMap(); map.put(cloudinary.randomPublicId(), rand.nextInt()); field.set(instance, map); + } else if (fieldType instanceof Class && Enum.class.isAssignableFrom((Class) fieldType)) { + field.set(instance, randomEnum((Class) fieldType, rand)); } else { throw new IllegalArgumentException("Object have unexpected field type, randomizing not supported: " + field.getName() + ", type: " + field.getType().getSimpleName()); } } + + private > T randomEnum(Class clazz, Random random) { + return clazz.getEnumConstants()[random.nextInt(clazz.getEnumConstants().length)]; + } } From a871be187d6357b59aceb740817072eeffa01fa6 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Wed, 5 Aug 2020 08:39:33 +0300 Subject: [PATCH 437/592] Add eval upload parameter (#217) --- .../src/main/java/com/cloudinary/Util.java | 1 + .../java/com/cloudinary/test/AbstractApiTest.java | 3 ++- .../com/cloudinary/test/AbstractUploaderTest.java | 11 +++++++++++ 3 files changed, 14 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 2c9d217c..40f0d02b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -26,6 +26,7 @@ public static final Map buildUploadParams(Map options) { putBoolean(attr, options, params); } + params.put("eval",(String) options.get("eval")); params.put("notification_url", (String) options.get("notification_url")); params.put("eager_notification_url", (String) options.get("eager_notification_url")); params.put("proxy", (String) options.get("proxy")); 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 7e05d517..4f943b3a 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 @@ -714,7 +714,7 @@ 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)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true, "live", true, "eval",AbstractUploaderTest.SRC_TEST_EVAL)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); preset = api.uploadPreset(name, ObjectUtils.emptyMap()); @@ -722,6 +722,7 @@ public void testUpdateUploadPreset() throws Exception { assertEquals(Boolean.TRUE, preset.get("unsigned")); assertEquals(settings.get("live"), Boolean.TRUE); assertEquals(settings, preset.get("settings")); + api.deleteUploadPreset(name, ObjectUtils.emptyMap()); } 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 72c28420..76ebb919 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 @@ -34,6 +34,9 @@ abstract public class AbstractUploaderTest extends MockableTest { private static final String UPLOADER_TEST_PUBLIC_ID = "uploader_test"; public static final String SRC_FULLY_QUALIFIED_IMAGE="image/upload/" + UPLOADER_TEST_PUBLIC_ID; public static final String SRC_FULLY_QUALIFIED_VIDEO="video/upload/dog"; + public static final String SRC_TEST_EVAL= "if (resource_info['width'] < 450) { upload_options['tags'] = 'a,b' };" + "upload_options['context'] = 'width=' + resource_info['width'];"; + private static final ArrayList TEST_EVAL_TAGS_RESULT = new ArrayList(Arrays.asList("a","b")); + @BeforeClass public static void setUpClass() throws IOException { @@ -270,6 +273,14 @@ public void testImageUploadTag() { assertTrue(tag.contains("class='cloudinary-fileupload myclass'")); } + @Test + public void testEvalUploadParameter() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eval",SRC_TEST_EVAL)); + assertEquals(result.get("tags"), TEST_EVAL_TAGS_RESULT); + Map custom= (Map)((Map) result.get("context")).get("custom"); + assertEquals(custom.get("width"),Integer.toString(SRC_TEST_IMAGE_W)); + } + @Test public void testSprite() throws Exception { final String sprite_test_tag = String.format("sprite_test_tag_%d", new java.util.Date().getTime()); From 4ac51a73fd109e0111667276319766fbf2cdb03d Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Wed, 2 Sep 2020 13:46:43 +0300 Subject: [PATCH 438/592] Add support for 'accessibility_analysis' parameter (#218) --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 3 ++- cloudinary-core/src/main/java/com/cloudinary/Util.java | 3 ++- .../main/java/com/cloudinary/test/AbstractApiTest.java | 6 ++++++ .../java/com/cloudinary/test/AbstractUploaderTest.java | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 266f7962..d475f2b3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -140,7 +140,8 @@ 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"), options); + "image_metadata", "pages", "phash", "max_results", "quality_analysis", "cinemagraph_analysis", + "accessibility_analysis"), options); return response; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 40f0d02b..03b5965e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -10,7 +10,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"}; + "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async", "quality_analysis", "cinemagraph_analysis", + "accessibility_analysis"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map 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 4f943b3a..8c894ac3 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 @@ -996,4 +996,10 @@ public void testCinemagraphAnalysisResource() throws Exception { ApiResponse res = api.resource(API_TEST, Collections.singletonMap("cinemagraph_analysis", true)); assertNotNull(res.get("cinemagraph_analysis")); } + + @Test + public void testAccessibilityAnalysisResource() throws Exception { + ApiResponse res = api.resource(API_TEST, Collections.singletonMap("accessibility_analysis", true)); + assertNotNull(res.get("accessibility_analysis")); + } } 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 76ebb919..6114b93d 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 @@ -727,6 +727,14 @@ public void testCinemagraphAnalysisUpload() throws IOException { } + @Test + public void testAccessibilityAnalysisUpload() throws IOException { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("accessibility_analysis", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + assertNotNull(result.get("accessibility_analysis")); + result = cloudinary.uploader().explicit(result.get("public_id").toString(), ObjectUtils.asMap("type", "upload", "resource_type", "image", "accessibility_analysis", true)); + assertNotNull(result.get("accessibility_analysis")); + } + private void addToDeleteList(String type, String id) { Set ids = toDelete.get(type); if (ids == null) { From f0646a89fb5946fbd1e594db97c189c456168d01 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Mon, 7 Sep 2020 10:09:22 +0300 Subject: [PATCH 439/592] Fix normalize_expression for complex cases (#216) --- .../transformation/BaseExpression.java | 22 ++- .../com/cloudinary/TransformationTest.java | 12 ++ .../transformation/ExpressionTest.java | 177 ++++++++++++++++++ 3 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index 43415e4d..0da9d702 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -53,6 +53,7 @@ public abstract class BaseExpression { ); private static final Pattern PATTERN = getPattern(); + private static final Pattern USER_VARIABLE_PATTERN = Pattern.compile("\\$_*[^_]+"); protected List expressions = null; protected Transformation parent = null; @@ -77,10 +78,25 @@ public static String normalize(Object expression) { return String.valueOf(expression); } - String replacement; String conditionStr = StringUtils.mergeToSingleUnderscore(String.valueOf(expression)); - Matcher matcher = PATTERN.matcher(conditionStr); - StringBuffer result = new StringBuffer(conditionStr.length()); + + Matcher m = USER_VARIABLE_PATTERN.matcher(conditionStr); + StringBuilder builder = new StringBuilder(); + int lastMatchEnd = 0; + while (m.find()) { + String beforeMatch = conditionStr.substring(lastMatchEnd, m.start()); + builder.append(normalizeBuiltins(beforeMatch)); + builder.append(m.group()); + lastMatchEnd = m.end(); + } + builder.append(normalizeBuiltins(conditionStr.substring(lastMatchEnd))); + return builder.toString(); + } + + private static String normalizeBuiltins(String input) { + String replacement; + Matcher matcher = PATTERN.matcher(input); + StringBuffer result = new StringBuffer(input.length()); while (matcher.find()) { if (OPERATORS.containsKey(matcher.group())) { replacement = (String) OPERATORS.get(matcher.group()); diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index cadb8a97..0259dcc4 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -276,4 +276,16 @@ public void testRadiusArray2() { assertEquals("r_10:$v", t.generate()); } + + @Test + public void testUserVariableNamesContainingPredefinedNamesAreNotAffected() { + Transformation t = new Transformation() + .variable("$mywidth", "100") + .variable("$aheight", 300) + .chain() + .width("3 + $mywidth * 3 + 4 / 2 * initialWidth * $mywidth") + .height("3 * initialHeight + $aheight"); + + assertEquals("$aheight_300,$mywidth_100/h_3_mul_ih_add_$aheight,w_3_add_$mywidth_mul_3_add_4_div_2_mul_iw_mul_$mywidth", t.generate()); + } } \ No newline at end of file diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java new file mode 100644 index 00000000..733f1a2a --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java @@ -0,0 +1,177 @@ +package com.cloudinary.transformation; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ExpressionTest { + + @Test + public void normalize_null_null() { + String result = Expression.normalize(null); + assertNull(result); + } + + @Test + public void normalize_number_number() { + String result = Expression.normalize(10); + assertEquals("10", result); + } + + @Test + public void normalize_emptyString_emptyString() { + String result = Expression.normalize(""); + assertEquals("", result); + } + + @Test + public void normalize_singleSpace_underscore() { + String result = Expression.normalize(" "); + assertEquals("_", result); + } + + @Test + public void normalize_blankString_underscore() { + String result = Expression.normalize(" "); + assertEquals("_", result); + } + + @Test + public void normalize_underscore_underscore() { + String result = Expression.normalize("_"); + assertEquals("_", result); + } + + @Test + public void normalize_underscores_underscore() { + String result = Expression.normalize("___"); + assertEquals("_", result); + } + + @Test + public void normalize_underscoresAndSpaces_underscore() { + String result = Expression.normalize(" _ __ _"); + assertEquals("_", result); + } + + @Test + public void normalize_arbitraryText_isNotAffected() { + String result = Expression.normalize("foobar"); + assertEquals("foobar", result); + } + + @Test + public void normalize_doubleAmpersand_replacedWithAndOperator() { + String result = Expression.normalize("foo && bar"); + assertEquals("foo_and_bar", result); + } + + @Test + public void normalize_doubleAmpersandWithNoSpaceAtEnd_isNotAffected() { + String result = Expression.normalize("foo&&bar"); + assertEquals("foo&&bar", result); + } + + @Test + public void normalize_width_recognizedAsVariableAndReplacedWithW() { + String result = Expression.normalize("width"); + assertEquals("w", result); + } + + @Test + public void normalize_initialAspectRatio_recognizedAsVariableAndReplacedWithW() { + String result = Expression.normalize("initial_aspect_ratio"); + assertEquals("iar", result); + } + + @Test + public void normalize_dollarWidth_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$width"); + assertEquals("$width", result); + } + + @Test + public void normalize_dollarInitialAspectRatio_recognizedAsUserVariableAndAsVariableReplacedWithAr() { + String result = Expression.normalize("$initial_aspect_ratio"); + assertEquals("$initial_ar", result); + } + + @Test + public void normalize_dollarMyWidth_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$mywidth"); + assertEquals("$mywidth", result); + } + + @Test + public void normalize_dollarWidthWidth_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$widthwidth"); + assertEquals("$widthwidth", result); + } + + @Test + public void normalize_dollarUnderscoreWidth_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$_width"); + assertEquals("$_width", result); + } + + @Test + public void normalize_dollarUnderscoreX2Width_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$__width"); + assertEquals("$_width", result); + } + + @Test + public void normalize_dollarX2Width_recognizedAsUserVariableAndNotAffected() { + String result = Expression.normalize("$$width"); + assertEquals("$$width", result); + } + + @Test + public void normalize_doesntReplaceVariable_1() { + String actual = Expression.normalize("$height_100"); + assertEquals("$height_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_2() { + String actual = Expression.normalize("$heightt_100"); + assertEquals("$heightt_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_3() { + String actual = Expression.normalize("$$height_100"); + assertEquals("$$height_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_4() { + String actual = Expression.normalize("$heightmy_100"); + assertEquals("$heightmy_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_5() { + String actual = Expression.normalize("$myheight_100"); + assertEquals("$myheight_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_6() { + String actual = Expression.normalize("$heightheight_100"); + assertEquals("$heightheight_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_7() { + String actual = Expression.normalize("$theheight_100"); + assertEquals("$theheight_100", actual); + } + + @Test + public void normalize_doesntReplaceVariable_8() { + String actual = Expression.normalize("$__height_100"); + assertEquals("$_height_100", actual); + } +} \ No newline at end of file From 470de5c9a5ad06dbb554f309898acfee8da1f635 Mon Sep 17 00:00:00 2001 From: francistagbo <61406266+francistagbo@users.noreply.github.com> Date: Tue, 20 Oct 2020 15:40:57 +0800 Subject: [PATCH 440/592] Fix illegal detection test (#222) --- .../src/main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 +- 1 file changed, 1 insertion(+), 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 6114b93d..f37d2fbd 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 @@ -496,7 +496,7 @@ public void testDetectionRequest() { message = e.getMessage(); } - assertTrue("Detection is invalid".equals(message)); + assertTrue("Detection invalid model 'illegal'".equals(message)); } @Test From e4e73a1eb917f134bb9b0b20491392b74d965bb4 Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Thu, 22 Oct 2020 11:48:53 +0300 Subject: [PATCH 441/592] Add downloadFolder method (#219) --- .../main/java/com/cloudinary/Cloudinary.java | 25 +++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index d855902a..88cece6b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -284,6 +284,31 @@ public String downloadMulti(String[] urls, Map options) throws IOException { return buildUrl(cloudinaryApiUrl("multi", options), params); } + /** + * Generates URL for executing "Download Folder" operation on Cloudinary site. + * + * @param folderPath path of folder to generate download URL for + * @param options optional, holds hints for URL generation procedure, see documentation for full list + * @return generated URL for downloading specified folder as ZIP archive + */ + public String downloadFolder(String folderPath, Map options) throws UnsupportedEncodingException { + if (StringUtils.isEmpty(folderPath)) { + throw new IllegalArgumentException("Folder path parameter value is required"); + } + + Map adjustedOptions = new HashMap(); + if (options != null) { + adjustedOptions.putAll(options); + } + + adjustedOptions.put("prefixes", folderPath); + + final Object resourceType = adjustedOptions.get("resource_type"); + adjustedOptions.put("resource_type", resourceType != null ? resourceType : "all"); + + return downloadArchive(adjustedOptions, (String) adjustedOptions.get("target_format")); + } + private String buildUrl(String base, Map params) throws UnsupportedEncodingException { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(base); 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 9c4e508c..6479c177 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -702,6 +702,37 @@ public void testDownloadMulti() throws Exception{ assertNotNull(parameters.get("signature")); } + + @Test + public void testDownloadFolderShouldReturnURLWithResourceTypeAllByDefault() throws UnsupportedEncodingException { + String url = cloudinary.downloadFolder("folder", null); + assertTrue(url.contains("all")); + } + + @Test + public void testDownloadFolderShouldAllowToOverrideResourceType() throws UnsupportedEncodingException { + String url = cloudinary.downloadFolder("folder", Collections.singletonMap("resource_type", "audio")); + assertTrue(url.contains("audio")); + } + + @Test + public void testDownloadFolderShouldPutFolderPathAsPrefixes() throws UnsupportedEncodingException { + String url = cloudinary.downloadFolder("folder", null); + assertTrue(url.contains("prefixes[]=folder")); + } + + @Test + public void testDownloadFolderShouldIncludeSpecifiedTargetFormat() throws UnsupportedEncodingException { + String url = cloudinary.downloadFolder("folder", Collections.singletonMap("target_format", "rar")); + assertTrue(url.contains("target_format=rar")); + } + + @Test + public void testDownloadFolderShouldNotIncludeTargetFormatIfNotSpecified() throws UnsupportedEncodingException { + String url = cloudinary.downloadFolder("folder", null); + assertFalse(url.contains("target_format")); + } + @Test public void testSpriteCss() { String result = cloudinary.url().generateSpriteCss("test"); From a5cda7b1a1804848cf8b62a50ecb47e48143a944 Mon Sep 17 00:00:00 2001 From: francistagbo <61406266+francistagbo@users.noreply.github.com> Date: Thu, 22 Oct 2020 17:52:54 +0800 Subject: [PATCH 442/592] Fix named transformation with spaces (#224) --- .../src/main/java/com/cloudinary/Transformation.java | 5 ++++- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 78330c2f..f1efd89c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -675,10 +675,13 @@ public String generate(Map options) { List transformations = ObjectUtils.asArray(options.get("transformation")); boolean allNamed = true; - for (Object baseTransformation : transformations) { + for ( int i =0; i < transformations.size(); i++ ){ + Object baseTransformation = transformations.get(i); if (baseTransformation instanceof Map) { allNamed = false; break; + } else if (baseTransformation instanceof String){ + transformations.set(i, ((String) baseTransformation).replaceAll(" ", "%20")); } } String namedTransformation = null; 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 6479c177..7202e993 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -388,6 +388,14 @@ public void testTransformationArray() { assertEquals(DEFAULT_UPLOAD_PATH + "t_blip.blop/test", result); } + @Test + public void testNamedTransformationWithSpaces() { + // should support named transformations with spaces + Transformation transformation = new Transformation().named("blip blop"); + String result = cloudinary.url().transformation(transformation).generate("test"); + assertEquals(DEFAULT_UPLOAD_PATH + "t_blip%20blop/test", result); + } + @Test public void testBaseTransformations() { // should support base transformation From 4570326346a677983340a03a60133ca43b2557d4 Mon Sep 17 00:00:00 2001 From: francistagbo <61406266+francistagbo@users.noreply.github.com> Date: Thu, 29 Oct 2020 15:17:36 +0800 Subject: [PATCH 443/592] Add test for context metadata as user variables (#223) --- .../test/java/com/cloudinary/TransformationTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 0259dcc4..a3de2700 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -288,4 +288,16 @@ public void testUserVariableNamesContainingPredefinedNamesAreNotAffected() { assertEquals("$aheight_300,$mywidth_100/h_3_mul_ih_add_$aheight,w_3_add_$mywidth_mul_3_add_4_div_2_mul_iw_mul_$mywidth", t.generate()); } + + @Test + public void testContextMetadataToUserVariables() { + Transformation t = new Transformation() + .variable("$xpos", "ctx:!x_pos!_to_f") + .variable("$ypos", "ctx:!y_pos!_to_f") + .crop("crop") + .x("$xpos * w") + .y("$ypos * h"); + + assertEquals("$xpos_ctx:!x_pos!_to_f,$ypos_ctx:!y_pos!_to_f,c_crop,x_$xpos_mul_w,y_$ypos_mul_h", t.generate()); + } } \ No newline at end of file From 922d42d21de7706666dff1ca07ca40aa32541398 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 11 Nov 2020 09:48:43 +0200 Subject: [PATCH 444/592] Add support for variables in text style. (#225) --- .../cloudinary/transformation/TextLayer.java | 29 ++++++++++++++++++- .../com/cloudinary/test/CloudinaryTest.java | 24 +++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java index df2986c4..55380df3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayer.java @@ -21,6 +21,7 @@ public class TextLayer extends AbstractLayer { protected String letterSpacing = null; protected Integer lineSpacing = null; protected String text = null; + protected Object textStyle = null; @Override TextLayer getThis() { @@ -118,6 +119,28 @@ public TextLayer text(String text) { return getThis(); } + /** + * Sets a text style identifier, + * Note: If this is used, all other style attributes are ignored in favor of this identifier + * @param textStyleIdentifier A variable string or an explicit style (e.g. "Arial_17_bold_antialias_best") + * @return Itself for chaining + */ + public TextLayer textStyle(String textStyleIdentifier) { + this.textStyle = textStyleIdentifier; + return getThis(); + } + + /** + * Sets a text style identifier using an expression. + * Note: If this is used, all other style attributes are ignored in favor of this identifier + * @param textStyleIdentifier An expression instance referencing the style. + * @return Itself for chaining + */ + public TextLayer textStyle(Expression textStyleIdentifier) { + this.textStyle = textStyleIdentifier; + return getThis(); + } + @Override public String toString() { if (this.publicId == null && this.text == null) { @@ -144,6 +167,11 @@ public String toString() { } protected String textStyleIdentifier() { + // Note: if a text-style argument is provided as a whole, it overrides everything else, no mix and match. + if (StringUtils.isNotBlank(this.textStyle)) { + return textStyle.toString(); + } + ArrayList components = new ArrayList(); if (StringUtils.isNotBlank(this.fontWeight) && !this.fontWeight.equals("normal")) @@ -181,6 +209,5 @@ protected String textStyleIdentifier() { components.add(0, this.fontFamily); return StringUtils.join(components, "_"); - } } 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 7202e993..e0875980 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -101,6 +101,30 @@ public void testSecureDistribution() { assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); } + @Test + public void testTextLayerStyleIdentifierVariables() { + String url = cloudinary.url().transformation( + new Transformation() + .variable("$style", "!Arial_12!") + .chain() + .overlay( + 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); + + url = cloudinary.url().transformation( + new Transformation() + .variable("$style", "!Arial_12!") + .chain() + .overlay( + 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); + } + + @Test public void testSecureDistributionOverwrite() { // should allow overwriting secure distribution if secure=TRUE From 7842e9a0320d670d23a4dabcc61187a2498aa2d1 Mon Sep 17 00:00:00 2001 From: Michal kuperman <51990104+michalkcloudinay@users.noreply.github.com> Date: Mon, 16 Nov 2020 09:51:44 +0200 Subject: [PATCH 445/592] Support `type` parameter in `Uploader.updateMetadata()` (#226) --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 1 + .../com/cloudinary/test/AbstractStructuredMetadataTest.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index c4ee5d59..65d74a0f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -532,6 +532,7 @@ public Map updateMetadata(Map metadata, String[] publicIds, Map options) throws Map params = new HashMap(); params.put("metadata", Util.encodeContext(metadata)); params.put("public_ids", Arrays.asList(publicIds)); + params.put("type", (String)options.get("type")); return callApi("metadata", params, options, null); } 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 1256545b..e073e442 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 @@ -18,6 +18,7 @@ public abstract class AbstractStructuredMetadataTest extends MockableTest { private static final String METADATA_UPLOADER_TAG = SDK_TEST_TAG + "_uploader"; private static final String PUBLIC_ID = "before_class_public_id" + SUFFIX; + private static final String PRIVATE_PUBLIC_ID = "before_class_private_public_id" + SUFFIX; protected Api api; public static final List metadataFieldExternalIds = new ArrayList(); @@ -29,6 +30,7 @@ public static void setUpClass() throws IOException { } cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("public_id", PUBLIC_ID)); + cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("public_id", PRIVATE_PUBLIC_ID, "type", "private")); } @AfterClass @@ -226,6 +228,10 @@ public void testUploaderUpdateMetadata() throws Exception { Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{PUBLIC_ID}, null); assertNotNull(result); assertEquals(PUBLIC_ID, ((List) result.get("public_ids")).get(0).toString()); + //test updateMetadata for private asset + Map result2 = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{PRIVATE_PUBLIC_ID}, asMap("type","private")); + assertNotNull(result); + assertEquals(PRIVATE_PUBLIC_ID, ((List) result2.get("public_ids")).get(0).toString()); } @Test From 16f8e2118f379bfcb15a9b7feaf38b53166c2212 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 16 Nov 2020 20:43:39 +0200 Subject: [PATCH 446/592] Version 1.27.0 --- CHANGELOG.md | 22 +++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf063d2..8ad93a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,26 @@ +1.27.0 / 2020-11-16 +=================== + +New functionality +----------------- + * Support `type` parameter in `Uploader.updateMetadata()` (#226) + * Add `downloadFolder` method (#219) + * Add eval upload parameter (#217) + * Add support of SHA-256 algorithm in calculation of auth signatures (#215) + * Support different radius for each corner (#212) + * Add support for variables in text style. (#225) + * Add support for 'accessibility_analysis' parameter (#218) + * Support new parameter and modes in `generateSprite()` and `multi()` API cals. + * Add support for `date` param in `Api.usage()` (#210) + + +Other changes +------------- + * Fix named transformation with spaces (#224) + * Fix normalize_expression for complex cases (#216) + * Detect data URLs with suffix in mime type (#213) + 1.26.0 / 2020-05-05 =================== diff --git a/README.md b/README.md index 58079075..69ef18cf 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.26.0 + 1.27.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.26.0/cloudinary-core-1.26.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.26.0/cloudinary-http44-1.26.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.27.0/cloudinary-core-1.27.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.27.0/cloudinary-http44-1.27.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 88cece6b..07ec04a0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -35,7 +35,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.26.0"; + public final static String VERSION = "1.27.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index bf43c39f..07263ab8 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.26.0 +version=1.27.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 1ce6675ef248063f7a0879a6aa2020a8660ec572 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 19 Jan 2021 17:45:53 +0200 Subject: [PATCH 447/592] Add `oauth` support to Admin Api calls. (#230) Fix `timeout` config value on copy. --- .../java/com/cloudinary/Configuration.java | 20 ++++++++++++++++--- .../strategies/AbstractApiStrategy.java | 16 +++++++++++++++ .../com/cloudinary/http42/ApiStrategy.java | 13 ++++++------ .../com/cloudinary/http43/ApiStrategy.java | 6 +++--- .../com/cloudinary/http44/ApiStrategy.java | 8 ++++---- .../com/cloudinary/test/AbstractApiTest.java | 14 ++++++++++++- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index b21aa799..18e813ce 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -47,6 +47,7 @@ public class Configuration { public boolean forceVersion = true; public boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; public SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; + public String oauthToken = null; public Configuration() { } @@ -71,7 +72,8 @@ private Configuration( boolean loadStrategies, boolean forceVersion, boolean longUrlSignature, - SignatureAlgorithm signatureAlgorithm) { + SignatureAlgorithm signatureAlgorithm, + String oauthToken) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -87,11 +89,12 @@ private Configuration( this.proxyPort = proxyPort; this.secureCdnSubdomain = secureCdnSubdomain; this.useRootPath = useRootPath; - this.timeout = 0; + this.timeout = timeout; this.loadStrategies = loadStrategies; this.forceVersion = forceVersion; this.longUrlSignature = longUrlSignature; this.signatureAlgorithm = signatureAlgorithm; + this.oauthToken = oauthToken; } @SuppressWarnings("rawtypes") @@ -130,6 +133,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.oauthToken = (String) config.get("oauth_token"); } @SuppressWarnings("rawtypes") @@ -160,6 +164,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("oauth_token", oauthToken); return map; } @@ -190,6 +195,7 @@ public Configuration(Configuration other) { this.properties.putAll(other.properties); this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; + this.oauthToken = other.oauthToken; } /** @@ -301,6 +307,7 @@ public static class Builder { private boolean forceVersion = true; private boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; private SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; + private String oauthToken = null; /** * Set the HTTP connection timeout. @@ -337,7 +344,8 @@ public Configuration build() { loadStrategies, forceVersion, longUrlSignature, - signatureAlgorithm); + signatureAlgorithm, + oauthToken); configuration.clientHints = clientHints; return configuration; } @@ -466,6 +474,11 @@ public Builder setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { return this; } + public Builder setOAuthToken(String oauthToken) { + this.oauthToken = oauthToken; + return this; + } + /** * Initialize builder from existing {@link Configuration} * @@ -495,6 +508,7 @@ public Builder from(Configuration other) { this.forceVersion = other.forceVersion; this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; + this.oauthToken = other.oauthToken; return this; } } 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 65d0970d..9e427c4a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -4,6 +4,7 @@ import com.cloudinary.Api.HttpMethod; import com.cloudinary.SmartUrlEncoder; import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.StringUtils; import java.util.Arrays; import java.util.Map; @@ -31,4 +32,19 @@ protected String createApiUrl (Iterable uri, String prefix, String cloud 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; + + 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"); + } + } } 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 7c19aeb9..a8aa7c93 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -38,14 +38,15 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map params, String apiKey, String apiSecret, String contentType, int timeout, String apiUrl) throws Exception { + 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()) { @@ -105,7 +106,7 @@ private ApiResponse getApiResponse(HttpMethod method, Map params, Str request = new HttpDelete(apiUri); break; } - request.setHeader("Authorization", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); + request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); request.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); if (contentType.equals("json")) { JSONObject asJSON = ObjectUtils.toJSON(params); diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index 400e252d..1d1b4232 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -79,15 +79,15 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map uri, Map 0); } + @Test + public void testOAuthToken() { + String message = ""; + try { + api.resource(API_TEST, Collections.singletonMap("oauth_token", "not_a_real_token")); + } catch (Exception e) { + message = e.getMessage(); + } + + assertTrue(message.contains("Invalid token")); + } + @Test public void test05ResourcesByPrefix() throws Exception { // should allow listing resources by prefix @@ -996,7 +1008,7 @@ public void testCinemagraphAnalysisResource() throws Exception { ApiResponse res = api.resource(API_TEST, Collections.singletonMap("cinemagraph_analysis", true)); assertNotNull(res.get("cinemagraph_analysis")); } - + @Test public void testAccessibilityAnalysisResource() throws Exception { ApiResponse res = api.resource(API_TEST, Collections.singletonMap("accessibility_analysis", true)); From a4679346f235dfb87ef5ee68e33fa98f6d4033a0 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 31 Jan 2021 17:55:20 +0200 Subject: [PATCH 448/592] Fix connection reuse when using apache-http-client versions 4.3 and 4.4 (#231) --- .../src/main/java/com/cloudinary/http43/ApiStrategy.java | 7 +++++-- .../src/main/java/com/cloudinary/http44/ApiStrategy.java | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index 1d1b4232..ef02ce17 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -10,6 +10,7 @@ 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; @@ -21,6 +22,7 @@ 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; @@ -98,8 +100,9 @@ private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { CloseableHttpResponse response = client.execute(request); try { code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); + final HttpEntity entity = response.getEntity(); + responseData = StringUtils.read(entity.getContent()); + EntityUtils.consume(entity); } finally { response.close(); } diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index bfe67f59..df8eedb3 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -10,6 +10,7 @@ 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; @@ -22,6 +23,7 @@ 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; @@ -100,8 +102,9 @@ private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { CloseableHttpResponse response = client.execute(request); try { code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); + final HttpEntity entity = response.getEntity(); + responseData = StringUtils.read(entity.getContent()); + EntityUtils.consume(entity); } finally { response.close(); } From 4cb77620de288eac09d529bd92c3464fabdf0ff1 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 1 Feb 2021 19:23:41 +0200 Subject: [PATCH 449/592] Version 1.28.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ad93a31..d2f1f294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ +1.28.0 / 2021-02-01 +================== + + * Add `oauth` support to Admin Api calls. (#230) + * Fix connection reuse when using apache-http-client (versions 4.3 and 4.4) (#231) + 1.27.0 / 2020-11-16 =================== diff --git a/README.md b/README.md index 69ef18cf..216bd90d 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.27.0 + 1.28.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.27.0/cloudinary-core-1.27.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.27.0/cloudinary-http44-1.27.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.28.0/cloudinary-core-1.28.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.28.0/cloudinary-http44-1.28.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 07ec04a0..1f2a05a0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -35,7 +35,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.27.0"; + public final static String VERSION = "1.28.0"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 07263ab8..751b66a8 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.27.0 +version=1.28.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 3602cc7b57f68ff5eac47fc55b2a9e32972466cf Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 Feb 2021 07:51:13 +0200 Subject: [PATCH 450/592] Fix `api` reuse bug when calling `cloudinary.search()` (#232) --- cloudinary-core/src/main/java/com/cloudinary/Search.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index 94cd222a..dc2f8a96 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -10,14 +10,14 @@ public class Search { - private Cloudinary cloudinary; + private final Api api; private ArrayList> sortByParam; private ArrayList aggregateParam; private ArrayList withFieldParam; private HashMap params; Search(Cloudinary cloudinary) { - this.cloudinary = cloudinary; + this.api = cloudinary.api(); this.params = new HashMap(); this.sortByParam = new ArrayList>(); this.aggregateParam = new ArrayList(); @@ -66,6 +66,6 @@ public HashMap toQuery() { public ApiResponse execute() throws Exception { Map options = ObjectUtils.asMap("content_type", "json"); - return this.cloudinary.api().callApi(Api.HttpMethod.POST, Arrays.asList("resources", "search"), this.toQuery(), options); + return this.api.callApi(Api.HttpMethod.POST, Arrays.asList("resources", "search"), this.toQuery(), options); } } \ No newline at end of file From 0ae1065b691f25f406b197a549cbaafe103b44c7 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 3 Feb 2021 07:59:01 +0200 Subject: [PATCH 451/592] Version 1.28.1 --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f1f294..c5cfd8dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ -1.28.0 / 2021-02-01 +1.28.1 / 2021-02-03 +================== + + * Fix `api` reuse bug when calling `cloudinary.search()` (#232) + +1.28.1 / 2021-02-01 ================== * Add `oauth` support to Admin Api calls. (#230) diff --git a/README.md b/README.md index 216bd90d..a9d61ce0 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.28.0 + 1.28.1 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.28.0/cloudinary-core-1.28.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.28.0/cloudinary-http44-1.28.0.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.28.1/cloudinary-core-1.28.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.28.1/cloudinary-http44-1.28.1.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1f2a05a0..1ed4521b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -35,7 +35,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.28.0"; + public final static String VERSION = "1.28.1"; public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; public final Configuration config; diff --git a/gradle.properties b/gradle.properties index 751b66a8..484fccbd 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.28.0 +version=1.28.1 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From ae2434294b880f9e552f26d2a049c27c7f900dfe Mon Sep 17 00:00:00 2001 From: RTLcoil Date: Thu, 4 Feb 2021 18:05:08 +0200 Subject: [PATCH 452/592] Fix test name in `ExpressionTest` (#233) --- .../java/com/cloudinary/transformation/ExpressionTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java index 733f1a2a..69615769 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/ExpressionTest.java @@ -80,7 +80,7 @@ public void normalize_width_recognizedAsVariableAndReplacedWithW() { } @Test - public void normalize_initialAspectRatio_recognizedAsVariableAndReplacedWithW() { + public void normalize_initialAspectRatio_recognizedAsVariableAndReplacedWithIar() { String result = Expression.normalize("initial_aspect_ratio"); assertEquals("iar", result); } @@ -174,4 +174,4 @@ public void normalize_doesntReplaceVariable_8() { String actual = Expression.normalize("$__height_100"); assertEquals("$_height_100", actual); } -} \ No newline at end of file +} From f66644b90036ae8225bba70a0e63728057d6003f Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Tue, 9 Feb 2021 18:27:39 +0200 Subject: [PATCH 453/592] Add support for apache http-client 4.5 (#234) --- .travis.yml | 1 + .../main/java/com/cloudinary/Cloudinary.java | 6 +- cloudinary-http45/build.gradle | 120 ++++++++++ .../com/cloudinary/http45/ApiStrategy.java | 205 ++++++++++++++++++ .../java/com/cloudinary/http45/ApiUtils.java | 53 +++++ .../cloudinary/http45/UploaderStrategy.java | 139 ++++++++++++ .../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 +++ settings.gradle | 1 + 16 files changed, 683 insertions(+), 2 deletions(-) create mode 100644 cloudinary-http45/build.gradle create mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java create mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java create mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java create mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java create mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java diff --git a/.travis.yml b/.travis.yml index 72169c8a..d71e7b1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ env: - MODULE=http42 - MODULE=http43 - MODULE=http44 + - MODULE=http45 branches: except: diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1ed4521b..1cf44999 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -23,12 +23,14 @@ public class Cloudinary { "com.cloudinary.android.UploaderStrategy", "com.cloudinary.http42.UploaderStrategy", "com.cloudinary.http43.UploaderStrategy", - "com.cloudinary.http44.UploaderStrategy")); + "com.cloudinary.http44.UploaderStrategy", + "com.cloudinary.http45.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.http44.ApiStrategy", + "com.cloudinary.http45.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-http45/build.gradle b/cloudinary-http45/build.gradle new file mode 100644 index 00000000..13be8f83 --- /dev/null +++ b/cloudinary-http45/build.gradle @@ -0,0 +1,120 @@ +plugins { + id 'java-library' + id 'signing' + id 'maven-publish' + id 'io.codearte.nexus-staging' version '0.21.1' + id "de.marcphilipp.nexus-publish" version "0.4.0" +} + +apply from: "../java_shared.gradle" + +task ciTest( type: Test ) { + useJUnit { + excludeCategories 'com.cloudinary.test.TimeoutTest' + } +} + +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' + } + } + } + } + + nexusPublishing { + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } + } + } + + 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 diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java new file mode 100644 index 00000000..e7c55bf8 --- /dev/null +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java @@ -0,0 +1,205 @@ +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(Cloudinary.USER_AGENT + " 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 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); + + validateAuthorization(apiKey, apiSecret, oauthToken); + + + String apiUrl = createApiUrl(uri, prefix, cloudName); + 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 new file mode 100644 index 00000000..6639b9cc --- /dev/null +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java @@ -0,0 +1,53 @@ +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 new file mode 100644 index 00000000..e29b07ad --- /dev/null +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java @@ -0,0 +1,139 @@ +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.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.USER_AGENT + " 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); + 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 new file mode 100644 index 00000000..6cb29455 --- /dev/null +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java @@ -0,0 +1,69 @@ +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)"); + 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 new file mode 100644 index 00000000..573a12e5 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..c39a89e2 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java @@ -0,0 +1,32 @@ +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 new file mode 100644 index 00000000..4841e9f6 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java @@ -0,0 +1,5 @@ +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 new file mode 100644 index 00000000..971bcf39 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..16a4708c --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..6a2e8a31 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..8cc186f4 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..efbf9190 --- /dev/null +++ b/cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java @@ -0,0 +1,34 @@ +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/settings.gradle b/settings.gradle index c1342b86..174cb41d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,3 +5,4 @@ include ':cloudinary-test-common' include ':cloudinary-http42' include ':cloudinary-http43' include ':cloudinary-http44' +include ':cloudinary-http45' From 480518a01e966dfb0d4336a5131eab6a221a9f02 Mon Sep 17 00:00:00 2001 From: Asi Sayag <42962333+asisayag2@users.noreply.github.com> Date: Wed, 10 Feb 2021 16:32:09 +0200 Subject: [PATCH 454/592] Allow setting the user agent (#235) --- .../main/java/com/cloudinary/Cloudinary.java | 22 +++++++++++++++++-- .../com/cloudinary/http42/ApiStrategy.java | 2 +- .../cloudinary/http42/UploaderStrategy.java | 2 +- .../com/cloudinary/http43/ApiStrategy.java | 2 +- .../cloudinary/http43/UploaderStrategy.java | 2 +- .../com/cloudinary/http44/ApiStrategy.java | 2 +- .../cloudinary/http44/UploaderStrategy.java | 2 +- .../com/cloudinary/http45/ApiStrategy.java | 2 +- .../cloudinary/http45/UploaderStrategy.java | 2 +- .../com/cloudinary/test/AbstractApiTest.java | 12 +++++++++- 10 files changed, 39 insertions(+), 11 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1cf44999..940e0d0f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -38,15 +38,16 @@ public class Cloudinary { public final static String SHARED_CDN = AKAMAI_SHARED_CDN; public final static String VERSION = "1.28.1"; - public final static String USER_AGENT = "CloudinaryJava/" + VERSION + " (Java " + System.getProperty("java.version") + ")"; + static String USER_AGENT_PREFIX = "CloudinaryJava"; + public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; public final Configuration config; private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; + private String userAgent = USER_AGENT_PREFIX+"/"+ VERSION + " "+USER_AGENT_JAVA_VERSION; public Uploader uploader() { return new Uploader(this, uploaderStrategy); - } public Api api() { @@ -135,6 +136,23 @@ public String apiSignRequest(Map paramsToSign, String apiSecret) return Util.produceSignature(paramsToSign, apiSecret, config.signatureAlgorithm); } + /** + * @return the userAgent that will be sent with every API call. + */ + public String getUserAgent(){ + return userAgent; + } + + /** + * Set the prefix and version for the user agent that will be sent with every API call + * a userAgent is built from `prefix/version (additional data)` + * @param prefix - the prefix of the userAgent to be set + * @param version - the version of the userAgent to be set + */ + public void setUserAgent(String prefix, String version){ + userAgent = prefix+"/"+ version + " ("+USER_AGENT_PREFIX+ " "+VERSION+") " + USER_AGENT_JAVA_VERSION; + } + /** * Verifies that Cloudinary notification request is genuine by checking its signature. * 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 a8aa7c93..6703448e 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -107,7 +107,7 @@ private ApiResponse getApiResponse(HttpMethod method, Map params, Str break; } request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); - request.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); + 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); diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index b34ae4ff..9984f015 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -60,7 +60,7 @@ public Map callApi(String action, Map params, Map options, Objec } HttpPost postMethod = new HttpPost(apiUrl); - postMethod.setHeader("User-Agent", Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.2"); + postMethod.setHeader("User-Agent", this.cloudinary().getUserAgent() + " ApacheHTTPComponents/4.2"); Map extraHeaders = (Map) options.get("extra_headers"); if (extraHeaders != null) { diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java index ef02ce17..04ac023e 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java @@ -47,7 +47,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.3"); + 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) { diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index 956e9bd7..bb748f8f 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -37,7 +37,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.3"); + 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) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java index df8eedb3..dcd34fe2 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java @@ -49,7 +49,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.4"); + 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) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index 20e18258..e075db29 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -38,7 +38,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.4"); + 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) { diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java index e7c55bf8..6b11cce9 100644 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java @@ -47,7 +47,7 @@ public void init(Api api) { super.init(api); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.5"); + 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) { diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java index e29b07ad..175653cf 100644 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java @@ -34,7 +34,7 @@ public void init(Uploader uploader) { super.init(uploader); HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.5"); + 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) { 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 c94468e7..0bc51d31 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 @@ -47,6 +47,9 @@ abstract public class AbstractApiTest extends MockableTest { public static final String TEST_KEY = "test-key" + SUFFIX; public static final String API_TEST_RESTORE = "api_test_restore" + SUFFIX; public static final Set createdFolders = new HashSet(); + private static final String CUSTOM_USER_AGENT_PREFIX = "TEST_USER_AGENT"; + private static final String CUSTOM_USER_AGENT_VERSION = "9.9.9"; + protected Api api; @@ -126,7 +129,6 @@ public static void tearDownClass() { } } catch (Exception ignored) { } - } @Rule @@ -151,6 +153,14 @@ public Map findByAttr(List elements, String attr, Object value) { return null; } + @Test + public void testCustomUserAgent() throws Exception { + // should allow setting a custom user-agent + cloudinary.setUserAgent(CUSTOM_USER_AGENT_PREFIX, CUSTOM_USER_AGENT_VERSION); + Map results = api.ping(ObjectUtils.emptyMap()); + //TODO Mock server and assert the header + } + @Test public void test01ResourceTypes() throws Exception { // should allow listing resource_types From 97a54d2e02c441bb8b5b31a03ed48a52d3bad1d6 Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Wed, 10 Feb 2021 17:12:49 +0200 Subject: [PATCH 455/592] Version 1.29.0 --- CHANGELOG.md | 15 ++++++++++++++- README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5cfd8dd..0350cb4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,23 @@ +1.29.0 / 2021-02-10 +=================== + +New functionality +----------------- + * Allow setting the user agent (#235) + * Add support for Apache http-client 4.5 (#234) + +Other changes +------------- + * Fix test name in `ExpressionTest` (#233) + + 1.28.1 / 2021-02-03 ================== * Fix `api` reuse bug when calling `cloudinary.search()` (#232) -1.28.1 / 2021-02-01 +1.28.0 / 2021-02-01 ================== * Add `oauth` support to Admin Api calls. (#230) diff --git a/README.md b/README.md index a9d61ce0..ac8d9ee5 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. com.cloudinary cloudinary-http44 - 1.28.1 + 1.29.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.28.1/cloudinary-core-1.28.1.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.28.1/cloudinary-http44-1.28.1.jar) +Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.29.0/cloudinary-core-1.29.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.29.0/cloudinary-http44-1.29.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. ## Try it right away diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 940e0d0f..75cb5100 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -37,7 +37,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.28.1"; + public final static String VERSION = "1.29.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 484fccbd..ef9fd370 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.28.1 +version=1.29.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 8061519d940b19d1cda13c347e841890764e7f58 Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Mon, 3 May 2021 14:29:39 +0300 Subject: [PATCH 456/592] updated expected text that returns from the server --- .../com/cloudinary/test/AbstractStructuredMetadataTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e073e442..7b06c557 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 @@ -204,7 +204,7 @@ public void testExplicitWithMetadata() throws Exception { message = e.getMessage(); } - assertTrue(message.contains("Value 12 is invalid for field") ); + assertTrue(message.contains("is not valid for field") ); } @Test From eac25a15db12fc096d768f7fec27caa44095fc8a Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Tue, 11 May 2021 14:21:28 +0300 Subject: [PATCH 457/592] Add test for double underscore in layers --- .../src/test/java/com/cloudinary/transformation/LayerTest.java | 2 ++ 1 file changed, 2 insertions(+) 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 ad5e6e17..155ff091 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -64,6 +64,8 @@ public void testLayerOptions() { Object tests[] = { new Layer().publicId("logo"), "logo", + new Layer().publicId("logo__111"), //testing SNI-4729 + "logo__111", new Layer().publicId("folder/logo"), "folder:logo", new Layer().publicId("logo").type("private"), From 6481c37c5ab75249e6141e6184029f74e27ac1e9 Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Tue, 11 May 2021 14:22:19 +0300 Subject: [PATCH 458/592] Added users cleanup on account testing to avoid "too many users" errors. --- .../test/AbstractAccountApiTest.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) 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 549f4543..b1a5ac0c 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 @@ -151,8 +151,10 @@ public void testDeleteSubAccount() throws Exception { @Test public void testGetUser() throws Exception { ApiResponse user = createUser(); - ApiResponse result = account.user(user.get("id").toString(), null); + String id = user.get("id").toString(); + ApiResponse result = account.user(id, null); assertNotNull(result); + deleteUser(id); } @Test @@ -171,6 +173,9 @@ public void testGetUsers() throws Exception { assertTrue("User1 id should be in the result set", returnedIds.contains(id1)); assertTrue("User2 id should be in the result set", returnedIds.contains(id2)); + deleteUser(id1); + deleteUser(id2); + } @Test @@ -183,11 +188,12 @@ public void testCreateUser() throws Exception { @Test public void testUpdateUser() throws Exception { ApiResponse user = createUser(Account.Role.ADMIN); - + String id = user.get("id").toString(); String newName = randomLetters(); - ApiResponse result = account.updateUser(user.get("id").toString(), newName, null, null, null, null); + ApiResponse result = account.updateUser(id, newName, null, null, null, null); assertNotNull(result); assertEquals(result.get("name"), newName); + deleteUser(id); } @Test @@ -228,8 +234,10 @@ public void testDeleteUserGroup() throws Exception { public void testAddUserToUserGroup() throws Exception { ApiResponse user = createUser(); ApiResponse group = createGroup(); - ApiResponse result = account.addUserToGroup(group.get("id").toString(), user.get("id").toString(), null); + String userId = user.get("id").toString(); + ApiResponse result = account.addUserToGroup(group.get("id").toString(), userId, null); assertNotNull(result); + deleteUser(userId); } @Test @@ -241,6 +249,7 @@ public void testRemoveUserFromUserGroup() throws Exception { account.addUserToGroup(groupId, userId, null); ApiResponse result = account.removeUserFromGroup(groupId, userId, null); assertNotNull(result); + deleteUser(userId); } @Test @@ -271,6 +280,8 @@ public void testListUsersInGroup() throws Exception { ApiResponse result = account.userGroupUsers(groupId, null); assertNotNull(result); assertTrue(((List) result.get("users")).size() >= 2); + deleteUser(user1Id); + deleteUser(user2Id); } @@ -280,7 +291,6 @@ private ApiResponse createGroup() throws Exception { ApiResponse userGroup = account.createUserGroup(name); createdGroupIds.add(userGroup.get("id").toString()); return userGroup; - } private ApiResponse createUser(Account.Role role) throws Exception { @@ -313,7 +323,15 @@ private static String randomLetters() { for (int i = 0; i < 10; i++) { sb.append((char) ('a' + rand.nextInt('z' - 'a' + 1))); } - return sb.toString(); } + + private void deleteUser(String userId) { + try { + account.deleteUser(userId, null); + createdUserIds.remove(userId); + } catch (Exception e) { + e.printStackTrace(); + } + } } From d2f66adae5eff63bc581d19b8abc4f08960f274e Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Wed, 12 May 2021 16:36:25 +0300 Subject: [PATCH 459/592] Remove unneeded comment --- .../src/test/java/com/cloudinary/transformation/LayerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 155ff091..dba71ee7 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -64,7 +64,7 @@ public void testLayerOptions() { Object tests[] = { new Layer().publicId("logo"), "logo", - new Layer().publicId("logo__111"), //testing SNI-4729 + new Layer().publicId("logo__111"), "logo__111", new Layer().publicId("folder/logo"), "folder:logo", From 2f29d8ee94354282e9823683993569a508c53c5c Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Sat, 15 May 2021 00:01:35 +0300 Subject: [PATCH 460/592] Removed the local users clean-up --- .../test/AbstractAccountApiTest.java | 18 ------------------ 1 file changed, 18 deletions(-) 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 b1a5ac0c..b8e4f549 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 @@ -154,7 +154,6 @@ public void testGetUser() throws Exception { String id = user.get("id").toString(); ApiResponse result = account.user(id, null); assertNotNull(result); - deleteUser(id); } @Test @@ -173,9 +172,6 @@ public void testGetUsers() throws Exception { assertTrue("User1 id should be in the result set", returnedIds.contains(id1)); assertTrue("User2 id should be in the result set", returnedIds.contains(id2)); - deleteUser(id1); - deleteUser(id2); - } @Test @@ -193,7 +189,6 @@ public void testUpdateUser() throws Exception { ApiResponse result = account.updateUser(id, newName, null, null, null, null); assertNotNull(result); assertEquals(result.get("name"), newName); - deleteUser(id); } @Test @@ -237,7 +232,6 @@ public void testAddUserToUserGroup() throws Exception { String userId = user.get("id").toString(); ApiResponse result = account.addUserToGroup(group.get("id").toString(), userId, null); assertNotNull(result); - deleteUser(userId); } @Test @@ -249,7 +243,6 @@ public void testRemoveUserFromUserGroup() throws Exception { account.addUserToGroup(groupId, userId, null); ApiResponse result = account.removeUserFromGroup(groupId, userId, null); assertNotNull(result); - deleteUser(userId); } @Test @@ -280,8 +273,6 @@ public void testListUsersInGroup() throws Exception { ApiResponse result = account.userGroupUsers(groupId, null); assertNotNull(result); assertTrue(((List) result.get("users")).size() >= 2); - deleteUser(user1Id); - deleteUser(user2Id); } @@ -325,13 +316,4 @@ private static String randomLetters() { } return sb.toString(); } - - private void deleteUser(String userId) { - try { - account.deleteUser(userId, null); - createdUserIds.remove(userId); - } catch (Exception e) { - e.printStackTrace(); - } - } } From 73751bebd9f505e419131d21ac155adfde7fc3cf Mon Sep 17 00:00:00 2001 From: asisayag2 Date: Mon, 2 Aug 2021 14:50:55 +0300 Subject: [PATCH 461/592] Added users deletion at the end of tests --- .../test/AbstractAccountApiTest.java | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) 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 b8e4f549..a5d00fcb 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 @@ -38,6 +38,7 @@ public void setUp() throws Exception { @AfterClass public static void tearDownClass() { + System.out.println("Start TearDownClass"); Account account = new Account(new Cloudinary()); for (String createdSubAccountId : createdSubAccountIds) { try { @@ -62,6 +63,7 @@ public static void tearDownClass() { e.printStackTrace(); } } + System.out.println("### Deleted - SubAccounts:"+createdSubAccountIds.size()+", Users:"+createdUserIds.size()+ ", UserGroups:"+createdGroupIds.size()); } @Test @@ -151,16 +153,18 @@ public void testDeleteSubAccount() throws Exception { @Test public void testGetUser() throws Exception { ApiResponse user = createUser(); - String id = user.get("id").toString(); - ApiResponse result = account.user(id, null); + String userId = user.get("id").toString(); + ApiResponse result = account.user(userId, null); + assertNotNull(result); + deleteUser(userId); } @Test public void testGetUsers() throws Exception { - String id1 = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); - String id2 = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); - ApiResponse result = account.users(null, Arrays.asList(id1, id2), null, null, null); + String user1Id = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); + String user2Id = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); + ApiResponse result = account.users(null, Arrays.asList(user1Id, user2Id), null, null, null); assertNotNull(result); final ArrayList users = (ArrayList) result.get("users"); ArrayList returnedIds = new ArrayList(2); @@ -170,8 +174,10 @@ public void testGetUsers() throws Exception { returnedIds.add(((Map) users.get(0)).get("id").toString()); returnedIds.add(((Map) users.get(1)).get("id").toString()); - assertTrue("User1 id should be in the result set", returnedIds.contains(id1)); - assertTrue("User2 id should be in the result set", returnedIds.contains(id2)); + assertTrue("User1 id should be in the result set", returnedIds.contains(user1Id)); + assertTrue("User2 id should be in the result set", returnedIds.contains(user2Id)); + deleteUser(user1Id); + deleteUser(user2Id); } @Test @@ -184,11 +190,13 @@ public void testCreateUser() throws Exception { @Test public void testUpdateUser() throws Exception { ApiResponse user = createUser(Account.Role.ADMIN); - String id = user.get("id").toString(); + String userId = user.get("id").toString(); String newName = randomLetters(); - ApiResponse result = account.updateUser(id, newName, null, null, null, null); + ApiResponse result = account.updateUser(userId, newName, null, null, null, null); + assertNotNull(result); assertEquals(result.get("name"), newName); + deleteUser(userId); } @Test @@ -232,6 +240,7 @@ public void testAddUserToUserGroup() throws Exception { String userId = user.get("id").toString(); ApiResponse result = account.addUserToGroup(group.get("id").toString(), userId, null); assertNotNull(result); + deleteUser(userId); } @Test @@ -243,6 +252,7 @@ public void testRemoveUserFromUserGroup() throws Exception { account.addUserToGroup(groupId, userId, null); ApiResponse result = account.removeUserFromGroup(groupId, userId, null); assertNotNull(result); + deleteUser(userId); } @Test @@ -273,6 +283,8 @@ public void testListUsersInGroup() throws Exception { ApiResponse result = account.userGroupUsers(groupId, null); assertNotNull(result); assertTrue(((List) result.get("users")).size() >= 2); + deleteUser(user1Id); + deleteUser(user2Id); } @@ -298,11 +310,21 @@ private ApiResponse createUser(List subAccountsIds) throws Exception { private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { String email = String.format("%s@%s.com", randomLetters(), randomLetters()); - ApiResponse user = account.createUser("TestName", email, role, subAccountsIds, null); + ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); return user; } + private void deleteUser(String userId){ + try { + account.deleteUser(userId, null); + createdUserIds.remove(userId); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private ApiResponse createSubAccount() throws Exception { ApiResponse subAccount = account.createSubAccount(randomLetters(), null, emptyMap(), true, null); createdSubAccountIds.add(subAccount.get("id").toString()); From 652dd201458e2693c032b6e3466d9a40ef1b4ec4 Mon Sep 17 00:00:00 2001 From: patrick-tolosa <59408474+patrick-tolosa@users.noreply.github.com> Date: Sun, 19 Sep 2021 11:52:33 +0300 Subject: [PATCH 462/592] Update Configuration.java --- cloudinary-core/src/main/java/com/cloudinary/Configuration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 18e813ce..199071f2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -312,7 +312,7 @@ public static class Builder { /** * Set the HTTP connection timeout. * - * @param timeout time in milliseconds, or 0 to use the default platform value + * @param timeout time in seconds, or 0 to use the default platform value * @return builder for chaining */ public Builder setTimeout(int timeout) { From 4fffc8b8aef4c0757ffd662ad2b23bb25b69b7d4 Mon Sep 17 00:00:00 2001 From: patrick-tolosa <59408474+patrick-tolosa@users.noreply.github.com> Date: Thu, 14 Oct 2021 17:11:30 +0300 Subject: [PATCH 463/592] Add email notification to Travis build --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index d71e7b1e..15fbae01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,3 +31,9 @@ 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 -DCLOUDINARY_ACCOUNT_URL=$CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i + + +notifications: + email: + recipients: + - sdk_developers@cloudinary.com From df7b3cca08c165a2fc28dc9ef72e9db2520b6009 Mon Sep 17 00:00:00 2001 From: Asi Sayag <42962333+asisayag2@users.noreply.github.com> Date: Wed, 27 Oct 2021 18:29:03 +0300 Subject: [PATCH 464/592] Fix a bug where a publicId which contains 'v[num]' is considered to contain a version, therefore the version is skipped. (#242) --- .../src/main/java/com/cloudinary/Url.java | 2 +- .../com/cloudinary/utils/StringUtils.java | 25 ++++---------- .../test/java/com/cloudinary/UtilTest.java | 33 ++++++++++++------- .../com/cloudinary/test/CloudinaryTest.java | 8 +++++ java_shared.gradle | 4 +-- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index fad9ece8..e8cc7e38 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -377,7 +377,7 @@ public String generate(String source) { source = finalizedSource[0]; String sourceToSign = finalizedSource[1]; - if (this.config.forceVersion && sourceToSign.contains("/") && !StringUtils.hasVersionString(sourceToSign) && + if (this.config.forceVersion && sourceToSign.contains("/") && !StringUtils.startWithVersionString(sourceToSign) && !httpSource && StringUtils.isEmpty(version)) { version = "1"; } 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 73ddf45d..3149e018 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -340,26 +340,15 @@ public static String removeStartingChars(String s, char c) { } /** - * Checks whether the url contains a versioning string (v + number, e.g. v12345) - * @param url The url to check - * @return Whether a version string is contained within the url + * Checks whether a publicId starts a versioning string (v + number, e.g. v12345) + * @param publicId The url to check + * @return Whether a version string is contained within the publicId */ - public static boolean hasVersionString(String url) { - boolean inVersion = false; - for (int i = 0; i < url.length(); i++) { - char c = url.charAt(i); - if (c == 'v') { - inVersion = true; - } else if (Character.isDigit(c) && inVersion) { - return true; - } else { - inVersion = false; - } - - + public static boolean startWithVersionString(String publicId){ + if (publicId.startsWith("/")){ + publicId = publicId.substring(1); } - - return false; + return publicId.length()>1 && publicId.startsWith("v") && Character.isDigit(publicId.charAt(1)); } /** diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java index a8c7b0ba..8f8f5e15 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -129,18 +129,27 @@ public void testMergeSlashes(){ } @Test - public void testHasVersionString(){ - assertTrue(StringUtils.hasVersionString("wqeasdlv31423423")); - assertTrue(StringUtils.hasVersionString("v1")); - assertTrue(StringUtils.hasVersionString("v1fdasfasd")); - assertTrue(StringUtils.hasVersionString("asdasv1fdasfasd")); - assertTrue(StringUtils.hasVersionString("12v1fdasfasd")); - - assertFalse(StringUtils.hasVersionString("121fdasfasd")); - assertFalse(StringUtils.hasVersionString("")); - assertFalse(StringUtils.hasVersionString("vvv")); - assertFalse(StringUtils.hasVersionString("v")); - assertFalse(StringUtils.hasVersionString("asdvvv")); + public void testStartWithVersionString(){ + assertTrue(StringUtils.startWithVersionString("v1")); + assertTrue(StringUtils.startWithVersionString("v1fdasfasd")); + assertTrue(StringUtils.startWithVersionString("v112fdasfasd")); + assertTrue(StringUtils.startWithVersionString("v112/fda/sfasd")); + assertTrue(StringUtils.startWithVersionString("v112/fda/v1sfasd")); + assertTrue(StringUtils.startWithVersionString("/v112/fda/v1sfasd")); + + assertFalse(StringUtils.startWithVersionString("asdasv1fdasfasd")); + assertFalse(StringUtils.startWithVersionString("12v1fdasfasd")); + assertFalse(StringUtils.startWithVersionString("asdasv1fdasfasd")); + assertFalse(StringUtils.startWithVersionString("wqeasdlv31423423")); + assertFalse(StringUtils.startWithVersionString("wqeasdl/v31423423")); + assertFalse(StringUtils.startWithVersionString("121fdasfasd")); + assertFalse(StringUtils.startWithVersionString("/121fdasfasd")); + assertFalse(StringUtils.startWithVersionString("/")); + assertFalse(StringUtils.startWithVersionString("/v")); + assertFalse(StringUtils.startWithVersionString("")); + assertFalse(StringUtils.startWithVersionString("vvv")); + assertFalse(StringUtils.startWithVersionString("v")); + assertFalse(StringUtils.startWithVersionString("asdvvv")); } @Test 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 e0875980..109b44ed 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -636,6 +636,14 @@ public void testFoldersWithExcludeVersion() { result = cloudinary.url().generate("folder/test"); assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); + // should not add version if the path STARTS with 'v[num]' + result = cloudinary.url().generate("v1234/folder/test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1234/folder/test", result); + + // should add version if the path CONTAINS 'v[num]' + result = cloudinary.url().generate("folder/v123test"); + assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/v123test", result); + // should add version if forceVersion is true result = cloudinary.url().forceVersion(true).generate("folder/test"); assertEquals(DEFAULT_UPLOAD_PATH + "v1/folder/test", result); diff --git a/java_shared.gradle b/java_shared.gradle index b9396ad2..134636f7 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -1,5 +1,5 @@ -sourceCompatibility = 1.6 -targetCompatibility = 1.6 +sourceCompatibility = 1.7 +targetCompatibility = 1.7 javadoc { options.encoding = 'UTF-8' From 2d8c1ebdb20ae01b43766d6100556eefb177edf6 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 23 Jan 2022 15:34:10 +0200 Subject: [PATCH 465/592] Add analytics support to url-gen --- .../src/main/java/com/cloudinary/Cloudinary.java | 9 +++++++++ .../src/main/java/com/cloudinary/Url.java | 4 ++++ .../com/cloudinary/utils/AnalyticsUtils.java | 6 ++++++ .../java/com/cloudinary/test/CloudinaryTest.java | 16 ++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 75cb5100..18739ca0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -5,6 +5,7 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.StrategyLoader; +import com.cloudinary.utils.AnalyticsUtils; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -153,6 +154,14 @@ public void setUserAgent(String prefix, String version){ userAgent = prefix+"/"+ version + " ("+USER_AGENT_PREFIX+ " "+VERSION+") " + USER_AGENT_JAVA_VERSION; } + /** + * Set the analytics token that will be sent with every URL generate call + * @param token - the analytics token to be set + */ + public void setAnalyticsToken(String token) { + AnalyticsUtils.token = token; + } + /** * Verifies that Cloudinary notification request is genuine by checking its signature. * diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index e8cc7e38..9ead079f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; import java.util.zip.CRC32; +import com.cloudinary.utils.AnalyticsUtils; import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -417,6 +418,9 @@ public String generate(String source) { } catch (MalformedURLException ignored) { } } + if (AnalyticsUtils.token != null) { + url = (new StringBuilder()).append(url).append(AnalyticsUtils.analyticsPrefix).append(AnalyticsUtils.token).toString(); + } return url; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java new file mode 100644 index 00000000..3939c760 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java @@ -0,0 +1,6 @@ +package com.cloudinary.utils; + +public class AnalyticsUtils { + public static String analyticsPrefix = "?_a="; + public static String token = null; +} 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 109b44ed..a7cf8898 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -2,6 +2,7 @@ import com.cloudinary.*; import com.cloudinary.transformation.*; +import com.cloudinary.utils.AnalyticsUtils; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -1454,4 +1455,19 @@ private void setRandomValue(Random rand, Field field, Object instance) throws Il private > T randomEnum(Class clazz, Random random) { return clazz.getEnumConstants()[random.nextInt(clazz.getEnumConstants().length)]; } + + @Test + public void testUrlWithAnalytics() { + AnalyticsUtils.token = "AFAACAI0"; + String url = cloudinary.url().generate("test"); + assertEquals(url,"http://res.cloudinary.com/test123/image/upload/test?_a=AFAACAI0"); + AnalyticsUtils.token = null; + } + + @Test + public void testUrlWithNoAnalytics() { + AnalyticsUtils.token = null; + String url = cloudinary.url().generate("test"); + assertEquals(url,"http://res.cloudinary.com/test123/image/upload/test"); + } } From b650f8e95ab3328b4b387eefba98d9905675dd53 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 24 Jan 2022 14:05:54 +0200 Subject: [PATCH 466/592] Add support to analytics if query params already exist --- .../src/main/java/com/cloudinary/Url.java | 4 +++- .../java/com/cloudinary/utils/AnalyticsUtils.java | 15 +++++++++++++++ .../test/java/com/cloudinary/AuthTokenTest.java | 10 ++++++++++ .../java/com/cloudinary/test/CloudinaryTest.java | 2 ++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 9ead079f..7862d0b4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -419,7 +419,9 @@ public String generate(String source) { } } if (AnalyticsUtils.token != null) { - url = (new StringBuilder()).append(url).append(AnalyticsUtils.analyticsPrefix).append(AnalyticsUtils.token).toString(); + if(!AnalyticsUtils.checkIfQueryParamExist(url)) { + url = (new StringBuilder()).append(url).append(AnalyticsUtils.analyticsPrefix).append(AnalyticsUtils.token).toString(); + } } return url; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java index 3939c760..c055dc96 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java @@ -1,6 +1,21 @@ package com.cloudinary.utils; +import java.net.MalformedURLException; +import java.net.URL; + public class AnalyticsUtils { public static String analyticsPrefix = "?_a="; public static String token = null; + + public static Boolean checkIfQueryParamExist(String urlString) { + try { + URL url = new URL(urlString); + if (url.getQuery() == null) { + return false; + } + } catch (MalformedURLException e) { + return true; + } + return true; + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 90b278ee..f97e2e88 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -1,5 +1,6 @@ package com.cloudinary; +import com.cloudinary.utils.AnalyticsUtils; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; @@ -92,6 +93,15 @@ public void testAuthenticatedUrl() { } + @Test + public void testUrlAnalyticsWithQueryParams() { + 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"); + AnalyticsUtils.token = null; + cloudinary.config.privateCdn = false; + } + @Test public void testConfiguration() { cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&auth_token[key]=aabbcc112233&auth_token[duration]=200"); 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 a7cf8898..15cb52d7 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1470,4 +1470,6 @@ public void testUrlWithNoAnalytics() { String url = cloudinary.url().generate("test"); assertEquals(url,"http://res.cloudinary.com/test123/image/upload/test"); } + + } From ea3c262edce887ab6a02f0ced485d366539930fa Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 30 Jan 2022 16:30:58 +0200 Subject: [PATCH 467/592] Add Analytics support --- .../main/java/com/cloudinary/Cloudinary.java | 13 +- .../java/com/cloudinary/Configuration.java | 20 +++- .../src/main/java/com/cloudinary/Url.java | 14 ++- .../java/com/cloudinary/utils/Analytics.java | 113 ++++++++++++++++++ .../com/cloudinary/utils/AnalyticsUtils.java | 21 ---- .../java/com/cloudinary/utils/Base64Map.java | 76 ++++++++++++ .../com/cloudinary/utils/StringUtils.java | 56 ++++++++- .../java/com/cloudinary/AuthTokenTest.java | 5 +- .../cloudinary/analytics/AnalyticsTest.java | 93 ++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 18 --- java_shared.gradle | 1 + 11 files changed, 373 insertions(+), 57 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java create mode 100644 cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 18739ca0..8b0b462a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -5,7 +5,7 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.strategies.AbstractUploaderStrategy; import com.cloudinary.strategies.StrategyLoader; -import com.cloudinary.utils.AnalyticsUtils; +import com.cloudinary.utils.Analytics; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -46,7 +46,7 @@ public class Cloudinary { private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; private String userAgent = USER_AGENT_PREFIX+"/"+ VERSION + " "+USER_AGENT_JAVA_VERSION; - + public Analytics analytics; public Uploader uploader() { return new Uploader(this, uploaderStrategy); } @@ -155,11 +155,12 @@ public void setUserAgent(String prefix, String version){ } /** - * Set the analytics token that will be sent with every URL generate call - * @param token - the analytics token to be set + * Set the prefix and version for the user agent that will be sent with every API call + * a userAgent is built from `prefix/version (additional data)` + * @param analytics - the analytics objec to set */ - public void setAnalyticsToken(String token) { - AnalyticsUtils.token = token; + public void setAnalytics(Analytics analytics) { + this.analytics = analytics; } /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 199071f2..9d3cc039 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -48,7 +48,7 @@ public class Configuration { public boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; public SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; public String oauthToken = null; - + public Boolean analytics; public Configuration() { } @@ -73,7 +73,8 @@ private Configuration( boolean forceVersion, boolean longUrlSignature, SignatureAlgorithm signatureAlgorithm, - String oauthToken) { + String oauthToken, + boolean analytics) { this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; @@ -95,6 +96,7 @@ private Configuration( this.longUrlSignature = longUrlSignature; this.signatureAlgorithm = signatureAlgorithm; this.oauthToken = oauthToken; + this.analytics = analytics; } @SuppressWarnings("rawtypes") @@ -122,6 +124,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); Map tokenMap = (Map) config.get("auth_token"); if (tokenMap != null) { this.authToken = new AuthToken(tokenMap); @@ -134,6 +137,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.oauthToken = (String) config.get("oauth_token"); + } @SuppressWarnings("rawtypes") @@ -165,6 +169,7 @@ public Map asMap() { map.put("long_url_signature", longUrlSignature); map.put(CONFIG_PROP_SIGNATURE_ALGORITHM, signatureAlgorithm.toString()); map.put("oauth_token", oauthToken); + map.put("analytics", analytics); return map; } @@ -196,6 +201,7 @@ public Configuration(Configuration other) { this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; this.oauthToken = other.oauthToken; + this.analytics = other.analytics; } /** @@ -308,6 +314,7 @@ public static class Builder { private boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; private SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; private String oauthToken = null; + private boolean analytics; /** * Set the HTTP connection timeout. @@ -345,7 +352,8 @@ public Configuration build() { forceVersion, longUrlSignature, signatureAlgorithm, - oauthToken); + oauthToken, + analytics); configuration.clientHints = clientHints; return configuration; } @@ -450,6 +458,11 @@ public Builder setLoadStrategies(boolean loadStrategies) { return this; } + public Builder setAnalytics(boolean analytics) { + this.analytics = analytics; + return this; + } + public Builder setClientHints(boolean clientHints) { this.clientHints = clientHints; return this; @@ -509,6 +522,7 @@ public Builder from(Configuration other) { this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; this.oauthToken = other.oauthToken; + this.analytics = other.analytics; return this; } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 7862d0b4..d02d5a07 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -13,7 +13,7 @@ import java.util.regex.Pattern; import java.util.zip.CRC32; -import com.cloudinary.utils.AnalyticsUtils; +import com.cloudinary.utils.Analytics; import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -409,6 +409,7 @@ public String generate(String source) { String join = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/"); String url = StringUtils.mergeSlashesInUrl(join); + if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { try { URL tempUrl = new URL(url); @@ -418,9 +419,14 @@ public String generate(String source) { } catch (MalformedURLException ignored) { } } - if (AnalyticsUtils.token != null) { - if(!AnalyticsUtils.checkIfQueryParamExist(url)) { - url = (new StringBuilder()).append(url).append(AnalyticsUtils.analyticsPrefix).append(AnalyticsUtils.token).toString(); + if (cloudinary.analytics != null && cloudinary.config.analytics) { + try { + URL tempUrl = new URL(url); + if (tempUrl.getQuery() == null) { + String path = tempUrl.getPath(); + url = url + "?" + cloudinary.analytics.toQueryParam(); + } + } catch (MalformedURLException ignored) { } } return url; diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java new file mode 100644 index 00000000..e81ec716 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -0,0 +1,113 @@ +package com.cloudinary.utils; + +import java.util.Arrays; +import java.util.List; + +public class Analytics { + private String sdkTokenQueryKey = "_a="; //sdkTokenQueryKey + public String algoVersion = "A"; + public String SDKCode = ""; // Java = G, Android = F + public String SDKSemver = ""; // Calculate the SDK version . + public String techVersion = ""; // Calculate the Java version. + public void Analytics() { + // initilaize with Java values. + // From Android pick the values. + } + + public Analytics() {} + public Analytics(String sdkCode, String sdkVersion, String techVersion) { + this.SDKCode = sdkCode; + this.SDKSemver = sdkVersion; + this.techVersion = techVersion; + } + + public Analytics setSDKCode(String SDKCode) { + this.SDKCode = SDKCode; + return this; + } + + public Analytics setSDKSemver(String SDKSemver) { + this.SDKSemver = SDKSemver; + return this; + } + + public Analytics setTechVersion(String techVersion) { + this.techVersion = techVersion; + return this; + } + + /** + * Function turn analytics variables into viable query parameter. + * @return query param with analytics values. + */ + public String toQueryParam() { + try { + return sdkTokenQueryKey + getAlgorithmVersion() + getSDKType() + getSDKVersion() + getTechVersion() + getSDKFeatureCode(); + } catch (Exception e) { + return sdkTokenQueryKey + "E"; + } + } + + private String getTechVersion() throws Exception { + String[] techVersionString = techVersion.split("_"); + String[] versions = techVersionString[0].split("\\."); + if (versions.length > 2) { + versions = Arrays.copyOf(versions, versions.length - 1); + } + return getPaddedString(StringUtils.join(versions, ".")); + } + + private String getSDKType() { + return SDKCode; + } + + private String getAlgorithmVersion() { + return "A"; + } + + private String getSDKFeatureCode() { + return "0"; + } + + private String getSDKVersion() throws Exception { + return getPaddedString(SDKSemver); + } + + private String getPaddedString(String string) throws Exception { + String paddedReversedSemver = ""; + int parts = string.split("\\.").length; + int paddedStringLength = parts * 6; + try { + paddedReversedSemver = reverseVersion(string); + } catch (Exception e) { + throw new Exception("Error"); + } + int num = Integer.parseInt(StringUtils.join(paddedReversedSemver.split("\\."),"")); + + String paddedBinary = StringUtils.padStart(Integer.toBinaryString(num), paddedStringLength, '0'); + + if (paddedBinary.length() % 6 != 0) { + throw new Exception("Error"); + } + + String result = ""; + List resultList = StringUtils.usingPattern(paddedBinary,6); + int i = 0; + while (i < resultList.size()) { + result = result + Base64Map.values.get(resultList.get(i)); + i++; + } + return result; + } + + private String reverseVersion(String SDKSemver) throws Exception { + if (SDKSemver.split("\\.").length < 2) { + throw new Exception("invalid semVer, must have at least two segments"); + } + String[] versionArray = SDKSemver.split("\\."); + for (int i = 0 ; i < versionArray.length; i ++) { + versionArray[i] = StringUtils.padStart(versionArray[i], 2, '0'); + } + return StringUtils.join(StringUtils.reverseStringArray(versionArray), "."); + } +} \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java deleted file mode 100644 index c055dc96..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/AnalyticsUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.cloudinary.utils; - -import java.net.MalformedURLException; -import java.net.URL; - -public class AnalyticsUtils { - public static String analyticsPrefix = "?_a="; - public static String token = null; - - public static Boolean checkIfQueryParamExist(String urlString) { - try { - URL url = new URL(urlString); - if (url.getQuery() == null) { - return false; - } - } catch (MalformedURLException e) { - return true; - } - return true; - } -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java new file mode 100644 index 00000000..d5e755f9 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java @@ -0,0 +1,76 @@ +package com.cloudinary.utils; + +import java.util.HashMap; +import java.util.Map; + +public class Base64Map { + public static Map values; + + static { + values = new HashMap<>(); + values.put("000000", "A"); + values.put("000001", "B"); + values.put("000010", "C"); + values.put("000011", "D"); + values.put("000100", "E"); + values.put("000101", "F"); + values.put("000110", "G"); + values.put("000111", "H"); + values.put("001000", "I"); + values.put("001001", "J"); + values.put("001010", "K"); + values.put("001011", "L"); + values.put("001100", "M"); + values.put("001101", "N"); + values.put("001110", "O"); + values.put("001111", "P"); + values.put("010000", "Q"); + values.put("010001", "R"); + values.put("010010", "S"); + values.put("010011", "T"); + values.put("010100", "U"); + values.put("010101", "V"); + values.put("010110", "W"); + values.put("010111", "X"); + values.put("011000", "Y"); + values.put("011001", "Z"); + values.put("011010", "a"); + values.put("011011", "b"); + values.put("011100", "c"); + values.put("011101", "d"); + values.put("011110", "e"); + values.put("011111", "f"); + values.put("100000","g"); + values.put("100001","h"); + values.put("100010","i"); + values.put("100011","j"); + values.put("100100","k"); + values.put("100101","l"); + values.put("100110","m"); + values.put("100111","n"); + values.put("101000","o"); + values.put("101001","p"); + values.put("101010","q"); + values.put("101011","r"); + values.put("101100","s"); + values.put("101101","t"); + values.put("101110","u"); + values.put("101111","v"); + values.put("110000","w"); + values.put("110001","x"); + values.put("110010","y"); + values.put("110011","z"); + values.put("110100","0"); + values.put("110101","1"); + values.put("110110","2"); + values.put("110111","3"); + values.put("111000","4"); + values.put("111001","5"); + values.put("111010","6"); + values.put("111011","7"); + values.put("111100","8"); + values.put("111101","9"); + values.put("111110","+"); + values.put("111111","/"); + } +} 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 3149e018..2388ed0d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -4,8 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -216,7 +215,7 @@ public static boolean isRemoteUrl(String file) { * Replaces the unsafe characters in url with url-encoded values. * This is based on {@link java.net.URLEncoder#encode(String, String)} * @param url The url to encode - * @param unsafe Regex pattern of unsafe caracters + * @param unsafe Regex pattern of unsafe characters * @param charset * @return An encoded url string */ @@ -397,4 +396,55 @@ public static String mergeSlashesInUrl(String url) { public static String emptyIfNull(String str) { return isEmpty(str) ? "" : str; } + + /** + * Returns an array of strings in reveresed order. + * + * @param strings array of strings + * @return reversed array of string or empty array, if the passed array is null or empty + */ + static String[] reverseStringArray(String[] strings) { + Collections.reverse(Arrays.asList(strings)); + return strings; + } + + /** + * Returns the padded string with requested character to the left with length equals to length param sent. + * + * @param inputString The string to process + * @param length The requested length to pad to + * @param paddingCharacter The requested character to pad with + * @return reversed array of string or empty array, if the passed array is null or empty + */ + public static String padStart(String inputString, int length, char paddingCharacter) { + if (inputString.length() >= length) { + return inputString; + } + StringBuilder sb = new StringBuilder(); + while (sb.length() < length - inputString.length()) { + sb.append(paddingCharacter); + } + sb.append(inputString); + + return sb.toString(); + } + + /** + * Break string into groups of n size strings + * + * @param text The string to process + * @param n Size of group + * @return List with all strings with group size n. + */ + public static List usingPattern(String text, int n) { + List results = new ArrayList<>(); + + Pattern pattern = Pattern.compile(".{1," + n + "}"); + Matcher matcher = pattern.matcher(text); + while (matcher.find()) { + String match = text.substring(matcher.start(), matcher.end()); + results.add(match); + } + return results; + } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index f97e2e88..b93f21b6 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -1,6 +1,6 @@ package com.cloudinary; -import com.cloudinary.utils.AnalyticsUtils; +import com.cloudinary.utils.Analytics; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; @@ -95,10 +95,11 @@ public void testAuthenticatedUrl() { @Test public void testUrlAnalyticsWithQueryParams() { + cloudinary.config.analytics = true; + cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"))); 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"); - AnalyticsUtils.token = null; cloudinary.config.privateCdn = false; } diff --git a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java new file mode 100644 index 00000000..84dd599b --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -0,0 +1,93 @@ +package com.cloudinary.analytics; + +import com.cloudinary.Cloudinary; +import com.cloudinary.utils.Analytics; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +public class AnalyticsTest { + + private Cloudinary cloudinary; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + } + + @Test + public void testToQueryParam() { + Analytics analytics = new Analytics("F", "2.0.0", "1.8.0"); + String result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AFAACMh0"); + } + + @Test + public void testUrlWithAnalytics() { + cloudinary.config.analytics = true; + cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0")); + String url = cloudinary.url().generate("test"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AFAACMh0"); + cloudinary.config.analytics = false; + cloudinary.analytics = null; + } + + @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; + cloudinary.analytics = null; + } + + @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"); + cloudinary.config.analytics = false; + cloudinary.analytics = null; + } + + @Test + public void testUrlWithNoAnalyticsNull() { + cloudinary.analytics = null; + String url = cloudinary.url().generate("test"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + cloudinary.config.analytics = false; + cloudinary.analytics = null; + } + + @Test + public void testUrlWithNoAnalyticsNullAndTrue() { + cloudinary.config.analytics = true; + cloudinary.analytics = null; + String url = cloudinary.url().generate("test"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + cloudinary.config.analytics = false; + cloudinary.analytics = null; + } + + @Test + public void testMiscAnalyticsObject() { + cloudinary.config.analytics = true; + Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0"); + String result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AZAlhAM0"); + } + + @Test + public void testErrorAnalytics() { + cloudinary.config.analytics = true; + Analytics analytics = new Analytics("Z", "1.24.0", "0"); + String result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=E"); + } + +} \ No newline at end of file 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 15cb52d7..109b44ed 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -2,7 +2,6 @@ import com.cloudinary.*; import com.cloudinary.transformation.*; -import com.cloudinary.utils.AnalyticsUtils; import com.cloudinary.utils.ObjectUtils; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -1455,21 +1454,4 @@ private void setRandomValue(Random rand, Field field, Object instance) throws Il private > T randomEnum(Class clazz, Random random) { return clazz.getEnumConstants()[random.nextInt(clazz.getEnumConstants().length)]; } - - @Test - public void testUrlWithAnalytics() { - AnalyticsUtils.token = "AFAACAI0"; - String url = cloudinary.url().generate("test"); - assertEquals(url,"http://res.cloudinary.com/test123/image/upload/test?_a=AFAACAI0"); - AnalyticsUtils.token = null; - } - - @Test - public void testUrlWithNoAnalytics() { - AnalyticsUtils.token = null; - String url = cloudinary.url().generate("test"); - assertEquals(url,"http://res.cloudinary.com/test123/image/upload/test"); - } - - } diff --git a/java_shared.gradle b/java_shared.gradle index 134636f7..f61e3fea 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -2,6 +2,7 @@ sourceCompatibility = 1.7 targetCompatibility = 1.7 javadoc { + failOnError false options.encoding = 'UTF-8' } From 39e45d4a4d9c75918dfab4a9c60a2a964bd490ca Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 31 Jan 2022 08:48:37 +0200 Subject: [PATCH 468/592] Add Analytics support --- .../main/java/com/cloudinary/Cloudinary.java | 4 ++-- .../java/com/cloudinary/utils/Analytics.java | 9 ++++---- .../com/cloudinary/utils/StringUtils.java | 2 +- .../cloudinary/analytics/AnalyticsTest.java | 21 +++++++------------ 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 8b0b462a..0bda8649 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -155,9 +155,9 @@ public void setUserAgent(String prefix, String version){ } /** - * Set the prefix and version for the user agent that will be sent with every API call + * Set the analytics object that will be sent with every URL generation call. * a userAgent is built from `prefix/version (additional data)` - * @param analytics - the analytics objec to set + * @param analytics - the analytics object to set */ public void setAnalytics(Analytics analytics) { this.analytics = analytics; diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index e81ec716..1a057e78 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -4,7 +4,8 @@ import java.util.List; public class Analytics { - private String sdkTokenQueryKey = "_a="; //sdkTokenQueryKey + private String sdkTokenQueryKey = "_a"; //sdkTokenQueryKey + private String sdkQueryDelimiter = "="; public String algoVersion = "A"; public String SDKCode = ""; // Java = G, Android = F public String SDKSemver = ""; // Calculate the SDK version . @@ -42,9 +43,9 @@ public Analytics setTechVersion(String techVersion) { */ public String toQueryParam() { try { - return sdkTokenQueryKey + getAlgorithmVersion() + getSDKType() + getSDKVersion() + getTechVersion() + getSDKFeatureCode(); + return sdkTokenQueryKey + sdkQueryDelimiter + getAlgorithmVersion() + getSDKType() + getSDKVersion() + getTechVersion() + getSDKFeatureCode(); } catch (Exception e) { - return sdkTokenQueryKey + "E"; + return sdkTokenQueryKey + sdkQueryDelimiter + "E"; } } @@ -91,7 +92,7 @@ private String getPaddedString(String string) throws Exception { } String result = ""; - List resultList = StringUtils.usingPattern(paddedBinary,6); + List resultList = StringUtils.getAllSubStringWithSize(paddedBinary,6); int i = 0; while (i < resultList.size()) { result = result + Base64Map.values.get(resultList.get(i)); 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 2388ed0d..6dcbc664 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -436,7 +436,7 @@ public static String padStart(String inputString, int length, char paddingCharac * @param n Size of group * @return List with all strings with group size n. */ - public static List usingPattern(String text, int n) { + public static List getAllSubStringWithSize(String text, int n) { List results = new ArrayList<>(); Pattern pattern = Pattern.compile(".{1," + n + "}"); 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 84dd599b..dd2ef998 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -2,10 +2,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.utils.Analytics; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.*; import org.junit.rules.TestName; public class AnalyticsTest { @@ -34,16 +31,12 @@ public void testUrlWithAnalytics() { cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0")); String url = cloudinary.url().generate("test"); Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AFAACMh0"); - cloudinary.config.analytics = false; - cloudinary.analytics = null; } @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; - cloudinary.analytics = null; } @Test @@ -51,8 +44,6 @@ public void testUrlWithNoAnalyticsDefined() { cloudinary.config.analytics = false; String url = cloudinary.url().generate("test"); Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); - cloudinary.config.analytics = false; - cloudinary.analytics = null; } @Test @@ -60,8 +51,6 @@ public void testUrlWithNoAnalyticsNull() { cloudinary.analytics = null; String url = cloudinary.url().generate("test"); Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); - cloudinary.config.analytics = false; - cloudinary.analytics = null; } @Test @@ -70,8 +59,6 @@ public void testUrlWithNoAnalyticsNullAndTrue() { cloudinary.analytics = null; String url = cloudinary.url().generate("test"); Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); - cloudinary.config.analytics = false; - cloudinary.analytics = null; } @Test @@ -90,4 +77,10 @@ public void testErrorAnalytics() { Assert.assertEquals(result, "_a=E"); } + @After + public void tearDown() { + cloudinary.config.analytics = false; + cloudinary.analytics = null; + } + } \ No newline at end of file From 2fe233ac62e3cdc4d6a6b14fbff3db72004e5df8 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 31 Jan 2022 09:38:46 +0200 Subject: [PATCH 469/592] Update README.md --- README.md | 190 +++++++++++++++++------------------------------------- 1 file changed, 58 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index ac8d9ee5..3a5afcb4 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,36 @@ [![Build Status](https://travis-ci.org/cloudinary/cloudinary_java.svg?branch=master)](https://travis-ci.org/cloudinary/cloudinary_java) -[![Maven Central](https://img.shields.io/maven-central/v/com.cloudinary/cloudinary-core.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3Acom.cloudinary) -[![license](https://img.shields.io/github/license/cloudinary/cloudinary_js.svg?maxAge=2592000)]() Cloudinary ========== -Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. +## About +The Cloudinary Java SDK allows you to quickly and easily integrate your application with Cloudinary. +Effortlessly optimize and transform your cloud's assets. -Easily 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. +### Additional documentation +This Readme provides basic installation and usage information. +For the complete documentation, see the [Java SDK Guide](https://cloudinary.com/documentation/java_integration). -Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with any web application, existing or new. +## Table of Contents +- [Key Features](#key-features) +- [Version Support](#Version-Support) +- [Installation](#installation) +- [Usage](#usage) + - [Setup](#Setup) + - [Transform and Optimize Assets](#Transform-and-Optimize-Assets) + - [File upload](#File upload) +## Key Features +- [Transform](https://cloudinary.com/documentation/java_video_manipulation) and [optimize](https://cloudinary.com/documentation/java_image_manipulation#image_optimizations) assets (links to docs). +- [Upload assets to cloud](https://cloudinary.com/documentation/java_image_and_video_upload) -Cloudinary provides URL and HTTP based APIs that can be easily integrated with any Web development framework. +## Version Support +| SDK Version | Java 6+ | +|----------------|---------| +| 1.1.0 - 1.29.0 | V | -For Java, Cloudinary provides a library for simplifying the integration even further. - -**Notes:** - -* There are three flavors of the library to support different HttpClient versions: cloudinary-http42, cloudinary-http43 and cloudinary-http44. -* For Android there's a separate library available at https://github.com/cloudinary/cloudinary_android - -## Getting started guide -![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **Take a look at our [Getting started guide for Java](https://cloudinary.com/documentation/java_integration#getting_started_guide)**. - -## Setup ###################################################################### - -The cloudinary_java library is available in [Maven Central](https://repo1.maven.org/maven2/com/cloudinary/). To use it, add the following dependency to your pom.xml : +## 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 : ```xml @@ -41,52 +43,17 @@ The cloudinary_java library is available in [Maven Central](https://repo1.maven. Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.29.0/cloudinary-core-1.29.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.29.0/cloudinary-http44-1.29.0.jar) and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. -## Try it right away - -Sign up for a [free account](https://cloudinary.com/users/register/free) so you can try out image transformations and seamless image delivery through CDN. - -*Note: Replace `demo` in all the following examples with your Cloudinary's `cloud name`.* - -Accessing an uploaded image with the `sample` public ID through a CDN: - - http://res.cloudinary.com/demo/image/upload/sample.jpg - -![Sample](https://res.cloudinary.com/demo/image/upload/w_0.4/sample.jpg "Sample") - -Generating a 150x100 version of the `sample` image and downloading it through a CDN: - - http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg - -![Sample 150x100](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill/sample.jpg "Sample 150x100") - -Converting to a 150x100 PNG with rounded corners of 20 pixels: - - http://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png - -![Sample 150x150 Rounded PNG](https://res.cloudinary.com/demo/image/upload/w_150,h_100,c_fill,r_20/sample.png "Sample 150x150 Rounded PNG") - -For plenty more transformation options, see our [image transformations documentation](http://cloudinary.com/documentation/image_transformations). - -Generating a 120x90 thumbnail based on automatic face detection of the Facebook profile picture of Bill Clinton: - - http://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg - -![Facebook 90x120](https://res.cloudinary.com/demo/image/facebook/c_thumb,g_face,h_90,w_120/billclinton.jpg "Facebook 90x200") - -For more details, see our documentation for embedding [Facebook](https://cloudinary.com/documentation/facebook_profile_pictures) and [Twitter](https://cloudinary.com/documentation/twitter_profile_pictures) profile pictures. - ## Usage +### Setup -### Configuration - -Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. -Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. +Each request for building a URL of a remote cloud resource must have the `cloud_name` parameter set. +Each request to our secure APIs (e.g., image uploads, eager sprite generation) must have the `api_key` and `api_secret` parameters set. See [API, URLs and access identifiers](https://cloudinary.com/documentation/solution_overview#account_and_api_setup) for more details. -Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, +Setting the `cloud_name`, `api_key` and `api_secret` parameters can be done either directly in each call to a Cloudinary method, by when initializing the Cloudinary object, or by using the CLOUDINARY_URL environment variable / system property. -The entry point of the library is the Cloudinary object. +The entry point of the library is the Cloudinary object. ```java Cloudinary cloudinary = new Cloudinary(); ``` @@ -105,8 +72,8 @@ Another example of setting the configuration parameters by providing the CLOUDIN Cloudinary cloudinary = new Cloudinary("cloudinary://123456789012345:abcdeghijklmnopqrstuvwxyz12@n07t21i7"); -### Embedding and transforming images - +### Transform and Optimize Assets +- [See full documentation](https://cloudinary.com/documentation/java_image_manipulation) Any image uploaded to Cloudinary can be transformed and embedded using powerful view helper methods: The following example generates the url for accessing an uploaded `sample` image while transforming it to fill a 100x150 rectangle: @@ -115,38 +82,29 @@ The following example generates the url for accessing an uploaded `sample` image cloudinary.url().transformation(new Transformation().width(100).height(150).crop("fill")).generate("sample.jpg"); ``` -Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: +Another example, emedding a smaller version of an uploaded image while generating a 90x90 face detection based thumbnail: ```java cloudinary.url().transformation(new Transformation().width(90).height(90).crop("thumb").gravity("face")).generate("woman.jpg"); ``` -You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. - +You can provide either a Facebook name or a numeric ID of a Facebook profile or a fan page. + Embedding a Facebook profile to match your graphic design is very simple: ```java cloudinary.url().type("facebook").transformation(new Transformation().width(130).height(130).crop("fill").gravity("north_west")).generate("billclinton.jpg"); ``` -Same goes for Twitter: - -```java -cloudinary.url().type("twitter_name").generate("billclinton.jpg"); -``` - -![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_manipulation) for more information about displaying and transforming images in Java**. - -### Upload - +### File upload Assuming you have your Cloudinary configuration parameters defined (`cloud_name`, `api_key`, `api_secret`), uploading to Cloudinary is very simple. - -The following example uploads a local JPG to the cloud: + +The following example uploads a local JPG to the cloud: ```java cloudinary.uploader().upload("my_picture.jpg", ObjectUtils.emptyMap()); ``` - + The uploaded image is assigned a randomly generated public ID. The image is immediately available for download through a CDN: ```java @@ -155,7 +113,7 @@ cloudinary.url().generate("abcfrmo8zul1mafopawefg.jpg"); # http://res.cloudinary.com/demo/image/upload/abcfrmo8zul1mafopawefg.jpg ``` -You can also specify your own public ID: +You can also specify your own public ID: ```java cloudinary.uploader().upload("http://www.example.com/image.jpg", ObjectUtils.asMap("public_id", "sample_remote")); @@ -165,60 +123,28 @@ cloudinary.url().generate("sample_remote.jpg"); # http://res.cloudinary.com/demo/image/upload/sample_remote.jpg ``` -![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. - -### imageTag - -Returns an html image tag pointing to Cloudinary. - -Usage: - -```java -cloudinary.url().format("png").transformation(new Transformation().width(100).height(100).crop("fill")).imageTag("sample"); - -# -``` - -### imageUploadTag - -Returns an html input field for direct image upload, to be used in conjunction with [cloudinary\_js package](https://github.com/cloudinary/cloudinary_js/). It integrates [jQuery-File-Upload widget](https://github.com/blueimp/jQuery-File-Upload) and provides all the necessary parameters for a direct upload. - -Usage: - -```java -Map options = ObjectUtils.asMap("resource_type", "auto"); -Map htmlOptions = ObjectUtils.asMap("alt", "sample"); -String html = cloudinary.uploader().imageUploadTag("image_id", options, htmlOptions); -``` - -![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload#direct_uploading_from_the_browser) for plenty more options of uploading directly from the browser**. - -## Additional resources ########################################################## - -Additional resources are available at: - -* [Website](https://cloudinary.com) -* [Interactive demo](https://demo.cloudinary.com/default) -* [Knowledge Base](https://support.cloudinary.com/hc/en-us) -* [Documentation](https://cloudinary.com/documentation) -* [Documentation for Java integration](https://cloudinary.com/documentation/java_integration) -* [Image transformations documentation](https://cloudinary.com/documentation/image_transformations) -* [Upload API documentation](https://cloudinary.com/documentation/upload_images) - -## Support - -You can [open an issue through GitHub](https://github.com/cloudinary/cloudinary_java/issues). - -Contact us [https://cloudinary.com/contact](https://cloudinary.com/contact) - -Stay tuned for updates, tips and tutorials: [Blog](https://cloudinary.com/blog), [Twitter](https://twitter.com/cloudinary), [Facebook](https://www.facebook.com/Cloudinary). - -## Join the Community ########################################################## +![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. -Impact the product, hear updates, test drive new features and more! Join [here](https://www.facebook.com/groups/CloudinaryCommunity). +## Contributions +See [contributing guidelines](/CONTRIBUTING.md). +## Get Help +- [Open a Github issue](https://github.com/CloudinaryLtd/cloudinary_java/issues) (for issues related to the SDK) +- [Open a support ticket](https://cloudinary.com/contact) (for issues related to your account) -## License ####################################################################### +## About Cloudinary +Cloudinary is a powerful media API for websites and mobile apps alike, Cloudinary enables developers to efficiently manage, transform, optimize, and deliver images and videos through multiple CDNs. Ultimately, viewers enjoy responsive and personalized visual-media experiences—irrespective of the viewing device. -Released under the MIT license. +## Additional Resources +- [Cloudinary Transformation and REST API References](https://cloudinary.com/documentation/cloudinary_references): Comprehensive references, including syntax and examples for all SDKs. +- [MediaJams.dev](https://mediajams.dev/): Bite-size use-case tutorials written by and for Cloudinary Developers +- [DevJams](https://www.youtube.com/playlist?list=PL8dVGjLA2oMr09amgERARsZyrOz_sPvqw): Cloudinary developer podcasts on YouTube. +- [Cloudinary Academy](https://training.cloudinary.com/): Free self-paced courses, instructor-led virtual courses, and on-site courses. +- [Code Explorers and Feature Demos](https://cloudinary.com/documentation/code_explorers_demos_index): A one-stop shop for all code explorers, Postman collections, and feature demos found in the docs. +- [Cloudinary Roadmap](https://cloudinary.com/roadmap): Your chance to follow, vote, or suggest what Cloudinary should develop next. +- [Cloudinary Facebook Community](https://www.facebook.com/groups/CloudinaryCommunity): Learn from and offer help to other Cloudinary developers. +- [Cloudinary Account Registration](https://cloudinary.com/users/register/free): Free Cloudinary account registration. +- [Cloudinary Website](https://cloudinary.com) +## Licence +Released under the MIT license. \ No newline at end of file From 8e83b061daf8717ef30c37c1f22db4871b2a6de3 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 31 Jan 2022 09:40:32 +0200 Subject: [PATCH 470/592] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a5afcb4..9992595f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For the complete documentation, see the [Java SDK Guide](https://cloudinary.com/ - [Usage](#usage) - [Setup](#Setup) - [Transform and Optimize Assets](#Transform-and-Optimize-Assets) - - [File upload](#File upload) + - [File upload](#File-upload) ## Key Features - [Transform](https://cloudinary.com/documentation/java_video_manipulation) and [optimize](https://cloudinary.com/documentation/java_image_manipulation#image_optimizations) assets (links to docs). From 8d58527bb1c315e17575f3dcbba2acdd922e8c5e Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 31 Jan 2022 09:41:31 +0200 Subject: [PATCH 471/592] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9992595f..2380f145 100644 --- a/README.md +++ b/README.md @@ -123,8 +123,6 @@ cloudinary.url().generate("sample_remote.jpg"); # http://res.cloudinary.com/demo/image/upload/sample_remote.jpg ``` -![](https://res.cloudinary.com/cloudinary/image/upload/see_more_bullet.png) **See [our documentation](https://cloudinary.com/documentation/java_image_upload) for plenty more options of uploading to the cloud from your Java code**. - ## Contributions See [contributing guidelines](/CONTRIBUTING.md). From 6f5d5511cbf875159178549652e251d1b7fcdfe6 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 31 Jan 2022 23:01:21 +0200 Subject: [PATCH 472/592] Add analytics support. --- .../src/main/java/com/cloudinary/utils/Analytics.java | 2 +- .../src/test/java/com/cloudinary/analytics/AnalyticsTest.java | 3 +-- java_shared.gradle | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index 1a057e78..f4d0b6d3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -111,4 +111,4 @@ private String reverseVersion(String SDKSemver) throws Exception { } return StringUtils.join(StringUtils.reverseStringArray(versionArray), "."); } -} \ No newline at end of file +} 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 dd2ef998..a6a89ac6 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -14,7 +14,6 @@ public class AnalyticsTest { @Before public void setUp() { - System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); } @@ -83,4 +82,4 @@ public void tearDown() { cloudinary.analytics = null; } -} \ No newline at end of file +} diff --git a/java_shared.gradle b/java_shared.gradle index f61e3fea..d23a49b4 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -2,7 +2,6 @@ sourceCompatibility = 1.7 targetCompatibility = 1.7 javadoc { - failOnError false options.encoding = 'UTF-8' } @@ -39,4 +38,4 @@ tasks.withType(Test) { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' -} \ No newline at end of file +} From 8d0d9fcb2640c641623faf1fc508832fb3a55844 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 08:02:33 +0200 Subject: [PATCH 473/592] Add analytics support. --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index d02d5a07..01b29df8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -422,6 +422,7 @@ public String generate(String source) { if (cloudinary.analytics != null && cloudinary.config.analytics) { try { URL tempUrl = new URL(url); + // if any other query param already exist on the URL do not add analytics query param. if (tempUrl.getQuery() == null) { String path = tempUrl.getPath(); url = url + "?" + cloudinary.analytics.toQueryParam(); From 28049da66552a6e32ee14acffe0b8795a9fefb8b Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 08:08:35 +0200 Subject: [PATCH 474/592] Update test name --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 1 - cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 01b29df8..d7b1cc92 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -409,7 +409,6 @@ public String generate(String source) { String join = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/"); String url = StringUtils.mergeSlashesInUrl(join); - if (signUrl && authToken != null && !authToken.equals(AuthToken.NULL_AUTH_TOKEN)) { try { URL tempUrl = new URL(url); diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index b93f21b6..bb8b188b 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -94,7 +94,7 @@ public void testAuthenticatedUrl() { } @Test - public void testUrlAnalyticsWithQueryParams() { + public void testUrlNoAnalyticsWithQueryParams() { cloudinary.config.analytics = true; cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"))); cloudinary.config.privateCdn = true; From cc84ddcc8fdf0eca93ae98c1a1fb6ab38006b4dd Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:03:28 +0200 Subject: [PATCH 475/592] Add encode version tests --- .../cloudinary/analytics/AnalyticsTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) 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 a6a89ac6..9d0a1a12 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -1,12 +1,17 @@ package com.cloudinary.analytics; +import com.cloudinary.AuthToken; import com.cloudinary.Cloudinary; import com.cloudinary.utils.Analytics; import org.junit.*; import org.junit.rules.TestName; +import static org.junit.Assert.assertEquals; + public class AnalyticsTest { + public static final String KEY = "00112233FF99"; + private Cloudinary cloudinary; @Rule @@ -14,9 +19,35 @@ public class AnalyticsTest { @Before public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); } + @Test + public void testEncodeVersion() { + Analytics analytics = new Analytics(); + analytics.setSDKSemver("1.24.0"); + String result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AGAlhAN0"); + + analytics.setSDKSemver("12.0"); + result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AGAMAN0"); + + analytics.setSDKSemver("43.21.26"); + result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AG///AN0"); + + analytics.setSDKSemver("0.0.0"); + result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=AGAAAAN0"); + + analytics.setSDKSemver("43.21.27"); + result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=E"); + + } + @Test public void testToQueryParam() { Analytics analytics = new Analytics("F", "2.0.0", "1.8.0"); @@ -76,6 +107,21 @@ public void testErrorAnalytics() { Assert.assertEquals(result, "_a=E"); } + @Test + public void testUrlNoAnalyticsWithQueryParams() { + final AuthToken authToken = new AuthToken(KEY).duration(300); + authToken.startTime(11111111); // start time is set for test purposes + cloudinary.config.authToken = authToken; + cloudinary.config.cloudName = "test123"; + + cloudinary.config.analytics = true; + cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"))); + 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"); + cloudinary.config.privateCdn = false; + } + @After public void tearDown() { cloudinary.config.analytics = false; From 025b55636da2286d3b5a0889098276b28b76e945 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:06:40 +0200 Subject: [PATCH 476/592] Add default Java analytics constructor --- .../src/main/java/com/cloudinary/utils/Analytics.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index f4d0b6d3..e20ae5fa 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -1,5 +1,7 @@ package com.cloudinary.utils; +import com.cloudinary.Cloudinary; + import java.util.Arrays; import java.util.List; @@ -10,12 +12,10 @@ public class Analytics { public String SDKCode = ""; // Java = G, Android = F public String SDKSemver = ""; // Calculate the SDK version . public String techVersion = ""; // Calculate the Java version. - public void Analytics() { - // initilaize with Java values. - // From Android pick the values. - } - public Analytics() {} + public Analytics() { + this("G", Cloudinary.VERSION,System.getProperty("java.version")); + } public Analytics(String sdkCode, String sdkVersion, String techVersion) { this.SDKCode = sdkCode; this.SDKSemver = sdkVersion; From aad44ad136a25b49a9df569721cb84dce8342b35 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:06:51 +0200 Subject: [PATCH 477/592] Remove analytics test --- .../src/test/java/com/cloudinary/AuthTokenTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index bb8b188b..800fc581 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -93,16 +93,6 @@ public void testAuthenticatedUrl() { } - @Test - public void testUrlNoAnalyticsWithQueryParams() { - cloudinary.config.analytics = true; - cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"))); - 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"); - cloudinary.config.privateCdn = false; - } - @Test public void testConfiguration() { cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&auth_token[key]=aabbcc112233&auth_token[duration]=200"); From 5e67b33f4e7903961572650c556aad88e7148e73 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:07:14 +0200 Subject: [PATCH 478/592] Add default initialization for analytics --- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 0bda8649..c12be7ac 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -46,7 +46,7 @@ public class Cloudinary { private AbstractUploaderStrategy uploaderStrategy; private AbstractApiStrategy apiStrategy; private String userAgent = USER_AGENT_PREFIX+"/"+ VERSION + " "+USER_AGENT_JAVA_VERSION; - public Analytics analytics; + public Analytics analytics = new Analytics(); public Uploader uploader() { return new Uploader(this, uploaderStrategy); } @@ -156,7 +156,6 @@ public void setUserAgent(String prefix, String version){ /** * Set the analytics object that will be sent with every URL generation call. - * a userAgent is built from `prefix/version (additional data)` * @param analytics - the analytics object to set */ public void setAnalytics(Analytics analytics) { From 4f943dda2db809d304c50c54cdaf6871a045140d Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:07:27 +0200 Subject: [PATCH 479/592] Remove misleading if condition for analytics --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index d7b1cc92..c5db667d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -418,7 +418,7 @@ public String generate(String source) { } catch (MalformedURLException ignored) { } } - if (cloudinary.analytics != null && cloudinary.config.analytics) { + if (cloudinary.config.analytics) { try { URL tempUrl = new URL(url); // if any other query param already exist on the URL do not add analytics query param. From a1835ebee171dca5c118ac2ff749bf73ce2146b7 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 11:42:51 +0200 Subject: [PATCH 480/592] Fix failing URL test --- cloudinary-core/src/main/java/com/cloudinary/Url.java | 2 +- .../src/test/java/com/cloudinary/analytics/AnalyticsTest.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index c5db667d..b9fb0407 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -418,7 +418,7 @@ public String generate(String source) { } catch (MalformedURLException ignored) { } } - if (cloudinary.config.analytics) { + if (cloudinary.config.analytics != null && cloudinary.config.analytics) { try { URL tempUrl = new URL(url); // if any other query param already exist on the URL do not add analytics query param. 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 9d0a1a12..f6564092 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -78,7 +78,6 @@ public void testUrlWithNoAnalyticsDefined() { @Test public void testUrlWithNoAnalyticsNull() { - cloudinary.analytics = null; String url = cloudinary.url().generate("test"); Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); } @@ -86,9 +85,8 @@ public void testUrlWithNoAnalyticsNull() { @Test public void testUrlWithNoAnalyticsNullAndTrue() { cloudinary.config.analytics = true; - cloudinary.analytics = null; String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AGAtVAN0"); } @Test From e463570832ffe904589874144f58aebb6fb84c4a Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 1 Feb 2022 12:30:59 +0200 Subject: [PATCH 481/592] Fix failing test hardcoded Java version --- .../java/com/cloudinary/analytics/AnalyticsTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 f6564092..7e71db52 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -27,20 +27,21 @@ public void setUp() { public void testEncodeVersion() { Analytics analytics = new Analytics(); analytics.setSDKSemver("1.24.0"); + analytics.setTechVersion("12.0.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAlhAN0"); + Assert.assertEquals(result, "_a=AGAlhAM0"); analytics.setSDKSemver("12.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAMAN0"); + Assert.assertEquals(result, "_a=AGAMAM0"); analytics.setSDKSemver("43.21.26"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AG///AN0"); + Assert.assertEquals(result, "_a=AG///AM0"); analytics.setSDKSemver("0.0.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAAAAN0"); + Assert.assertEquals(result, "_a=AGAAAAM0"); analytics.setSDKSemver("43.21.27"); result = analytics.toQueryParam(); @@ -85,8 +86,9 @@ public void testUrlWithNoAnalyticsNull() { @Test public void testUrlWithNoAnalyticsNullAndTrue() { cloudinary.config.analytics = true; + cloudinary.analytics.setTechVersion("12.0.0"); String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AGAtVAN0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AGAtVAM0"); } @Test From 8ba890d8646183744fdfc86fd880a1ad2079ccd4 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Wed, 2 Feb 2022 09:32:43 +0200 Subject: [PATCH 482/592] Version 1.30.0 --- .gitignore | 7 +++++++ CHANGELOG.md | 9 +++++++++ README.md | 8 ++++---- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 8c7d1af1..e732dc7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +## Apple storage files +*.DS_Store + ## Android default ignore # Built application files *.apk @@ -40,3 +43,7 @@ test-output/ appengine-web.xml cloudinary-android/src/androidTest/AndroidManifest.xml +##Tools +/tools/cloudinary_url.txt +/tools/History.md + diff --git a/CHANGELOG.md b/CHANGELOG.md index 0350cb4c..98db5a9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ +1.30.0 / 2022-02-02 +=================== + +Other Changes +------------- + * Update `README.md` + * Add feature `SDK analytics` + * Fix a bug where a publicId which contains 'v[num]' is considered to contain a version, therefore the version is skipped. (#242) + 1.29.0 / 2021-02-10 =================== diff --git a/README.md b/README.md index 2380f145..08c54813 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.29.0 | V | +| 1.1.0 - 1.30.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,11 +36,11 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http44 - 1.29.0 + 1.30.0 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.29.0/cloudinary-core-1.29.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.29.0/cloudinary-http44-1.29.0.jar) +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 @@ -145,4 +145,4 @@ Cloudinary is a powerful media API for websites and mobile apps alike, Cloudinar - [Cloudinary Website](https://cloudinary.com) ## Licence -Released under the MIT license. \ No newline at end of file +Released under the MIT license. diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index c12be7ac..64e0c45b 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.29.0"; + public final static String VERSION = "1.30.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 ef9fd370..1c79d617 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.29.0 +version=1.30.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 7d154a964447973aa902e89fe6693b12db40bf44 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:53:31 +0200 Subject: [PATCH 483/592] Fix analytics test --- .../src/test/java/com/cloudinary/analytics/AnalyticsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 7e71db52..b45e76e0 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -86,9 +86,10 @@ public void testUrlWithNoAnalyticsNull() { @Test public void testUrlWithNoAnalyticsNullAndTrue() { cloudinary.config.analytics = true; + 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=AGAtVAM0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AGAu5AM0"); } @Test From 70a1f87c5aafffb9be8682e26d0ba6e35fd41ade Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 13 Feb 2022 10:24:18 +0200 Subject: [PATCH 484/592] Add template for PR --- .github/pull_request_template.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..e2509ff5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +### Brief Summary of Changes + + +#### What does this PR address? +- [ ] GitHub issue (Add reference - #XX) +- [ ] Refactoring +- [ ] New feature +- [ ] Bug fix +- [ ] Adds more tests + +#### Are tests included? +- [ ] Yes +- [ ] No + +#### Reviewer, please note: + + +#### Checklist: + + +- [ ] My code follows the code style of this project. +- [ ] My change requires a change to the documentation. +- [ ] I ran the full test suite before pushing the changes and all the tests pass. From aaac4f3fbe67003617ab1529da832c1f156e8bcb Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Wed, 16 Feb 2022 12:00:11 +0300 Subject: [PATCH 485/592] Fix named parameters normalization issue --- .../cloudinary/transformation/BaseExpression.java | 2 +- .../cloudinary/transformation/ExpressionTest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java index 0da9d702..a4ed118c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/BaseExpression.java @@ -123,7 +123,7 @@ private static Pattern getPattern() { sb.append(Pattern.quote(op)).append("|"); } sb.deleteCharAt(sb.length() - 1); - sb.append(")(?=[ _])|(? Date: Tue, 22 Feb 2022 12:48:12 +0200 Subject: [PATCH 486/592] Fix single character variable --- .../com/cloudinary/utils/StringUtils.java | 5 +-- .../transformation/VariableTest.java | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java 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 6dcbc664..66ec38ff 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -277,9 +277,8 @@ public static String mergeToSingleUnderscore(String s) { */ public static boolean isVariable(String s) { if (s == null || - s.length() < 3 || - !s.startsWith("$") || - !Character.isLetter(s.charAt(1))) { + s.length() < 2 || + !s.startsWith("$")) { return false; } diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java new file mode 100644 index 00000000..7ab38288 --- /dev/null +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java @@ -0,0 +1,42 @@ +package com.cloudinary.transformation; + +import com.cloudinary.Cloudinary; +import com.cloudinary.Transformation; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import static org.junit.Assert.assertEquals; + +public class VariableTest { + + private Cloudinary cloudinary; + + @Rule + public TestName currentTest = new TestName(); + + @Before + public void setUp() { + System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + } + + @Test + public void testSingleCharacterVariable() { + String url = cloudinary.url().transformation(new Transformation().variable("$a","100").chain().height("$a")).generate("sample.jpg"); + assertEquals("http://res.cloudinary.com/test123/image/upload/$a_100/h_$a/sample.jpg", url); + } + + @Test + public void testDollarOnlyAsVariable() { + String url = cloudinary.url().transformation(new Transformation().variable("$","100").chain().height("$")).generate("sample.jpg"); + assertEquals("http://res.cloudinary.com/test123/image/upload/h_$/sample.jpg", url); + } + + @Test + public void testMultipleCharactersVariable() { + String url = cloudinary.url().transformation(new Transformation().variable("$a45","100").chain().height("$a45")).generate("sample.jpg"); + assertEquals("http://res.cloudinary.com/test123/image/upload/$a45_100/h_$a45/sample.jpg", url); + } +} From dac690b689564e6136e571d523851ff9d059318c Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 22 Feb 2022 12:55:08 +0200 Subject: [PATCH 487/592] Revert "Fix single character variable" This reverts commit e670916eb30db80c45b42bf026b1e2f5127d4b1c. --- .../com/cloudinary/utils/StringUtils.java | 5 ++- .../transformation/VariableTest.java | 42 ------------------- 2 files changed, 3 insertions(+), 44 deletions(-) delete mode 100644 cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java 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 66ec38ff..6dcbc664 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -277,8 +277,9 @@ public static String mergeToSingleUnderscore(String s) { */ public static boolean isVariable(String s) { if (s == null || - s.length() < 2 || - !s.startsWith("$")) { + s.length() < 3 || + !s.startsWith("$") || + !Character.isLetter(s.charAt(1))) { return false; } diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java deleted file mode 100644 index 7ab38288..00000000 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/VariableTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.cloudinary.transformation; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Transformation; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import static org.junit.Assert.assertEquals; - -public class VariableTest { - - private Cloudinary cloudinary; - - @Rule - public TestName currentTest = new TestName(); - - @Before - public void setUp() { - System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); - } - - @Test - public void testSingleCharacterVariable() { - String url = cloudinary.url().transformation(new Transformation().variable("$a","100").chain().height("$a")).generate("sample.jpg"); - assertEquals("http://res.cloudinary.com/test123/image/upload/$a_100/h_$a/sample.jpg", url); - } - - @Test - public void testDollarOnlyAsVariable() { - String url = cloudinary.url().transformation(new Transformation().variable("$","100").chain().height("$")).generate("sample.jpg"); - assertEquals("http://res.cloudinary.com/test123/image/upload/h_$/sample.jpg", url); - } - - @Test - public void testMultipleCharactersVariable() { - String url = cloudinary.url().transformation(new Transformation().variable("$a45","100").chain().height("$a45")).generate("sample.jpg"); - assertEquals("http://res.cloudinary.com/test123/image/upload/$a45_100/h_$a45/sample.jpg", url); - } -} From a0ccbc4f674abfdf7aaa3951627c9db275511429 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 23 Feb 2022 07:24:24 +0200 Subject: [PATCH 488/592] Add support for single character variable --- .../src/main/java/com/cloudinary/utils/StringUtils.java | 2 +- cloudinary-core/src/test/java/com/cloudinary/UtilTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 6dcbc664..0d25bacb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -277,7 +277,7 @@ public static String mergeToSingleUnderscore(String s) { */ public static boolean isVariable(String s) { if (s == null || - s.length() < 3 || + s.length() < 2 || !s.startsWith("$") || !Character.isLetter(s.charAt(1))) { return false; diff --git a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java index 8f8f5e15..6794e277 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/UtilTest.java @@ -76,8 +76,8 @@ public void testIsVariable(){ assertTrue(StringUtils.isVariable("$ab")); assertTrue(StringUtils.isVariable("$asdasda")); assertTrue(StringUtils.isVariable("$a34asd12e")); + assertTrue(StringUtils.isVariable("$a")); - assertFalse(StringUtils.isVariable("$a")); assertFalse(StringUtils.isVariable("sda")); assertFalse(StringUtils.isVariable(" ")); assertFalse(StringUtils.isVariable("... . /")); @@ -159,4 +159,4 @@ public void testRemoveStartingChars(){ assertEquals("bcde", StringUtils.removeStartingChars("aaaaaabcde", 'a')); assertEquals("bcdeaa", StringUtils.removeStartingChars("aaaaaabcdeaa", 'a')); } -} \ No newline at end of file +} From 95d2b33206caa7830c6974eebf377b4a7dabf17f Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 1 Mar 2022 11:16:55 +0200 Subject: [PATCH 489/592] Fix post-test cleanup for `eval` upload parameter --- .../com/cloudinary/test/AbstractUploaderTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 f37d2fbd..6925927c 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 @@ -34,8 +34,7 @@ abstract public class AbstractUploaderTest extends MockableTest { private static final String UPLOADER_TEST_PUBLIC_ID = "uploader_test"; public static final String SRC_FULLY_QUALIFIED_IMAGE="image/upload/" + UPLOADER_TEST_PUBLIC_ID; public static final String SRC_FULLY_QUALIFIED_VIDEO="video/upload/dog"; - public static final String SRC_TEST_EVAL= "if (resource_info['width'] < 450) { upload_options['tags'] = 'a,b' };" + "upload_options['context'] = 'width=' + resource_info['width'];"; - private static final ArrayList TEST_EVAL_TAGS_RESULT = new ArrayList(Arrays.asList("a","b")); + public static final String SRC_TEST_EVAL= "if (resource_info['width'] < 450) { upload_options['quality_analysis'] = true };" + "upload_options['context'] = 'width=' + resource_info['width'];"; @BeforeClass @@ -275,8 +274,12 @@ public void testImageUploadTag() { @Test public void testEvalUploadParameter() throws IOException { - Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("eval",SRC_TEST_EVAL)); - assertEquals(result.get("tags"), TEST_EVAL_TAGS_RESULT); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap( + "eval",SRC_TEST_EVAL, + "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG) + )); + assertTrue(result.get("quality_analysis")!=null && + ((HashMap)result.get("quality_analysis")).containsKey("focus")); Map custom= (Map)((Map) result.get("context")).get("custom"); assertEquals(custom.get("width"),Integer.toString(SRC_TEST_IMAGE_W)); } From 9e91064610afd671c75771f829b7f95a59dda380 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Sun, 6 Mar 2022 17:49:05 +0200 Subject: [PATCH 490/592] Support `filename_override` upload parameter --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 ++ .../java/com/cloudinary/test/AbstractUploaderTest.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 03b5965e..2333e1cf 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -35,6 +35,8 @@ public static final Map buildUploadParams(Map options) { params.put("allowed_formats", StringUtils.join(ObjectUtils.asArray(options.get("allowed_formats")), ",")); params.put("moderation", options.get("moderation")); params.put("access_mode", (String) options.get("access_mode")); + params.put("filename_override", (String) options.get("filename_override")); + Object responsive_breakpoints = options.get("responsive_breakpoints"); if (responsive_breakpoints != null) { params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints)); 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 6925927c..d0f96181 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 @@ -562,6 +562,14 @@ public void testFilenameOption() throws Exception { assertEquals("emanelif", result.get("original_filename")); } + + @Test + public void testFilenameOverrideOption() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("filename_override", "overridden", "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); + assertEquals("overridden", result.get("original_filename")); + } + + @Test public void testResponsiveBreakpoints() throws Exception { ResponsiveBreakpoint breakpoint = new ResponsiveBreakpoint() From 8a3c757458671322360c4312c0a592aef04433ff Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Sun, 6 Mar 2022 17:49:28 +0200 Subject: [PATCH 491/592] Support download backup version api --- .../src/main/java/com/cloudinary/Api.java | 3 +- .../main/java/com/cloudinary/Cloudinary.java | 28 ++++++ .../com/cloudinary/test/CloudinaryTest.java | 16 ++++ .../com/cloudinary/test/AbstractApiTest.java | 86 +++++++++++++++++++ 4 files changed, 132 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 d475f2b3..8b1caeee 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -141,7 +141,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"), options); + "accessibility_analysis", "versions"), options); return response; } @@ -302,6 +302,7 @@ public ApiResponse restore(Iterable publicIds, Map options) throws Excep String type = ObjectUtils.asString(options.get("type"), "upload"); Map params = new HashMap(); params.put("public_ids", publicIds); + params.put("versions", options.get("versions")); ApiResponse response = callApi(HttpMethod.POST, Arrays.asList("resources", resourceType, type, "restore"), params, options); return response; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 64e0c45b..7fd3b80c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -338,6 +338,34 @@ public String downloadFolder(String folderPath, Map options) throws UnsupportedE return downloadArchive(adjustedOptions, (String) adjustedOptions.get("target_format")); } + /** + * Returns an URL of a specific version of a backed up asset that can be used to download that + * version of the asset (within an hour of the request). + * + * @param assetId The identifier of the uploaded asset. + * @param versionId The identifier of a backed up version of the asset. + * @param options Optional, holds hints for URL generation procedure, see documentation for + * full list + * @return The download URL of the asset + */ + public String downloadBackedupAsset(String assetId, String versionId, Map options) throws UnsupportedEncodingException { + if (StringUtils.isEmpty(assetId)) { + throw new IllegalArgumentException("AssetId parameter is required"); + } + + if (StringUtils.isEmpty(versionId)) { + throw new IllegalArgumentException("VersionId parameter is required"); + } + + Map params = new HashMap(); + params.put("asset_id", assetId); + params.put("version_id", versionId); + params.put("timestamp", Util.timestamp()); + + signRequest(params, options); + return buildUrl(cloudinaryApiUrl("download_backup", options), params); + } + private String buildUrl(String base, Map params) throws UnsupportedEncodingException { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(base); 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 109b44ed..a911f92d 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -19,6 +19,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.*; @@ -1405,6 +1406,21 @@ public void testApiSignRequestSHA256() { assertEquals("45ddaa4fa01f0c2826f32f669d2e4514faf275fe6df053f1a150e7beae58a3bd", signature); } + @Test + public void testDownloadBackedupAsset() throws UnsupportedEncodingException, URISyntaxException { + String url = cloudinary.downloadBackedupAsset("62c2a18d622be7e190d21df8e05b1416", + "26fe6d95df856f6ae12f5678be94516a", ObjectUtils.emptyMap()); + + URI uri = new URI(url); + assertTrue(uri.getPath().endsWith("download_backup")); + + Map params = getUrlParameters(uri); + assertEquals("62c2a18d622be7e190d21df8e05b1416", params.get("asset_id")); + assertEquals("26fe6d95df856f6ae12f5678be94516a", params.get("version_id")); + assertNotNull(params.get("signature")); + assertNotNull(params.get("timestamp")); + } + 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(); 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 0bc51d31..b9008aa6 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 @@ -829,6 +829,92 @@ public void testRestore() throws Exception { 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; + final Uploader uploader = cloudinary.uploader(); + + Map firstUpload = uploader.upload(SRC_TEST_IMAGE, + ObjectUtils.asMap( + "public_id", TEST_RESOURCE_PUBLIC_ID, + "backup", true, + "tags", UPLOAD_TAGS + )); + assertEquals(firstUpload.get("public_id"), TEST_RESOURCE_PUBLIC_ID); + ApiResponse firstDelete = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); + assertTrue(firstDelete.containsKey("deleted")); + + Map secondUpload = uploader.upload(SRC_TEST_IMAGE, + ObjectUtils.asMap( + "public_id", TEST_RESOURCE_PUBLIC_ID, + "backup", true, + "transformation", new Transformation().angle("0"), + "tags", UPLOAD_TAGS + )); + assertEquals(secondUpload.get("public_id"), TEST_RESOURCE_PUBLIC_ID); + ApiResponse secondDelete = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); + assertTrue(secondDelete.containsKey("deleted")); + + assertNotEquals(firstUpload.get("bytes"), secondUpload.get("bytes")); + + ApiResponse getVersionsResp = api.resource(TEST_RESOURCE_PUBLIC_ID, ObjectUtils.asMap("versions", true)); + List versions = (List) getVersionsResp.get("versions"); + Object firstAssetVersion = versions.get(0).get("version_id"); + Object secondAssetVersion = versions.get(1).get("version_id"); + + ApiResponse firstVerRestore = api.restore(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), + ObjectUtils.asMap("versions", Collections.singletonList(firstAssetVersion))); + assertEquals(((Map) firstVerRestore.get(TEST_RESOURCE_PUBLIC_ID)).get("bytes"), firstUpload.get("bytes")); + + ApiResponse secondVerRestore = api.restore(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), + ObjectUtils.asMap("versions", Collections.singletonList(secondAssetVersion))); + assertEquals(((Map) secondVerRestore.get(TEST_RESOURCE_PUBLIC_ID)).get("bytes"), secondUpload.get("bytes")); + + ApiResponse finalDeleteResp = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); + assertTrue(finalDeleteResp.containsKey("deleted")); + } + + @Test + public void testShouldRestoreTwoDifferentDeletedAssets() throws Exception { + final String PUBLIC_ID_BACKUP_1 = "api_test_restore_versions_different_assets_1_" + SUFFIX; + final String PUBLIC_ID_BACKUP_2 = "api_test_restore_versions_different_assets_2_" + SUFFIX; + + final Uploader uploader = cloudinary.uploader(); + + Map firstUpload = uploader.upload(SRC_TEST_IMAGE, + ObjectUtils.asMap( + "public_id", PUBLIC_ID_BACKUP_1, + "backup", true, + "tags", UPLOAD_TAGS + )); + Map secondUpload = uploader.upload(SRC_TEST_IMAGE, + ObjectUtils.asMap( + "public_id", PUBLIC_ID_BACKUP_2, + "backup", true, + "transformation", new Transformation().angle("0"), + "tags", UPLOAD_TAGS + )); + + ApiResponse deleteAll = api.deleteResources(Arrays.asList(PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2), ObjectUtils.emptyMap()); + assertEquals("deleted", ((Map) deleteAll.get("deleted")).get(PUBLIC_ID_BACKUP_1)); + assertEquals("deleted", ((Map) deleteAll.get("deleted")).get(PUBLIC_ID_BACKUP_2)); + + ApiResponse getFirstAssetVersion = api.resource(PUBLIC_ID_BACKUP_1, ObjectUtils.asMap("versions", true)); + ApiResponse getSecondAssetVersion = api.resource(PUBLIC_ID_BACKUP_2, ObjectUtils.asMap("versions", true)); + + Object firstAssetVersion = ((List) getFirstAssetVersion.get("versions")).get(0).get("version_id"); + Object secondAssetVersion = ((List) getSecondAssetVersion.get("versions")).get(0).get("version_id"); + + ApiResponse restore = api.restore(Arrays.asList(PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2), + ObjectUtils.asMap("versions", Arrays.asList(firstAssetVersion, secondAssetVersion))); + assertEquals(((Map) restore.get(PUBLIC_ID_BACKUP_1)).get("bytes"), firstUpload.get("bytes")); + assertEquals(((Map) restore.get(PUBLIC_ID_BACKUP_2)).get("bytes"), secondUpload.get("bytes")); + + ApiResponse finalDelete = api.deleteResources(Arrays.asList(PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2), ObjectUtils.emptyMap()); + assertEquals("deleted", ((Map) finalDelete.get("deleted")).get(PUBLIC_ID_BACKUP_1)); + assertEquals("deleted", ((Map) finalDelete.get("deleted")).get(PUBLIC_ID_BACKUP_2)); + } + @Test public void testEncodeUrlInApiCall() throws Exception { String apiTestEncodeUrlInApiCall = "sub^folder test"; From c721dff98b720e8c291072496546fcfce8343d4b Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Thu, 10 Mar 2022 12:10:51 +0300 Subject: [PATCH 492/592] Implement missing tests Users API --- .../com/cloudinary/provisioning/Account.java | 4 +- .../test/AbstractAccountApiTest.java | 68 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) 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 8c0f1eca..d81ee527 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -283,7 +283,7 @@ public ApiResponse user(String userId, Map options) throws Excep /** * Get a list of the users according to filters. * - * @param pending Optional. Whether to fetch pending users. Default all. + * @param pending Optional. Limit results to pending users (true), users that are not pending (false), or all users (null) * @param userIds Optionals. List of user IDs. Up to 100 * @param prefix Optional. Search by prefix of the user's name or email. Case-insensitive * @param subAccountId Optional. Return only users who have access to the given sub-account @@ -297,7 +297,7 @@ public ApiResponse users(Boolean pending, List userIds, String prefix, S /** * Get a list of the users according to filters. * - * @param pending Optional. Whether to fetch pending users. Default all. + * @param pending Optional. Limit results to pending users (true), users that are not pending (false), or all users (null) * @param userIds Optionals. List of user IDs. Up to 100 * @param prefix Optional. Search by prefix of the user's name or email. Case-insensitive * @param subAccountId Optional. Return only users who have access to the given sub-account 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 a5d00fcb..8b35075e 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 @@ -5,6 +5,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.provisioning.Account; import org.junit.*; +import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import java.util.*; @@ -29,6 +30,8 @@ public static void setUpClass() { @Rule public TestName currentTest = new TestName(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); @Before public void setUp() throws Exception { @@ -180,6 +183,63 @@ public void testGetUsers() throws Exception { deleteUser(user2Id); } + @Test + public void testGetPendingUsers() throws Exception { + String id = createUser(Account.Role.BILLING).get("id").toString(); + + ApiResponse pending = account.users(true, Collections.singletonList(id), null, null, null); + assertEquals(1, ((ArrayList) pending.get("users")).size()); + + ApiResponse notPending = account.users(false, Collections.singletonList(id), null, null, null); + assertEquals(0, ((ArrayList) notPending.get("users")).size()); + + ApiResponse all = account.users(null, Collections.singletonList(id), null, null, null); + assertEquals(1, ((ArrayList) all.get("users")).size()); + } + + @Test + public void testGetUsersByPrefix() throws Exception { + final long timeMillis = System.currentTimeMillis(); + final String userName = String.format("SDK TEST Get Users By Prefix %d", timeMillis); + final String userEmail = String.format("sdk-test-get-users-by-prefix+%d@cloudinary.com", timeMillis); + + createUser(userName, + userEmail, + Account.Role.BILLING, + Collections.emptyList()); + + ApiResponse userByPrefix = account.users(true, null, userName.substring(0, userName.length() - 1), null, null); + assertEquals(1, ((ArrayList) userByPrefix.get("users")).size()); + + ApiResponse userByNonExistingPrefix = account.users(true, null, userName + "zzz", null, null); + assertEquals(0, ((ArrayList) userByNonExistingPrefix.get("users")).size()); + } + + @Test + public void testGetUsersBySubAccountIds() throws Exception { + ApiResponse subAccount = createSubAccount(); + final String subAccountId = subAccount.get("id").toString(); + + final long timeMillis = System.currentTimeMillis(); + final String userName = String.format("SDK TEST Get Users By Sub Account Ids %d", timeMillis); + final String userEmail = String.format("sdk-test-get-users-by-sub-account-ids+%d@cloudinary.com", timeMillis); + + createUser(userName, + userEmail, + Account.Role.BILLING, + Collections.singletonList(subAccountId)); + + ApiResponse usersBySubAccount = account.users(true, null, userName, subAccountId, null); + assertEquals(1, ((ArrayList) usersBySubAccount.get("users")).size()); + } + + @Test + public void testGetUsersThrowsWhenSubAccountIdDoesntExist() throws Exception { + final String subAccountId = randomLetters(); + expectedException.expectMessage("Cannot find sub account with id " + subAccountId); + account.users(true, null, null, subAccountId, null); + } + @Test public void testCreateUser() throws Exception { ApiResponse createResult = createSubAccount(); @@ -294,6 +354,7 @@ private ApiResponse createGroup() throws Exception { ApiResponse userGroup = account.createUserGroup(name); createdGroupIds.add(userGroup.get("id").toString()); return userGroup; + } private ApiResponse createUser(Account.Role role) throws Exception { @@ -310,7 +371,11 @@ private ApiResponse createUser(List subAccountsIds) throws Exception { private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { String email = String.format("%s@%s.com", randomLetters(), randomLetters()); - ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, subAccountsIds, null); + return createUser("TestName", email, role, subAccountsIds); + } + + private ApiResponse createUser(final String name, String email, Account.Role role, List subAccountsIds) throws Exception { + ApiResponse user = account.createUser(name, email, role, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); return user; } @@ -336,6 +401,7 @@ private static String randomLetters() { for (int i = 0; i < 10; i++) { sb.append((char) ('a' + rand.nextInt('z' - 'a' + 1))); } + return sb.toString(); } } From c233ce68369ef5bdd9ee33454a47f97a3ee0d2a1 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:58:00 +0300 Subject: [PATCH 493/592] Allow to disable b-frames --- .../java/com/cloudinary/Transformation.java | 3 +++ .../com/cloudinary/test/CloudinaryTest.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index f1efd89c..8b3d8c40 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -923,6 +923,9 @@ private static String processVideoCodecParam(Object param) { outParam.append(":").append(paramMap.get("profile")); if (paramMap.containsKey("level")) { outParam.append(":").append(paramMap.get("level")); + if (paramMap.containsKey("b_frames") && paramMap.get("b_frames") == "false") { + outParam.append(":").append("bframes_no"); + } } } } 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 a911f92d..f4f94c43 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -879,6 +879,24 @@ public void testVideoCodec() { assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); } + @Test + public void testVideoCodecBFrameTrue() { + String actual = cloudinary.url().resourceType("video") + .transformation( + new Transformation().videoCodec(asMap("codec", "h264", "profile", "basic", "level", "3.1", "b_frames", "true")) + ).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1/video_id", actual); + } + + @Test + public void testVideoCodecBFrameFalse() { + String actual = cloudinary.url().resourceType("video") + .transformation( + new Transformation().videoCodec(asMap("codec", "h264", "profile", "basic", "level", "3.1", "b_frames", "false")) + ).generate("video_id"); + assertEquals(VIDEO_UPLOAD_PATH + "vc_h264:basic:3.1:bframes_no/video_id", actual); + } + @Test public void testAudioCodec() { // should support a string value From 709f4a9e0d230bfd5d523d2c2e1bc0afc36d1019 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:58:52 +0300 Subject: [PATCH 494/592] Add lowercase support for headers in API responses --- .../src/test/java/com/cloudinary/TransformationTest.java | 2 +- .../src/main/java/com/cloudinary/http42/api/Response.java | 2 +- .../src/main/java/com/cloudinary/http43/api/Response.java | 2 +- .../src/main/java/com/cloudinary/http44/api/Response.java | 2 +- .../src/main/java/com/cloudinary/http45/api/Response.java | 2 +- .../main/java/com/cloudinary/test/AbstractApiTest.java | 8 ++++++++ 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index a3de2700..2ff10486 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -300,4 +300,4 @@ public void testContextMetadataToUserVariables() { assertEquals("$xpos_ctx:!x_pos!_to_f,$ypos_ctx:!y_pos!_to_f,c_crop,x_$xpos_mul_w,y_$ypos_mul_h", t.generate()); } -} \ No newline at end of file +} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java index dfe44ed6..87de8299 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java @@ -32,7 +32,7 @@ public HttpResponse getRawHttpResponse() { } private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + .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); 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 index fd4bfa58..51cd9219 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java @@ -31,7 +31,7 @@ public HttpResponse getRawHttpResponse() { } private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + .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); 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 index 0beb97c5..0036d453 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java @@ -31,7 +31,7 @@ public HttpResponse getRawHttpResponse() { } private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + .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); 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 index 6cb29455..08121401 100644 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java @@ -31,7 +31,7 @@ public HttpResponse getRawHttpResponse() { } private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-Feature(\\w*)RateLimit(-Limit|-Reset|-Remaining)"); + .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); 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 b9008aa6..c124b11e 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 @@ -557,6 +557,14 @@ public void testRateLimitWithNonEnglishLocale() throws Exception { Assert.assertNotNull(result.apiRateLimit().getReset()); } + @Test + public void testRateLimits() throws Exception { + ApiResponse result = cloudinary.api().usage(new HashMap()); + Assert.assertNotEquals(0, result.apiRateLimit().getLimit()); + Assert.assertNotNull(result.apiRateLimit().getReset()); + Assert.assertNotEquals(0, result.apiRateLimit().getRemaining()); + } + @Test public void test19Ping() throws Exception { // should support ping API call From ac5ab2d0a8d427b4d6d62d2ad99bcadd6df0016e Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 14 Mar 2022 14:24:16 +0200 Subject: [PATCH 495/592] Add tags as an array (#252) * Add addTag and removeTag to receive Strings array * Add add tag function to recieve multiple tags * Add test * Add replace tag as array * Fix addTag, removeTag, replaceTag * Fix comment --- .../main/java/com/cloudinary/Uploader.java | 101 +++++++++++++++++- .../cloudinary/test/AbstractUploaderTest.java | 18 +++- 2 files changed, 113 insertions(+), 6 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 65d74a0f..0531161e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -323,9 +323,35 @@ public Map explode(String public_id, Map options) throws IOException { return callApi("explode", params, options, null); } - // options may include 'exclusive' (boolean) which causes clearing this tag - // from all other resources + /** + * Add a tag to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - The tag to assign. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available parameters for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return A map with the public ids returned from the server + * @throws IOException + */ public Map addTag(String tag, String[] publicIds, Map options) throws IOException { + return addTag(new String[]{tag}, publicIds, options); + } + + /** + * Add a tag to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - An array of tags to assign. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available parameters for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return A map with the public ids returned from the server. + * @throws IOException + */ + public Map addTag(String[] tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); boolean exclusive = ObjectUtils.asBoolean(options.get("exclusive"), false); @@ -333,30 +359,97 @@ public Map addTag(String tag, String[] publicIds, Map options) throws IOExceptio return callTagsApi(tag, command, publicIds, options); } + /** + * Remove a tag to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - The tag to remove. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available parameters for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return - A map with the public ids returned from the server. + * @throws IOException + */ public Map removeTag(String tag, String[] publicIds, Map options) throws IOException { + return removeTag(new String[]{tag}, publicIds, options); + } + + /** + * Remove tags to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - The array of tags to remove. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available parameters for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return - * @return - A map with the public ids returned from the server. + * @throws IOException + */ + public Map removeTag(String[] tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); return callTagsApi(tag, Command.remove, publicIds, options); } + /** + * Remove an array of tags to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available parameters for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return - * @return - A map with the public ids returned from the server. + * @throws IOException + */ public Map removeAllTags(String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); return callTagsApi(null, Command.removeAll, publicIds, options); } + /** + * Replaces a tag to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - The tag to replace. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available options for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return - A map with the public ids returned from the server. + * @throws IOException + */ public Map replaceTag(String tag, String[] publicIds, Map options) throws IOException { + return replaceTag(new String[]{tag}, publicIds, options); + } + + /** + * Replaces tags to one or more assets in your cloud. + * Tags are used to categorize and organize your images, and can also be used to apply group actions to images, + * for example to delete images, create sprites, ZIP files, JSON lists, or animated GIFs. + * Each image can be assigned one or more tags, which is a short name that you can dynamically use (no need to predefine tags). + * @param tag - An array of tag to replace. + * @param publicIds - An array of Public IDs of images uploaded to Cloudinary. + * @param options - An object holding the available options for the request. + * options may include 'exclusive' (boolean) which causes clearing this tag from all other resources + * @return - A map with the public ids returned from the server. + * @throws IOException + */ + public Map replaceTag(String[] tag, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); return callTagsApi(tag, Command.replace, publicIds, options); } - public Map callTagsApi(String tag, String command, String[] publicIds, Map options) throws IOException { + public Map callTagsApi(String[] tag, String command, String[] publicIds, Map options) throws IOException { if (options == null) options = ObjectUtils.emptyMap(); Map params = new HashMap(); if (tag != null) { - params.put("tag", tag); + params.put("tag", StringUtils.join(tag, ",")); } params.put("command", command); params.put("type", (String) options.get("type")); 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 d0f96181..bfce72dd 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 @@ -342,21 +342,35 @@ public void testTags() throws Exception { Map result2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.emptyMap()); String public_id2 = (String) result2.get("public_id"); addToDeleteList("upload", public_id2); + + //Test add tags cloudinary.uploader().addTag("tag1", new String[]{public_id, public_id2}, ObjectUtils.emptyMap()); cloudinary.uploader().addTag("tag2", new String[]{public_id}, ObjectUtils.emptyMap()); + cloudinary.uploader().addTag(new String[]{"tag4","tag5"}, new String[]{public_id}, ObjectUtils.emptyMap()); List tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); - assertEquals(tags, asArray(new String[]{"tag1", "tag2"})); + assertEquals(tags, asArray(new String[]{"tag1", "tag2", "tag4", "tag5"})); tags = (List) cloudinary.api().resource(public_id2, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, asArray(new String[]{"tag1"})); + + //Test remove tags cloudinary.uploader().removeTag("tag1", new String[]{public_id}, ObjectUtils.emptyMap()); tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, asArray(new String[]{"tag2", "tag4", "tag5"})); + cloudinary.uploader().removeTag(new String[]{"tag4", "tag5"}, new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, asArray(new String[]{"tag2"})); + + //Test replace tags cloudinary.uploader().replaceTag("tag3", new String[]{public_id}, ObjectUtils.emptyMap()); tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); assertEquals(tags, asArray(new String[]{"tag3"})); + cloudinary.uploader().replaceTag(new String[]{"tag6", "tag7"}, new String[]{public_id}, ObjectUtils.emptyMap()); + tags = (List) cloudinary.api().resource(public_id, ObjectUtils.emptyMap()).get("tags"); + assertEquals(tags, asArray(new String[]{"tag6", "tag7"})); + + //Test remove all tags result = cloudinary.uploader().removeAllTags(new String[]{public_id, public_id2, "noSuchId"}, ObjectUtils.emptyMap()); List publicIds = (List) result.get("public_ids"); - assertThat(publicIds, containsInAnyOrder(public_id, public_id2)); // = and not containing "noSuchId" result = cloudinary.api().resource(public_id, ObjectUtils.emptyMap()); assertThat((Map) result, not(hasKey("tags"))); From d0bc4bdeb9502840790d21d6edead4856fdf6da2 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 14 Mar 2022 15:33:51 +0200 Subject: [PATCH 496/592] Add `enabled` parameter to `updateUser`, `replaceUser` and `createUser` --- .../com/cloudinary/provisioning/Account.java | 47 +++++++++-- .../test/AbstractAccountApiTest.java | 79 +++++++++++++++++-- 2 files changed, 113 insertions(+), 13 deletions(-) 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 d81ee527..55860aa5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -5,7 +5,6 @@ import com.cloudinary.Util; import com.cloudinary.api.ApiResponse; import com.cloudinary.utils.ObjectUtils; - import java.util.*; /** @@ -327,7 +326,7 @@ public ApiResponse users(Boolean pending, List userIds, String prefix, S * @throws Exception If the request fails. */ public ApiResponse createUser(String name, String email, Role role, List subAccountsIds) throws Exception { - return createUser(name, email, role, subAccountsIds); + return createUser(name, email, role, subAccountsIds, null); } /** @@ -343,8 +342,25 @@ public ApiResponse createUser(String name, String email, Role role, List * @throws Exception If the request fails. */ public ApiResponse createUser(String name, String email, Role role, List subAccountsIds, Map options) throws Exception { + return createUser(name, email, role, null, subAccountsIds, options); + } + + /** + * Create a new user. + * + * @param name Required. Username. + * @param email Required. User's email. + * @param role Required. User's role. + * @param enabled Optional. User's status (enabled or disabled). + * @param subAccountsIds Optional. Sub-accounts for which the user should have access. + * If not provided or empty, user should have access to all accounts. + * @param options Generic advanced options map, see online documentation. + * @return The newly created user details. + * @throws Exception If the request fails. + */ + public ApiResponse createUser(String name, String email, Role role, Boolean enabled, List subAccountsIds, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS); - return performUserAction(Api.HttpMethod.POST, uri, email, name, role, subAccountsIds, options); + return performUserAction(Api.HttpMethod.POST, uri, email, name, role, enabled, subAccountsIds, options); } /** @@ -377,8 +393,26 @@ public ApiResponse updateUser(String userId, String name, String email, Role rol * @throws Exception If the request fails. */ public ApiResponse updateUser(String userId, String name, String email, Role role, List subAccountsIds, Map options) throws Exception { + return updateUser(userId, name ,email ,role ,null , subAccountsIds , options); + } + + /** + * Update an existing user. + * + * @param userId The id of the user to update. + * @param name Username. + * @param email User's email. + * @param role User's role. + * @param enabled User's status (enabled or disabled) + * @param subAccountsIds Sub-accounts for which the user should have access. + * If not provided or empty, user should have access to all accounts. + * @param options Generic advanced options map, see online documentation. + * @return The updated user details + * @throws Exception If the request fails. + */ + public ApiResponse updateUser(String userId, String name, String email, Role role, Boolean enabled, List subAccountsIds, Map options) throws Exception { List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, USERS, userId); - return performUserAction(Api.HttpMethod.PUT, uri, email, name, role, subAccountsIds, options); + return performUserAction(Api.HttpMethod.PUT, uri, email, name, role, enabled, subAccountsIds, options); } /** @@ -597,7 +631,7 @@ public ApiResponse userGroupUsers(String groupId, Map options) t * @return The response of the api call. * @throws Exception If the request fails. */ - private ApiResponse performUserAction(Api.HttpMethod method, List uri, String email, String name, Role role, List subAccountsIds, Map options) throws Exception { + private ApiResponse performUserAction(Api.HttpMethod method, List uri, String email, String name, Role role, Boolean enabled, List subAccountsIds, Map options) throws Exception { options = verifyOptions(options); options.put("content_type", "json"); @@ -605,6 +639,7 @@ private ApiResponse performUserAction(Api.HttpMethod method, List uri, S "email", email, "name", name, "role", role == null ? null : role.serializedValue, + "enabled", enabled, "sub_account_ids", subAccountsIds), options); } @@ -616,4 +651,4 @@ private Map verifyOptions(Map options) { return options; } -} \ No newline at end of file +} 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 8b35075e..0fe3c95a 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 @@ -4,6 +4,7 @@ import com.cloudinary.Cloudinary; import com.cloudinary.api.ApiResponse; import com.cloudinary.provisioning.Account; +import com.cloudinary.utils.ObjectUtils; import org.junit.*; import org.junit.rules.ExpectedException; import org.junit.rules.TestName; @@ -13,8 +14,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; public abstract class AbstractAccountApiTest extends MockableTest { private static Random rand = new Random(); @@ -247,6 +247,27 @@ public void testCreateUser() throws Exception { assertNotNull(result); } + @Test + public void testCreateUserWithOptions() throws Exception { + ApiResponse createResult = createSubAccount(); + ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), ObjectUtils.emptyMap()); + assertNotNull(result); + } + + @Test + public void testCreateUserEnabled() throws Exception { + ApiResponse createResult = createSubAccount(); + ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), true); + assertTrue((Boolean) result.get("enabled")); + } + + @Test + public void testCreateUserDisabled() throws Exception { + ApiResponse createResult = createSubAccount(); + ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), false); + assertFalse((Boolean) result.get("enabled")); + } + @Test public void testUpdateUser() throws Exception { ApiResponse user = createUser(Account.Role.ADMIN); @@ -259,6 +280,30 @@ public void testUpdateUser() throws Exception { deleteUser(userId); } + @Test + public void testUpdateUserEnabled() throws Exception { + ApiResponse user = createUser(Account.Role.ADMIN); + String userId = user.get("id").toString(); + String newName = randomLetters(); + ApiResponse result = account.updateUser(userId, newName, null, null, true, null, null); + + assertNotNull(result); + assertTrue((Boolean) result.get("enabled")); + deleteUser(userId); + } + + @Test + public void testUpdateUserDisabled() throws Exception { + ApiResponse user = createUser(Account.Role.ADMIN); + String userId = user.get("id").toString(); + String newName = randomLetters(); + ApiResponse result = account.updateUser(userId, newName, null, null, false, null, null); + + assertNotNull(result); + assertFalse((Boolean) result.get("enabled")); + deleteUser(userId); + } + @Test public void testDeleteUser() throws Exception { ApiResponse user = createUser(Collections.emptyList()); @@ -354,26 +399,47 @@ private ApiResponse createGroup() throws Exception { ApiResponse userGroup = account.createUserGroup(name); createdGroupIds.add(userGroup.get("id").toString()); return userGroup; + } + private ApiResponse createUser() throws Exception { + return createUser(Collections.emptyList()); } private ApiResponse createUser(Account.Role role) throws Exception { return createUser(Collections.emptyList(), role); } - private ApiResponse createUser() throws Exception { - return createUser(Collections.emptyList()); - } - private ApiResponse createUser(List subAccountsIds) throws Exception { return createUser(subAccountsIds, Account.Role.BILLING); } + private ApiResponse createUser(List subAccountsIds, Map options) throws Exception { + return createUser(subAccountsIds, Account.Role.BILLING, options); + } + + private ApiResponse createUser(List subAccountsIds, Boolean enabled) throws Exception { + return createUser(subAccountsIds, Account.Role.BILLING, enabled); + } + private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { String email = String.format("%s@%s.com", randomLetters(), randomLetters()); return createUser("TestName", email, role, subAccountsIds); } + private ApiResponse createUser(List subAccountsIds, Account.Role role, Map options) throws Exception { + String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, null, subAccountsIds, options); + createdUserIds.add(user.get("id").toString()); + return user; + } + + private ApiResponse createUser(List subAccountsIds, Account.Role role, Boolean enabled) throws Exception { + String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, enabled, subAccountsIds, null); + createdUserIds.add(user.get("id").toString()); + return user; + } + private ApiResponse createUser(final String name, String email, Account.Role role, List subAccountsIds) throws Exception { ApiResponse user = account.createUser(name, email, role, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); @@ -401,7 +467,6 @@ private static String randomLetters() { for (int i = 0; i < 10; i++) { sb.append((char) ('a' + rand.nextInt('z' - 'a' + 1))); } - return sb.toString(); } } From f964084cafbc2a03045547a2b7ac8fcf2c2b504d Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Thu, 17 Mar 2022 10:04:42 +0300 Subject: [PATCH 497/592] Get resources by asset id --- .../src/main/java/com/cloudinary/Api.java | 8 ++++++++ .../java/com/cloudinary/test/AbstractApiTest.java | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 8b1caeee..990ecc2d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -115,6 +115,14 @@ public ApiResponse resourcesByContext(String key, String value, Map options) thr return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "context"), params, options); } + public ApiResponse resourcesByAssetIDs(Iterable assetIds, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + Map params = ObjectUtils.only(options, "public_ids", "tags", "context", "moderations"); + params.put("asset_ids", assetIds); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", "by_asset_ids"), params, options); + return response; + } + public ApiResponse resourcesByIds(Iterable publicIds, 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 c124b11e..d950ac6f 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 @@ -49,6 +49,8 @@ abstract public class AbstractApiTest extends MockableTest { public static final Set createdFolders = new HashSet(); private static final String CUSTOM_USER_AGENT_PREFIX = "TEST_USER_AGENT"; private static final String CUSTOM_USER_AGENT_VERSION = "9.9.9"; + private static String assetId1; + private static String assetId2; protected Api api; @@ -66,10 +68,10 @@ public static void setUpClass() throws IOException { Map options = ObjectUtils.asMap("public_id", API_TEST, "tags", uploadAndDirectionTag, "context", "key=value", "eager", Collections.singletonList(EXPLICIT_TRANSFORMATION)); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + assetId1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options).get("asset_id").toString(); options.put("public_id", API_TEST_1); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + assetId2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options).get("asset_id").toString(); options.remove("public_id"); options.put("eager", Collections.singletonList(UPDATE_TRANSFORMATION)); @@ -269,6 +271,15 @@ public void testTransformationsWithCursor() throws Exception { assertThat(transformations, hasItem(allOf(hasEntry("name", "t_" + name)))); } + @Test + public void testResourcesByAssetIds() throws Exception { + Map result = api.resourcesByAssetIDs(Arrays.asList(assetId1, assetId2), ObjectUtils.asMap("tags", true, "context", true)); + List resources = (List) result.get("resources"); + assertEquals(2, resources.size()); + assertNotNull(findByAttr(resources, "public_id", API_TEST)); + assertNotNull(findByAttr(resources, "public_id", API_TEST_1)); + } + @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids From 66b7872c2f268283afb02eb2d1dc1abd0254a906 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Thu, 17 Mar 2022 10:06:14 +0300 Subject: [PATCH 498/592] Disables provisioning tests if environment variable is not set --- cloudinary-http42/build.gradle | 5 ++++- cloudinary-http43/build.gradle | 5 ++++- cloudinary-http44/build.gradle | 5 ++++- cloudinary-http45/build.gradle | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index 6b1a0cba..cb8ec796 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -11,6 +11,9 @@ apply from: "../java_shared.gradle" task ciTest( type: Test ) { useJUnit { excludeCategories 'com.cloudinary.test.TimeoutTest' + if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { + exclude '**/AccountApiTest.class' + } } } @@ -118,4 +121,4 @@ if (hasProperty("ossrhPassword")) { } } } -} \ No newline at end of file +} diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 53a0f238..6b22abc2 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -11,6 +11,9 @@ apply from: "../java_shared.gradle" task ciTest( type: Test ) { useJUnit { excludeCategories 'com.cloudinary.test.TimeoutTest' + if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { + exclude '**/AccountApiTest.class' + } } } @@ -117,4 +120,4 @@ if (hasProperty("ossrhPassword")) { } } } -} \ No newline at end of file +} diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index 1312d2b9..7d533e05 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -11,6 +11,9 @@ apply from: "../java_shared.gradle" task ciTest( type: Test ) { useJUnit { excludeCategories 'com.cloudinary.test.TimeoutTest' + if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { + exclude '**/AccountApiTest.class' + } } } @@ -117,4 +120,4 @@ if (hasProperty("ossrhPassword")) { } } } -} \ No newline at end of file +} diff --git a/cloudinary-http45/build.gradle b/cloudinary-http45/build.gradle index 13be8f83..2d88dc60 100644 --- a/cloudinary-http45/build.gradle +++ b/cloudinary-http45/build.gradle @@ -11,6 +11,9 @@ apply from: "../java_shared.gradle" task ciTest( type: Test ) { useJUnit { excludeCategories 'com.cloudinary.test.TimeoutTest' + if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { + exclude '**/AccountApiTest.class' + } } } @@ -117,4 +120,4 @@ if (hasProperty("ossrhPassword")) { } } } -} \ No newline at end of file +} From 9ab86add0c806c669b0fcb0e2071e3e1e2610ff7 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 21 Mar 2022 12:30:25 +0200 Subject: [PATCH 499/592] Version 1.31.0 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98db5a9a..01a6586a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ +1.31.0 / 2022-03-21 +==================== + +New functionality +----------------- + * Get resources by asset id + * Add `enabled` parameter to `updateUser`, `replaceUser` and `createUser` + * Add tags as an array + * Add lowercase support for headers in API responses + * Allow to disable b-frames + * Support download backup version api + * Support `filename_override` upload parameter + * Add support for single character variable + 1.30.0 / 2022-02-02 =================== diff --git a/README.md b/README.md index 08c54813..1b4cc58e 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.30.0 | V | +| 1.1.0 - 1.31.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-http44 - 1.30.0 + 1.31.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 7fd3b80c..460beda5 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.30.0"; + public final static String VERSION = "1.31.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 1c79d617..d601b863 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.30.0 +version=1.31.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 5bbd8037a77d7abf008a6da735db601a268095ec Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 22 Mar 2022 16:04:47 +0200 Subject: [PATCH 500/592] Remove jcenter --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 4a3b9f77..7fff49dc 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,6 @@ import groovy.json.JsonSlurper allprojects { repositories { - jcenter() mavenCentral() } @@ -41,4 +40,4 @@ tasks.create('createTestSubAccount') { println("Test sub-account created succesfully!") } -} \ No newline at end of file +} From 14c0d95803dbc2cc067cdefafe5a49a95a7c8190 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:05:27 +0300 Subject: [PATCH 501/592] Validate auth token --- .../main/java/com/cloudinary/AuthToken.java | 28 +++++++++++---- .../java/com/cloudinary/AuthTokenTest.java | 34 +++++++++++++++++++ .../com/cloudinary/test/CloudinaryTest.java | 3 ++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index c12698ad..aa8cf213 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -29,7 +29,7 @@ public class AuthToken { private long startTime; private long expiration; private String ip; - private String acl; + private List acl = new ArrayList<>(); private long duration; private boolean isNullToken = false; private static final Pattern UNSAFE_URL_CHARS_PATTERN = Pattern.compile("[ \"#%&'/:;<=>?@\\[\\\\\\]^`{|}~]"); @@ -53,7 +53,16 @@ public AuthToken(Map options) { this.startTime = ObjectUtils.asLong(options.get("startTime"), 0L); this.expiration = ObjectUtils.asLong(options.get("expiration"), 0L); this.ip = (String) options.get("ip"); - this.acl = (String) options.get("acl"); + + Object acl = options.get("acl"); + if (acl != null) { + if (acl instanceof String) { + this.acl = Collections.singletonList(acl.toString()); + } else if (Collection.class.isAssignableFrom(acl.getClass())) { + this.acl = new ArrayList((Collection)acl); + } + } + this.duration = ObjectUtils.asLong(options.get("duration"), 0L); } } @@ -122,8 +131,8 @@ public AuthToken ip(String ip) { * @param acl * @return this */ - public AuthToken acl(String acl) { - this.acl = acl; + public AuthToken acl(String... acl) { + this.acl = Arrays.asList(acl); return this; } @@ -155,6 +164,11 @@ public String generate() { * @return a URL token */ public String generate(String url) { + + if (url == null && (acl == null || acl.size() == 0)) { + throw new IllegalArgumentException("Must provide acl or url"); + } + long expiration = this.expiration; if (expiration == 0) { if (duration > 0) { @@ -172,11 +186,11 @@ public String generate(String url) { tokenParts.add("st=" + startTime); } tokenParts.add("exp=" + expiration); - if (acl != null) { - tokenParts.add("acl=" + escapeToLower(acl)); + if (acl != null && acl.size() > 0) { + tokenParts.add("acl=" + escapeToLower(String.join("!", acl))); } ArrayList toSign = new ArrayList(tokenParts); - if (url != null && acl == null) { + if (url != null && (acl == null || acl.size() == 0)) { toSign.add("url=" + escapeToLower(url)); } String auth = digest(StringUtils.join(toSign, "~")); diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 800fc581..8f3a9f75 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -1,7 +1,11 @@ package com.cloudinary; import com.cloudinary.utils.Analytics; +import com.cloudinary.utils.ObjectUtils; + +import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -9,6 +13,8 @@ import java.io.UnsupportedEncodingException; import java.util.Calendar; +import java.util.Collections; +import java.util.Map; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -127,4 +133,32 @@ public void testIgnoreUrlIfAclIsProvided() { String cookieAclToken = aclToken.generate("http://res.cloudinary.com/test123/image/upload/v1486020273/sample.jpg"); assertEquals(cookieToken, cookieAclToken); } + + @Test + public void testMultiplePatternsInAcl() { + AuthToken token = new AuthToken(KEY).duration(3600).acl("/image/authenticated/*", "/image2/authenticated/*", "/image3/authenticated/*").startTime(22222222); + String cookieToken = token.generate(); + Assert.assertThat(cookieToken, CoreMatchers.containsString("~acl=%2fimage%2fauthenticated%2f*!%2fimage2%2fauthenticated%2f*!%2fimage3%2fauthenticated%2f*~")); + } + + @Test + public void testPublicAclInitializationFromMap() { + Map options = ObjectUtils.asMap( + "acl", Collections.singleton("foo"), + "expiration", 100, + "key", KEY, + "tokenName", "token"); + String token = new AuthToken(options).generate(); + assertEquals("token=exp=100~acl=foo~hmac=88be250f3a912add862959076ee74f392fa0959a953fddd9128787d5c849efd9", token); + } + + @Test(expected = IllegalArgumentException.class) + public void testMissingAclAndUrlShouldThrow() { + String token = new AuthToken(KEY).duration(300).generate(); + } + + @Test + public void testMissingUrlNotMissingAclShouldNotThrow() { + String token = new AuthToken(KEY).duration(300).generate("http://res.cloudinary.com/test123"); + } } 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 f4f94c43..763cb286 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -18,6 +18,7 @@ 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; @@ -1468,6 +1469,8 @@ private void setRandomValue(Random rand, Field field, Object instance) throws Il field.set(instance, rand.nextInt()); } else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) { field.set(instance, rand.nextLong()); + } else if (field.get(instance) instanceof List) { + field.set(instance, Collections.singletonList(cloudinary.randomPublicId())); } else if (fieldType.equals(String.class)) { field.set(instance, cloudinary.randomPublicId()); } else if (fieldType.equals(AuthToken.class)) { From ff53df7eb5b063d4f85011b5e6b6da023f3de717 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:56:28 +0300 Subject: [PATCH 502/592] Tests that rely on addons should not run by default --- .../com/cloudinary/test/AbstractApiTest.java | 3 ++- .../com/cloudinary/test/MockableTest.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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 d950ac6f..d31cd689 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 @@ -608,7 +608,8 @@ public void testManualModeration() throws Exception { } @Test - public void testOcrUpdate() { + public void testOcrUpdate() throws Exception { + assumeAddonEnabled("ocr"); Exception expected = null; // should support requesting ocr info try { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 18043d3e..c68a4131 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -4,7 +4,11 @@ import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; +import static org.junit.Assume.assumeTrue; + import java.io.IOException; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Random; @@ -36,4 +40,22 @@ protected Map preloadResource(Map options) throws IOException { combinedOptions.putAll(options); return cloudinary.uploader().upload("http://res.cloudinary.com/demo/image/upload/sample", combinedOptions); } + + private static final List enabledAddons = getEnabledAddons(); + + protected void assumeAddonEnabled(String addon) throws Exception { + boolean enabled = enabledAddons.contains(addon.toLowerCase()) + || (enabledAddons.size() == 1 && enabledAddons.get(0).equalsIgnoreCase("all")); + + assumeTrue(String.format("Use CLD_TEST_ADDONS environment variable to enable tests for %s.", addon), enabled); + } + + private static List getEnabledAddons() { + String envAddons = System.getenv() + .getOrDefault("CLD_TEST_ADDONS", "") + .toLowerCase() + .replaceAll("\\s", ""); + + return Arrays.asList(envAddons.split(",")); + } } From 8f222040921c2f11e9eb343f26784340698dbb12 Mon Sep 17 00:00:00 2001 From: daplf Date: Tue, 22 Mar 2022 14:57:43 +0000 Subject: [PATCH 503/592] Fix `verifySignature` timestamp units --- .../api/signing/NotificationRequestSignatureVerifier.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java index 62ad579b..1b5d3e89 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java @@ -54,7 +54,7 @@ public boolean verifySignature(String body, String timestamp, String signature) * * @param body notification message body, represented as string * @param timestamp value of X-Cld-Timestamp custom HTTP header of notification message, representing notification - * issue timestamp + * issue timestamp in seconds * @param signature actual signature value, usually passed via X-Cld-Signature custom HTTP header of notification * message * @param secondsValidFor the amount of time, in seconds, the notification message is considered valid by client @@ -69,7 +69,7 @@ public boolean verifySignature(String body, String timestamp, String signature, } return verifySignature(body, timestamp, signature) && - (System.currentTimeMillis() - parsedTimestamp <= secondsValidFor * 1000L); + (System.currentTimeMillis() / 1000L - parsedTimestamp <= secondsValidFor); } } From d5d21a87b4f06ed704aaa25fb53e674fbf6707ff Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:58:12 +0300 Subject: [PATCH 504/592] Support metadata fields reordering --- .../src/main/java/com/cloudinary/Api.java | 23 +++++++++++++ .../test/AbstractStructuredMetadataTest.java | 34 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 990ecc2d..3400364a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -710,6 +710,29 @@ public ApiResponse deleteMetadataField(String fieldExternalId) throws Exception return callApi(HttpMethod.DELETE, uri, Collections.emptyMap(), Collections.emptyMap()); } + /** + * Reorders metadata fields. + * + * @param orderBy Criteria for the order (one of the fields 'label', 'external_id', 'created_at') + * @param direction Optional (gets either asc or desc) + * @param options Additional options + * @return List of metadata fields in their new order + * @throws Exception + */ + public ApiResponse reorderMetadataFields(String orderBy, String direction, Map options) throws Exception { + if (orderBy == null) { + throw new IllegalArgumentException("Must supply orderBy"); + } + + List uri = Arrays.asList("metadata_fields", "order"); + Map map = ObjectUtils.asMap("order_by", orderBy); + if (direction != null) { + map.put("direction", direction); + } + + return callApi(HttpMethod.PUT, uri, map, 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/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index 7b06c557..48c60bc3 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 @@ -5,6 +5,8 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.metadata.*; + +import org.hamcrest.Matchers; import org.junit.*; import org.junit.rules.TestName; @@ -172,6 +174,38 @@ public void testRestoreDatasourceEntries() throws Exception { assertNotNull(result); } + @Test + 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")); + + result = api.reorderMetadataFields("label", "desc", Collections.EMPTY_MAP); + assertThat(getField(result, 0), Matchers.containsString("zzz")); + + result = api.reorderMetadataFields("label", "asc", Collections.EMPTY_MAP); + assertThat(getField(result, 0), Matchers.containsString("aaa")); + } + + @Test(expected = IllegalArgumentException.class) + public void testReorderMetadataFieldsOrderByIsRequired() throws Exception { + api.reorderMetadataFields(null, null, Collections.EMPTY_MAP); + } + + private String getField(ApiResponse result, int index) { + String actual = ((Map)((ArrayList)result.get("metadata_fields")).get(index)).get("label").toString(); + return actual; + } + + private void AddStringField(String labelPrefix) throws Exception { + StringMetadataField field = newFieldInstance(labelPrefix); + ApiResponse fieldResult = addFieldToAccount(field); + String fieldId = fieldResult.get("external_id").toString(); + } + @Test public void testUploadWithMetadata() throws Exception { StringMetadataField field = newFieldInstance("testUploadWithMetadata"); From c287a620eaf5056d430498462652224f82d50a0e Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:58:58 +0300 Subject: [PATCH 505/592] Search by asset id --- .../java/com/cloudinary/test/AbstractSearchTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 212b15e3..be97f3f9 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 @@ -21,6 +21,7 @@ abstract public class AbstractSearchTest extends MockableTest { private static final String SEARCH_TEST = "search_test_" + SUFFIX; private static final String SEARCH_TEST_1 = SEARCH_TEST + "_1"; private static final String SEARCH_TEST_2 = SEARCH_TEST + "_2"; + private static String SEARCH_TEST_ASSET_ID_1; @BeforeClass public static void setUpClass() throws Exception { @@ -29,7 +30,7 @@ public static void setUpClass() throws Exception { cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); options = ObjectUtils.asMap("public_id", SEARCH_TEST_1, "tags", UPLOAD_TAGS, "context", "stage=new"); - cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + SEARCH_TEST_ASSET_ID_1 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options).get("asset_id").toString(); options = ObjectUtils.asMap("public_id", SEARCH_TEST_2, "tags", UPLOAD_TAGS, "context", "stage=validated"); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); try { @@ -66,6 +67,13 @@ public void shouldFindResourceByPublicId() throws Exception { assertEquals(1, resources.size()); } + @Test + public void shouldFindResourceByAssetId() throws Exception { + Map result = cloudinary.search().expression(String.format("asset_id:%s", SEARCH_TEST_ASSET_ID_1)).execute(); + List resources = (List) result.get("resources"); + assertEquals(1, resources.size()); + } + @Test public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() throws Exception { List resources; From 65fd1e32dd130959ad860779d384d233682b5f0f Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:59:24 +0300 Subject: [PATCH 506/592] Get the details of a single resource by asset_id --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 6 ++++++ .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 3400364a..200000b0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -115,6 +115,12 @@ public ApiResponse resourcesByContext(String key, String value, Map options) thr return callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "context"), params, options); } + public ApiResponse resourceByAssetID(String assetId, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + Map params = ObjectUtils.only(options, "tags", "context", "moderations"); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", assetId), params, options); + return response; + } public ApiResponse resourcesByAssetIDs(Iterable assetIds, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); Map params = ObjectUtils.only(options, "public_ids", "tags", "context", "moderations"); 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 d31cd689..f99ec8f0 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 @@ -280,6 +280,12 @@ public void testResourcesByAssetIds() throws Exception { assertNotNull(findByAttr(resources, "public_id", API_TEST_1)); } + @Test + public void testResourceByAssetId() throws Exception { + Map result = api.resourceByAssetID(assetId1, ObjectUtils.asMap("tags", true, "context", true)); + assertEquals(API_TEST, result.get("public_id").toString()); + } + @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids From 97e47c103160049ddbaf24b01ad7815b0e486bc3 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 18:01:13 +0300 Subject: [PATCH 507/592] Support start offset and end offset in variable materialization --- .../java/com/cloudinary/Transformation.java | 4 +- .../com/cloudinary/TransformationTest.java | 69 +++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index 8b3d8c40..c4b2ca9e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -785,7 +785,7 @@ public String generate(Map options) { } if (!components.isEmpty()) { final String joined = StringUtils.join(components, ","); - transformations.add(Expression.normalize(joined)); + transformations.add(joined); } if (isResponsive) { @@ -894,7 +894,7 @@ private static String normRangeValue(Object objectValue) { Matcher matcher = RANGE_VALUE_RE.matcher(value); if (!matcher.matches()) { - return null; + return Expression.normalize(value); } String modifier = ""; diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 2ff10486..200efc14 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -4,9 +4,14 @@ import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; import org.cloudinary.json.JSONArray; +import org.hamcrest.CoreMatchers; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import java.util.*; @@ -18,6 +23,7 @@ * */ @SuppressWarnings("unchecked") +@RunWith(JUnitParamsRunner.class) public class TransformationTest { @Before @@ -300,4 +306,67 @@ public void testContextMetadataToUserVariables() { assertEquals("$xpos_ctx:!x_pos!_to_f,$ypos_ctx:!y_pos!_to_f,c_crop,x_$xpos_mul_w,y_$ypos_mul_h", t.generate()); } + + @Parameters({ "angle", + "aspect_ratio", + "dpr", + "effect", + "height", + "opacity", + "quality", + "width", + "x", + "y", + "end_offset", + "start_offset", + "zoom" }) + @Test + public void testVerifyNormalizationShouldNormalize(String input) throws Exception { + String t = new Transformation().param(input, "width * 2").generate(); + assertThat(t, CoreMatchers.containsString("w_mul_2")); + } + + @Parameters({ + "audio_codec", + "audio_frequency", + "border", + "bit_rate", + "color_space", + "default_image", + "delay", + "density", + "fetch_format", + "custom_function", + "fps", + "gravity", + "overlay", + "prefix", + "page", + "underlay", + "video_sampling", + "streaming_profile", + "keyframe_interval"}) + @Test + public void test1VerifyNormalizationShouldNotNormalize(String input) throws Exception { + String t = new Transformation().param(input, "width * 2").generate(); + assertThat(t, CoreMatchers.not(CoreMatchers.containsString("w_mul_2"))); + } + + @Test + public void testSupportStartOffset() throws Exception { + String t = new Transformation().width(100).startOffset("idu - 5").generate(); + assertThat(t, CoreMatchers.containsString("so_idu_sub_5")); + + t = new Transformation().width(100).startOffset("$logotime").generate(); + assertThat(t, CoreMatchers.containsString("so_$logotime")); + } + + @Test + public void testSupportEndOffset() throws Exception { + String t = new Transformation().width(100).endOffset("idu - 5").generate(); + assertThat(t, CoreMatchers.containsString("eo_idu_sub_5")); + + t = new Transformation().width(100).endOffset("$logotime").generate(); + assertThat(t, CoreMatchers.containsString("eo_$logotime")); + } } From 6297083be37d91f1415f36edb06885b446317e41 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Tue, 22 Mar 2022 19:38:02 +0300 Subject: [PATCH 508/592] Verify expression normalization --- .../src/main/java/com/cloudinary/Transformation.java | 1 + .../src/test/java/com/cloudinary/TransformationTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index c4b2ca9e..eaf2f4a9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -748,6 +748,7 @@ public String generate(Map options) { params.put("a", Expression.normalize(angle)); params.put("ar", Expression.normalize(options.get("aspect_ratio"))); + params.put("iar", Expression.normalize(options.get("initial_aspect_ratio"))); params.put("b", background); params.put("c", crop); params.put("co", color); diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 200efc14..66244e9a 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -306,7 +306,7 @@ public void testContextMetadataToUserVariables() { assertEquals("$xpos_ctx:!x_pos!_to_f,$ypos_ctx:!y_pos!_to_f,c_crop,x_$xpos_mul_w,y_$ypos_mul_h", t.generate()); } - + @Parameters({ "angle", "aspect_ratio", "dpr", @@ -347,7 +347,7 @@ public void testVerifyNormalizationShouldNormalize(String input) throws Exceptio "streaming_profile", "keyframe_interval"}) @Test - public void test1VerifyNormalizationShouldNotNormalize(String input) throws Exception { + public void testVerifyNormalizationShouldNotNormalize(String input) throws Exception { String t = new Transformation().param(input, "width * 2").generate(); assertThat(t, CoreMatchers.not(CoreMatchers.containsString("w_mul_2"))); } From bec0c3b28a87a2b8d0febfc7e9b3f3d92c6391a6 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 23 Mar 2022 16:34:45 +0200 Subject: [PATCH 509/592] bump versions --- cloudinary-http43/build.gradle | 2 +- samples/photo_album/pom.xml | 4 ++-- samples/photo_album_gae/pom.xml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 6b22abc2..23942327 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -20,7 +20,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.httpcomponents', name: 'httpclient', version: '4.3' + compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.1' compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.3' testCompile project(':cloudinary-test-common') testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index d38291c5..b6210d57 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -8,7 +8,7 @@ photo_album - 4.3.10.RELEASE + 5.3.0 @@ -108,7 +108,7 @@ commons-fileupload commons-fileupload - 1.3 + 1.3.3 diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index f6d84dad..f95b9dc3 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -8,7 +8,7 @@ photo_album_gae - 3.2.16.RELEASE + 5.3.0 1 1.9.37 UTF-8 @@ -111,7 +111,7 @@ commons-fileupload commons-fileupload - 1.3 + 1.3.3 From 94092782ce64bd48519b782de52d52b291d00e39 Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Wed, 23 Mar 2022 17:35:19 +0300 Subject: [PATCH 510/592] Upload APIs rename call should return metadata and context --- .../main/java/com/cloudinary/Uploader.java | 2 ++ .../test/AbstractStructuredMetadataTest.java | 9 ++----- .../cloudinary/test/AbstractUploaderTest.java | 25 +++++++++++++++++++ .../cloudinary/test/MetadataTestHelper.java | 24 ++++++++++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index 0531161e..e9a6909a 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -247,6 +247,8 @@ public Map rename(String fromPublicId, String toPublicId, Map options) throws IO params.put("to_public_id", toPublicId); params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); params.put("to_type", options.get("to_type")); + params.put("context", ObjectUtils.asBoolean(options.get("context"), false).toString()); + params.put("metadata", ObjectUtils.asBoolean(options.get("metadata"), false).toString()); return callApi("rename", params, options, null); } 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 48c60bc3..9aefb64b 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 @@ -302,17 +302,12 @@ private SetMetadataField createSetField(String labelPrefix) { } private StringMetadataField newFieldInstance(String labelPrefix) throws Exception { - StringMetadataField field = new StringMetadataField(); String label = labelPrefix + "_" + SUFFIX; - field.setLabel(label); - field.setMandatory(true); - field.setValidation(new MetadataValidation.StringLength(3, 9)); - field.setDefaultValue("val_test"); - return field; + return MetadataTestHelper.newFieldInstance(label); } private ApiResponse addFieldToAccount(MetadataField field) throws Exception { - ApiResponse apiResponse = api.addMetadataField(field); + ApiResponse apiResponse = MetadataTestHelper.addFieldToAccount(api, field); metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); return apiResponse; } 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 bfce72dd..79046890 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 @@ -219,6 +219,31 @@ public void testRename() throws Exception { assertEquals(cloudinary.api().resource(publicId2, ObjectUtils.emptyMap()).get("format"), "ico"); } + @Test + public void testRenameShouldReturnContext() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG), "context", asMap("foo", "boo"))); + + String publicId = result.get("public_id").toString(); + String publicId2 = "folder/" + publicId + "2"; + Map renameResult = cloudinary.uploader().rename(publicId, publicId2, asMap("context", true)); + assertNotNull(renameResult.get("context")); + } + + @Test + public void testRenameShouldReturnMetadata() throws Exception { + String label = "test" + SUFFIX; + StringMetadataField f = MetadataTestHelper.newFieldInstance(label); + Map fieldResult = MetadataTestHelper.addFieldToAccount(cloudinary.api(), f); + String fieldId = fieldResult.get("external_id").toString(); + Map metadata = Collections.singletonMap(fieldId, "123456"); + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG), "metadata", metadata)); + + String publicId = result.get("public_id").toString(); + String publicId2 = "folder/" + publicId + "2"; + Map renameResult = cloudinary.uploader().rename(publicId, publicId2, asMap("metadata", true)); + assertNotNull(renameResult.get("metadata")); + } + @Test public void testUniqueFilename() throws Exception { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("use_filename", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); 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 new file mode 100644 index 00000000..d130b282 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java @@ -0,0 +1,24 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.metadata.MetadataField; +import com.cloudinary.metadata.MetadataValidation; +import com.cloudinary.metadata.StringMetadataField; + +public class MetadataTestHelper { + public static StringMetadataField newFieldInstance(String label) throws Exception { + StringMetadataField field = new StringMetadataField(); + field.setLabel(label); + field.setMandatory(true); + field.setValidation(new MetadataValidation.StringLength(3, 9)); + field.setDefaultValue("val_test"); + return field; + } + + public static ApiResponse addFieldToAccount(Api api, MetadataField field) throws Exception { + ApiResponse apiResponse = api.addMetadataField(field); + return apiResponse; + } +} + From ff4fd2ed26430b30a578d6b36056254770a72eec Mon Sep 17 00:00:00 2001 From: francistagbo <61406266+francistagbo@users.noreply.github.com> Date: Wed, 23 Mar 2022 23:04:19 +0200 Subject: [PATCH 511/592] Remove duplicates in Search Api fields --- .../src/main/java/com/cloudinary/Search.java | 16 +++++-- .../cloudinary/test/AbstractSearchTest.java | 44 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index dc2f8a96..f16be7eb 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -40,18 +40,28 @@ public Search nextCursor(String value) { } public Search aggregate(String field) { - aggregateParam.add(field); + if (!aggregateParam.contains(field)) { + aggregateParam.add(field); + } return this; } public Search withField(String field) { - withFieldParam.add(field); + if (!withFieldParam.contains(field)) { + withFieldParam.add(field); + } return this; } public Search sortBy(String field, String dir) { HashMap sortBucket = new HashMap(); sortBucket.put(field, dir); + for (int i = 0; i < sortByParam.size(); i++) { + if (sortByParam.get(i).containsKey(field)){ + sortByParam.add(i, sortBucket); + return this; + } + } sortByParam.add(sortBucket); return this; } @@ -68,4 +78,4 @@ public ApiResponse execute() throws Exception { Map options = ObjectUtils.asMap("content_type", "json"); return this.api.callApi(Api.HttpMethod.POST, Arrays.asList("resources", "search"), this.toQuery(), options); } -} \ No newline at end of file +} 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 be97f3f9..67d62967 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,15 +1,15 @@ package com.cloudinary.test; import com.cloudinary.Cloudinary; +import com.cloudinary.Search; import com.cloudinary.utils.ObjectUtils; import org.junit.*; import org.junit.rules.TestName; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Field; +import java.util.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; @SuppressWarnings({"rawtypes", "unchecked", "JavaDoc"}) @@ -74,12 +74,46 @@ public void shouldFindResourceByAssetId() throws Exception { assertEquals(1, resources.size()); } + @Test + public void testShouldNotDuplicateValues() throws Exception { + Search request = cloudinary.search().maxResults(1). + sortBy("created_at", "asc") + .sortBy("created_at", "desc") + .sortBy("public_id", "asc") + .aggregate("format") + .aggregate("format") + .aggregate("resource_type") + .withField("context") + .withField("context") + .withField("tags"); + Field[] fields = Search.class.getDeclaredFields(); + for(Field field : fields) { + if(field.getName() == "aggregateParam") { + field.setAccessible(true); + ArrayList aggregateList = (ArrayList) field.get(request); + Set testSet = new HashSet(aggregateList); + assertTrue(aggregateList.size() == testSet.size()); + } + if (field.getName() == "withFieldParam") { + field.setAccessible(true); + ArrayList withFieldList = (ArrayList) field.get(request); + Set testSet = new HashSet(withFieldList); + assertTrue(withFieldList.size() == testSet.size()); + } + if (field.getName() == "sortByParam") { + field.setAccessible(true); + ArrayList> sortByList = (ArrayList>) field.get(request); + Set> testSet = new HashSet>(sortByList); + assertTrue(sortByList.size() == testSet.size()); + } + } + } + @Test public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() throws Exception { List resources; Map result = cloudinary.search().maxResults(1).expression(String.format("tags:%s", SEARCH_TAG)).sortBy("public_id", "asc").execute(); resources = (List) result.get("resources"); - assertEquals(1, resources.size()); assertEquals(3, result.get("total_count")); assertEquals(SEARCH_TEST, resources.get(0).get("public_id")); From 046777f5702e452fa7b620890f71fa2505860c84 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Thu, 24 Mar 2022 11:08:24 +0200 Subject: [PATCH 512/592] Remove iar from transfomration params --- cloudinary-core/src/main/java/com/cloudinary/Transformation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java index eaf2f4a9..c4b2ca9e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Transformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Transformation.java @@ -748,7 +748,6 @@ public String generate(Map options) { params.put("a", Expression.normalize(angle)); params.put("ar", Expression.normalize(options.get("aspect_ratio"))); - params.put("iar", Expression.normalize(options.get("initial_aspect_ratio"))); params.put("b", background); params.put("c", crop); params.put("co", color); From e47e686908d4ea98a0736da690bdeee703d7f87c Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Wed, 30 Mar 2022 14:45:22 +0300 Subject: [PATCH 513/592] Support structured metadata in `resources` api call --- .../src/main/java/com/cloudinary/Api.java | 8 ++-- .../com/cloudinary/test/AbstractApiTest.java | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 200000b0..74dd6bbc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -88,7 +88,7 @@ public ApiResponse resources(Map options) throws Exception { 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"), options); + ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at", "metadata"), options); return response; } @@ -96,7 +96,7 @@ 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"), options); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata"), options); return response; } @@ -107,7 +107,7 @@ 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"); + Map params = ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata"); params.put("key", key); if (StringUtils.isNotBlank(value)) { params.put("value", value); @@ -143,7 +143,7 @@ public ApiResponse resourcesByModeration(String kind, String status, Map options 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"), options); + 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); 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 f99ec8f0..e7cc0f32 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 @@ -4,6 +4,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.metadata.StringMetadataField; import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; import org.junit.*; @@ -367,6 +368,47 @@ public void testDeleteDerivedByTransformation() throws Exception { assertTrue(derived.size() == 0); } + @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(); + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", public_id, + "tags", UPLOAD_TAGS, + "metadata", ObjectUtils.asMap(fieldId, "test"), + "moderation", "manual", + "context", ObjectUtils.asMap("name", "value"))); + + Map result = api.resources(ObjectUtils.asMap("metadata", false)); + assertNull(getMetadata(public_id, result)); + + result = api.resources(ObjectUtils.asMap("metadata", true)); + assertNotNull(getMetadata(public_id, result)); + + result = api.resourcesByTag(UPLOAD_TAGS[0], ObjectUtils.asMap("metadata", true)); + assertNotNull(getMetadata(public_id, result)); + + result = api.resourcesByTag(UPLOAD_TAGS[0], ObjectUtils.asMap("metadata", false)); + assertNull(getMetadata(public_id, result)); + + result = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("metadata", true)); + assertNotNull(getMetadata(public_id, result)); + + result = api.resourcesByModeration("manual", "pending", ObjectUtils.asMap("metadata", false)); + assertNull(getMetadata(public_id, result)); + + result = api.resourcesByContext("name", "value", ObjectUtils.asMap("metadata", true)); + assertNotNull(getMetadata(public_id, result)); + + result = api.resourcesByContext("name", "value", ObjectUtils.asMap("metadata", false)); + assertNull(getMetadata(public_id, result)); + } + + private Object getMetadata(String public_id, Map result) { + Map resource = findByAttr((List) result.get("resources"), "public_id", public_id); + return resource.get("metadata"); + } + @Test(expected = NotFound.class) public void test09DeleteResources() throws Exception { // should allow deleting resources From cfa2d1082b32d19150e90d3c82f77210cd83971c Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Wed, 30 Mar 2022 16:39:13 +0300 Subject: [PATCH 514/592] Support multiple acls in cookies --- cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 8f3a9f75..ca30479e 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -161,4 +161,6 @@ public void testMissingAclAndUrlShouldThrow() { public void testMissingUrlNotMissingAclShouldNotThrow() { String token = new AuthToken(KEY).duration(300).generate("http://res.cloudinary.com/test123"); } + + } From fd41e665f3bf74980624c7bf94572c3a6754f44f Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Mon, 4 Apr 2022 12:53:33 +0300 Subject: [PATCH 515/592] Fix transformations API call --- .../src/main/java/com/cloudinary/Api.java | 14 ++++++++++---- .../java/com/cloudinary/EagerTransformation.java | 4 ++-- .../java/com/cloudinary/TransformationTest.java | 9 +++++++++ 3 files changed, 21 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 74dd6bbc..ee1123d5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -235,12 +235,15 @@ public ApiResponse transformations(Map options) throws Exception { public ApiResponse transformation(String transformation, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.GET, Arrays.asList("transformations", transformation), ObjectUtils.only(options, "next_cursor", "max_results"), options); + Map map = ObjectUtils.only(options, "next_cursor", "max_results"); + map.put("transformation", transformation); + return callApi(HttpMethod.GET, Arrays.asList("transformations"), map, options); } public ApiResponse deleteTransformation(String transformation, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.DELETE, Arrays.asList("transformations", transformation), ObjectUtils.emptyMap(), options); + Map updates = ObjectUtils.asMap("transformation", transformation); + return callApi(HttpMethod.DELETE, Arrays.asList("transformations"), updates, options); } // updates - currently only supported update are: @@ -248,11 +251,14 @@ public ApiResponse deleteTransformation(String transformation, Map options) thro // "unsafe_update": transformation string public ApiResponse updateTransformation(String transformation, Map updates, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - return callApi(HttpMethod.PUT, Arrays.asList("transformations", transformation), updates, options); + updates.put("transformation", transformation); + return callApi(HttpMethod.PUT, Arrays.asList("transformations"), updates, options); } public ApiResponse createTransformation(String name, String definition, Map options) throws Exception { - return callApi(HttpMethod.POST, Arrays.asList("transformations", name), ObjectUtils.asMap("transformation", definition), options); + return callApi(HttpMethod.POST, + Arrays.asList("transformations"), + ObjectUtils.asMap("transformation", definition, "name", name), options); } public ApiResponse uploadPresets(Map options) throws Exception { diff --git a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java index 29a64228..fc34ee0f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java @@ -36,7 +36,7 @@ public String generate(Iterable optionsList) { } } - if (StringUtils.isNotBlank(format)){ + if (format != null){ components.add(format); } @@ -48,7 +48,7 @@ public String generate(Map options) { List eager = new ArrayList(); eager.add(super.generate(options)); - if (StringUtils.isNotBlank(format)){ + if (format != null){ eager.add(format); } diff --git a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java index 66244e9a..1c7f93ba 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/TransformationTest.java @@ -307,6 +307,15 @@ public void testContextMetadataToUserVariables() { assertEquals("$xpos_ctx:!x_pos!_to_f,$ypos_ctx:!y_pos!_to_f,c_crop,x_$xpos_mul_w,y_$ypos_mul_h", t.generate()); } + @Test + public void testFormatInTransformation() { + String t = new EagerTransformation().width(100).format("jpeg").generate(); + assertEquals("w_100/jpeg", t); + + t = new EagerTransformation().width(100).format("").generate(); + assertEquals("w_100/", t); + } + @Parameters({ "angle", "aspect_ratio", "dpr", From 97f92bab7eb3ec9c5d2822104d4e039919208c2d Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:02:20 +0300 Subject: [PATCH 516/592] Add folder decoupling support --- .../src/main/java/com/cloudinary/Util.java | 5 ++++- .../com/cloudinary/test/AbstractUploaderTest.java | 15 +++++++++++++++ 2 files changed, 19 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 2333e1cf..870616a6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,7 @@ 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"}; + "accessibility_analysis", "use_filename_as_display_name"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { @@ -36,6 +36,9 @@ public static final Map buildUploadParams(Map options) { params.put("moderation", options.get("moderation")); params.put("access_mode", (String) options.get("access_mode")); params.put("filename_override", (String) options.get("filename_override")); + params.put("public_id_prefix", (String) options.get("public_id_prefix")); + params.put("asset_folder", (String) options.get("asset_folder")); + params.put("display_name", (String) options.get("display_name")); Object responsive_breakpoints = options.get("responsive_breakpoints"); if (responsive_breakpoints != null) { 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 79046890..42303df1 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 @@ -794,4 +794,19 @@ private void addToDeleteList(String type, String id) { ids.add(id); } + + @Test + public void testUploadFolderDecoupling() { + //TODO: Need to build a unit testing infrastructure + Map options = asMap( + "use_filename_as_display_name", true, + "public_id_prefix", "test_id_prefix", + "asset_folder", "asset_folder_test", + "display_name", "display_name_test"); + Map uploadParams = Util.buildUploadParams(options); + Assert.assertEquals("test_id_prefix", uploadParams.get("public_id_prefix")); + Assert.assertEquals(true, uploadParams.get("use_filename_as_display_name")); + Assert.assertEquals("asset_folder_test", uploadParams.get("asset_folder")); + Assert.assertEquals("display_name_test", uploadParams.get("display_name")); + } } From 6e226de33915d8ea8bf24f73d66fbb56a46e53cf Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 5 Apr 2022 08:15:58 +0300 Subject: [PATCH 517/592] Version 1.32.0 --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 4 ++-- .../main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a6586a..b2acd3e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ +1.32.0 / 2022-04-05 +=================== + +New functionality +----------------- + * Add folder decoupling support + * Support multiple acls in cookies + * Support structured metadata in `resources` api call + * Rename API call returns `metadata` and `context` + * Support start offset and end offset as expression + * Get the details of a single resource by asset_id + * Search by asset id + * Support metadata fields reordering +Other changes +------------- + * Fix `verifySignature` timestamp units + * Fix transformations API call + 1.31.0 / 2022-03-21 ==================== diff --git a/README.md b/README.md index 1b4cc58e..c6858e76 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.31.0 | V | +| 1.1.0 - 1.32.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-http44 - 1.31.0 + 1.32.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 460beda5..dae66f00 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.31.0"; + public final static String VERSION = "1.32.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 d601b863..1354ca9a 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.31.0 +version=1.32.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 837b43b3f656ad37587744469a5a59bd201fd7d8 Mon Sep 17 00:00:00 2001 From: francistagbo <61406266+francistagbo@users.noreply.github.com> Date: Sun, 10 Apr 2022 08:29:19 +0300 Subject: [PATCH 518/592] Update Spring framework version --- samples/photo_album/pom.xml | 2 +- samples/photo_album_gae/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index b6210d57..9606673a 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -8,7 +8,7 @@ photo_album - 5.3.0 + 5.3.18 diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index f95b9dc3..b320750c 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -8,7 +8,7 @@ photo_album_gae - 5.3.0 + 5.3.18 1 1.9.37 UTF-8 From 82cd4cd64c461ceae9a989f2331c1343fe93f70e Mon Sep 17 00:00:00 2001 From: Yomes <1785648+YomesInc@users.noreply.github.com> Date: Sun, 10 Apr 2022 08:32:20 +0300 Subject: [PATCH 519/592] Fix double underscore handling during normalization --- .../java/com/cloudinary/transformation/LayerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 dba71ee7..d801c4dc 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -58,6 +58,12 @@ public void testUnderlay() { assertEquals(DEFAULT_UPLOAD_PATH + "h_100,u_text:hello,w_100/test", result); } + @Test + public void testPublicIdWithDoubleUnderscoresInOverlay() { + Transformation transformation = new Transformation().width(300).height(200).crop("fill").overlay("my__lake"); + String result = cloudinary.url().transformation(transformation).generate("sample.jpg"); + assertEquals(DEFAULT_UPLOAD_PATH + "c_fill,h_200,l_my__lake,w_300/sample.jpg", result); + } @Test public void testLayerOptions() { @@ -133,4 +139,4 @@ public void testToString() throws Exception { public void testFormattedPublicId() throws Exception { } -} \ No newline at end of file +} From 06b4f7d8948bd52acdb42ffde5e1ba6822233935 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:42:51 +0300 Subject: [PATCH 520/592] Fix unstable tests --- .../com/cloudinary/test/AbstractApiTest.java | 17 +++++-- .../cloudinary/test/AbstractUploaderTest.java | 4 ++ .../com/cloudinary/test/rules/RetryRule.java | 47 +++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/rules/RetryRule.java 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 e7cc0f32..e83cf042 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 @@ -4,7 +4,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; -import com.cloudinary.metadata.StringMetadataField; +import com.cloudinary.test.rules.RetryRule; import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; import org.junit.*; @@ -53,6 +53,8 @@ abstract public class AbstractApiTest extends MockableTest { private static String assetId1; private static String assetId2; + private static final int SLEEP_TIMEOUT = 5000; + protected Api api; @@ -137,6 +139,9 @@ public static void tearDownClass() { @Rule public TestName currentTest = new TestName(); + @Rule + public RetryRule retryRule = new RetryRule(); + @Before public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); @@ -909,8 +914,10 @@ public void testRestoreDifferentVersionsOfDeletedAsset() throws Exception { "tags", UPLOAD_TAGS )); assertEquals(firstUpload.get("public_id"), TEST_RESOURCE_PUBLIC_ID); + Thread.sleep(SLEEP_TIMEOUT); ApiResponse firstDelete = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); assertTrue(firstDelete.containsKey("deleted")); + Thread.sleep(SLEEP_TIMEOUT); Map secondUpload = uploader.upload(SRC_TEST_IMAGE, ObjectUtils.asMap( @@ -920,13 +927,15 @@ public void testRestoreDifferentVersionsOfDeletedAsset() throws Exception { "tags", UPLOAD_TAGS )); assertEquals(secondUpload.get("public_id"), TEST_RESOURCE_PUBLIC_ID); + Thread.sleep(SLEEP_TIMEOUT); ApiResponse secondDelete = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); assertTrue(secondDelete.containsKey("deleted")); - + Thread.sleep(SLEEP_TIMEOUT); assertNotEquals(firstUpload.get("bytes"), secondUpload.get("bytes")); ApiResponse getVersionsResp = api.resource(TEST_RESOURCE_PUBLIC_ID, ObjectUtils.asMap("versions", true)); List versions = (List) getVersionsResp.get("versions"); + Assert.assertTrue(versions.size() > 1); Object firstAssetVersion = versions.get(0).get("version_id"); Object secondAssetVersion = versions.get(1).get("version_id"); @@ -937,7 +946,7 @@ public void testRestoreDifferentVersionsOfDeletedAsset() throws Exception { ApiResponse secondVerRestore = api.restore(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.asMap("versions", Collections.singletonList(secondAssetVersion))); assertEquals(((Map) secondVerRestore.get(TEST_RESOURCE_PUBLIC_ID)).get("bytes"), secondUpload.get("bytes")); - + Thread.sleep(SLEEP_TIMEOUT); ApiResponse finalDeleteResp = api.deleteResources(Collections.singletonList(TEST_RESOURCE_PUBLIC_ID), ObjectUtils.emptyMap()); assertTrue(finalDeleteResp.containsKey("deleted")); } @@ -1157,7 +1166,7 @@ public void testQualityAnalysis() throws Exception { public void testDeleteFolder() throws Exception { String toDelete = "todelete_" + SUFFIX; Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", UPLOAD_TAGS, "folder", toDelete)); - Thread.sleep(5000); + Thread.sleep(SLEEP_TIMEOUT); api.deleteResources(Collections.singletonList(uploadResult.get("public_id").toString()), emptyMap()); ApiResponse result = api.deleteFolder(toDelete, emptyMap()); assertTrue(((ArrayList) result.get("deleted")).contains(toDelete)); 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 42303df1..2194afaf 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 @@ -3,6 +3,7 @@ import com.cloudinary.*; import com.cloudinary.api.ApiResponse; import com.cloudinary.metadata.StringMetadataField; +import com.cloudinary.test.rules.RetryRule; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.Rectangle; import org.cloudinary.json.JSONArray; @@ -81,6 +82,9 @@ public static void tearDownClass() { @Rule public TestName currentTest = new TestName(); + @Rule + public RetryRule retryRule = new RetryRule(); + @Before public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/rules/RetryRule.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/rules/RetryRule.java new file mode 100644 index 00000000..4d407610 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/rules/RetryRule.java @@ -0,0 +1,47 @@ +package com.cloudinary.test.rules; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.Objects; + +public class RetryRule implements TestRule { + private int retryCount; + private int delay; + + public RetryRule(int retryCount, int delay) { + this.retryCount = retryCount; + this.delay = delay; + } + + public RetryRule() { + this.retryCount = 3; + this.delay = 3; + } + + public Statement apply(Statement base, Description description) { + return statement(base, description); + } + + private Statement statement(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + Throwable caughtThrowable = null; + for (int i = 0; i < retryCount; i++) { + try { + base.evaluate(); + return; + } catch (Throwable t) { + caughtThrowable = t; + System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed."); + Thread.sleep(delay * 1000); + } + } + System.err.println(description.getDisplayName() + ": Giving up after " + retryCount + " failures."); + throw Objects.requireNonNull(caughtThrowable); + } + }; + } +} From 3b64470dddf7163c9f387e9105f9724a8bc2f302 Mon Sep 17 00:00:00 2001 From: Adi Date: Mon, 25 Apr 2022 13:03:44 +0300 Subject: [PATCH 521/592] Version 1.32.1 --- 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 b2acd3e4..436b867e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,17 @@ +1.32.1 / 2022-04-25 +=================== + + * Fix double underscore handling during normalization + * Update Spring framework version + + / 2022-04-25 +============= + + * Fix unstable tests + * Fix double underscore handling during normalization + * Update Spring framework version + 1.32.0 / 2022-04-05 =================== diff --git a/README.md b/README.md index c6858e76..472fbaec 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.32.0 | V | +| 1.1.0 - 1.32.1 | 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-http44 - 1.32.0 + 1.32.1 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index dae66f00..61622ade 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.32.0"; + public final static String VERSION = "1.32.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 1354ca9a..6e7d0085 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.32.0 +version=1.32.1 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From a36d11ea19f7d9ca8d05803cd9babae67a4c5f18 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 3 May 2022 12:40:26 +0300 Subject: [PATCH 522/592] Fix nexus publishing script --- build.gradle | 17 +++++++++++++++++ cloudinary-core/build.gradle | 10 ---------- cloudinary-http42/build.gradle | 10 ---------- cloudinary-http43/build.gradle | 10 ---------- cloudinary-http44/build.gradle | 12 +----------- cloudinary-http45/build.gradle | 10 ---------- cloudinary-taglib/build.gradle | 10 ---------- cloudinary-test-common/build.gradle | 10 ---------- 8 files changed, 18 insertions(+), 71 deletions(-) diff --git a/build.gradle b/build.gradle index 7fff49dc..ebe374c8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,9 @@ import groovy.json.JsonSlurper +plugins { + id 'io.github.gradle-nexus.publish-plugin' version '1.0.0' +} + allprojects { repositories { @@ -9,6 +13,19 @@ allprojects { project.ext.set("publishGroupId", group) } +nexusPublishing { + transitionCheckOptions { + maxRetries.set(150) + delayBetween.set(Duration.ofSeconds(5)) + } + repositories { + sonatype { + username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" + password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + } + } +} + tasks.create('createTestSubAccount') { doFirst { println("Task createTestSubAccount called with module $moduleName") diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index fa305516..37246e23 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } task ciTest( type: Test ) @@ -87,15 +86,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle index cb8ec796..7c94214b 100644 --- a/cloudinary-http42/build.gradle +++ b/cloudinary-http42/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -100,15 +99,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-http43/build.gradle b/cloudinary-http43/build.gradle index 23942327..47fe4701 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http43/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -99,15 +98,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle index 7d533e05..a4c51ed8 100644 --- a/cloudinary-http44/build.gradle +++ b/cloudinary-http44/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -98,16 +97,7 @@ if (hasProperty("ossrhPassword")) { } } } - - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - + model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-http45/build.gradle b/cloudinary-http45/build.gradle index 2d88dc60..f4b7613d 100644 --- a/cloudinary-http45/build.gradle +++ b/cloudinary-http45/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -99,15 +98,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 9d2313be..6db5af30 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -94,15 +93,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index c374de6d..31a8bae2 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -3,7 +3,6 @@ plugins { id 'signing' id 'maven-publish' id 'io.codearte.nexus-staging' version '0.21.1' - id "de.marcphilipp.nexus-publish" version "0.4.0" } apply from: "../java_shared.gradle" @@ -88,15 +87,6 @@ if (hasProperty("ossrhPassword")) { } } - nexusPublishing { - repositories { - sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - } - } - model { tasks.generatePomFileForMavenJavaPublication { destination = file("$buildDir/generated-pom.xml") From 060b5e4e893f63526566ee51f6043309efb7ab21 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 10 May 2022 12:59:07 +0300 Subject: [PATCH 523/592] Version 1.32.2 --- CHANGELOG.md | 10 ++++------ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 436b867e..fa46856c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,12 @@ -1.32.1 / 2022-04-25 +1.32.2 / 2022-05-10 =================== - * Fix double underscore handling during normalization - * Update Spring framework version + * Fix nexus publishing script - / 2022-04-25 -============= +1.32.1 / 2022-04-25 +=================== - * Fix unstable tests * Fix double underscore handling during normalization * Update Spring framework version diff --git a/README.md b/README.md index 472fbaec..782d11df 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.32.1 | V | +| 1.1.0 - 1.32.2 | 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-http44 - 1.32.1 + 1.32.2 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 61622ade..a695de14 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.32.1"; + public final static String VERSION = "1.32.2"; 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 6e7d0085..9ee2713f 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.32.1 +version=1.32.2 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 3155774709a3a77ade76ecfaaedb9dbb09c1928b Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 9 Jun 2022 09:38:19 +0300 Subject: [PATCH 524/592] Bump spring frameworks version --- samples/photo_album/pom.xml | 2 +- samples/photo_album_gae/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 9606673a..889de220 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -84,7 +84,7 @@ org.springframework.data spring-data-jpa - 1.3.0.RELEASE + 1.11.20.RELEASE diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index b320750c..d730e1f5 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -8,7 +8,7 @@ photo_album_gae - 5.3.18 + 5.3.19 1 1.9.37 UTF-8 @@ -99,7 +99,7 @@ org.springframework.data spring-data-jpa - 1.2.0.RELEASE + 1.11.20.RELEASE From ccaae9ebf676684c4f1566a83bdbf68266548c8d Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 4 Sep 2022 09:46:56 +0300 Subject: [PATCH 525/592] Add parameter `use_asset_folder_as_public_id_prefix` --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 3 ++- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 870616a6..2259adc7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,7 @@ 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"}; + "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map 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 e83cf042..ad77e202 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 @@ -769,7 +769,7 @@ 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)); + context, "live", true, "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); @@ -777,6 +777,7 @@ public void testGetUploadPreset() throws Exception { 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); assertEquals(outTransformation.get("crop"), "scale"); 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 2194afaf..14084d10 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 @@ -806,11 +806,14 @@ public void testUploadFolderDecoupling() { "use_filename_as_display_name", true, "public_id_prefix", "test_id_prefix", "asset_folder", "asset_folder_test", - "display_name", "display_name_test"); + "display_name", "display_name_test", + "use_asset_folder_as_public_id_prefix", true); + Map uploadParams = Util.buildUploadParams(options); Assert.assertEquals("test_id_prefix", uploadParams.get("public_id_prefix")); Assert.assertEquals(true, uploadParams.get("use_filename_as_display_name")); Assert.assertEquals("asset_folder_test", uploadParams.get("asset_folder")); Assert.assertEquals("display_name_test", uploadParams.get("display_name")); + Assert.assertEquals(true, uploadParams.get("use_asset_folder_as_public_id_prefix")); } } From 0d996e26f4204593691ba219148cb08d90ff5f99 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 5 Sep 2022 07:51:15 +0300 Subject: [PATCH 526/592] Add asset_folder, unique_display_name to update resource API call --- .../src/main/java/com/cloudinary/Util.java | 8 +++++++- .../java/com/cloudinary/test/AbstractApiTest.java | 12 ++++++++++++ 2 files changed, 19 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 2259adc7..41a95a18 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,7 @@ 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"}; + "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { @@ -160,6 +160,12 @@ public static final void processWriteParameters(Map options, Map if (options.get("access_control") != null) { params.put("access_control", encodeAccessControl(options.get("access_control"))); } + if (options.get("asset_folder") != null) { + params.put("asset_folder", options.get("asset_folder")); + } + if (options.get("unique_display_name") != null) { + params.put("unique_display_name", options.get("unique_display_name")); + } putObject("ocr", options, params); putObject("raw_convert", options, params); putObject("categorization", options, params); 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 ad77e202..548a5865 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 @@ -1188,4 +1188,16 @@ public void testAccessibilityAnalysisResource() throws Exception { ApiResponse res = api.resource(API_TEST, Collections.singletonMap("accessibility_analysis", true)); assertNotNull(res.get("accessibility_analysis")); } + + @Test + public void testFolderDecoupling() { + //TODO: Need to build a unit testing infrastructure + Map params = new HashMap(); + Map options = asMap( + "asset_folder", "new_asset_folder", + "unique_display_name", true); + Util.processWriteParameters(options, params); + assertEquals("new_asset_folder", params.get("asset_folder")); + assertEquals(true, params.get("unique_display_name")); + } } From 38ff6dbcbbc3ba01feb57cb1edb7090499c2c172 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 8 Sep 2022 07:07:14 +0300 Subject: [PATCH 527/592] Add function to get resources by asset folder --- .../src/main/java/com/cloudinary/Api.java | 8 ++++++++ .../java/com/cloudinary/test/AbstractApiTest.java | 12 ++++++++++++ .../main/java/com/cloudinary/test/MockableTest.java | 10 ++++++++++ .../java/com/cloudinary/test/helpers/Feature.java | 6 ++++++ 4 files changed, 36 insertions(+) create mode 100644 cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index ee1123d5..4b0308ab 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -129,6 +129,14 @@ public ApiResponse resourcesByAssetIDs(Iterable assetIds, Map options) t return response; } + 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"); + params.put("asset_folder", assetFolder); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources/by_asset_folder"), params, options); + return response; + } + public ApiResponse resourcesByIds(Iterable publicIds, 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 548a5865..45c4a0b3 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 @@ -4,6 +4,7 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.api.exceptions.NotFound; +import com.cloudinary.test.helpers.Feature; import com.cloudinary.test.rules.RetryRule; import com.cloudinary.transformation.TextLayer; import com.cloudinary.utils.ObjectUtils; @@ -52,6 +53,7 @@ abstract public class AbstractApiTest extends MockableTest { private static final String CUSTOM_USER_AGENT_VERSION = "9.9.9"; private static String assetId1; private static String assetId2; + private static String assetId3; private static final int SLEEP_TIMEOUT = 5000; @@ -77,6 +79,8 @@ public static void setUpClass() throws IOException { assetId2 = cloudinary.uploader().upload(SRC_TEST_IMAGE, options).get("asset_id").toString(); options.remove("public_id"); + assetId3 = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("asset_folder", "test_asset_folder")).get("public_id").toString(); + options.put("eager", Collections.singletonList(UPDATE_TRANSFORMATION)); cloudinary.uploader().upload(SRC_TEST_IMAGE, options); @@ -292,6 +296,14 @@ public void testResourceByAssetId() throws Exception { assertEquals(API_TEST, result.get("public_id").toString()); } + @Test + public void testResourceByAssetFolder() throws Exception { + if (MockableTest.shouldTestFeature(Feature.DYNAMIC_FOLDERS)) { + Map result = api.resourcesByAssetFolder("test_asset_folder", ObjectUtils.asMap("tags", true, "context", true)); + assertNotNull(findByAttr((List) result.get("resources"), "public_id", assetId3)); + } + } + @Test public void testResourcesByPublicIds() throws Exception { // should allow listing resources by public ids diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index c68a4131..7a9ef662 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -1,6 +1,7 @@ package com.cloudinary.test; import com.cloudinary.Cloudinary; +import com.cloudinary.test.helpers.Feature; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -58,4 +59,13 @@ private static List getEnabledAddons() { return Arrays.asList(envAddons.split(",")); } + + protected static boolean shouldTestFeature(String feature) { + String sdkFeatures = System.getenv() + .getOrDefault("CLD_TEST_FEATURES", "") + .toLowerCase() + .replaceAll("\\s", ""); + List sdkFeaturesList = Arrays.asList(sdkFeatures.split(",")); + return sdkFeatures.contains(feature.toLowerCase()) || (sdkFeaturesList.size() == 1 && sdkFeaturesList.get(0).equalsIgnoreCase(Feature.ALL)); + } } 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 new file mode 100644 index 00000000..875f9b9f --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java @@ -0,0 +1,6 @@ +package com.cloudinary.test.helpers; + +public class Feature { + public static final String ALL = "all"; + public static final String DYNAMIC_FOLDERS = "dynamic_folders"; +} From b9b139f13d62d3ca955f35f4b8fc4ec655c5df3e Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 8 Sep 2022 08:28:05 +0300 Subject: [PATCH 528/592] Fix `videotag` missing `authtoken` --- .../src/main/java/com/cloudinary/Url.java | 1 + .../java/com/cloudinary/test/CloudinaryTest.java | 13 +++++++++++++ .../test/java/com/cloudinary/test/UploaderTest.java | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index b9fb0407..360193c0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -77,6 +77,7 @@ public Url clone() { cloned.urlSuffix = this.urlSuffix; cloned.useRootPath = this.useRootPath; cloned.longUrlSignature = this.longUrlSignature; + cloned.authToken = this.authToken; return cloned; } 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 763cb286..c46672d4 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1196,7 +1196,20 @@ public void testVideoTagWithPoster() { .poster(false) .videoTag("movie", emptyMap()); assertEquals(expectedTag, actualTag); + } + @Test + public void videoTagWithAuthTokenTest() { + String actualTag = cloudinary.url().transformation(new Transformation()) + .type("upload") + .authToken(new AuthToken("123456").duration(300)) + .signed(true) + .secure(true) + .videoTag("sample", Cloudinary.asMap( + "controls", true, + "loop", true) + ); + assert(actualTag.contains("cld_token")); } @Test diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java index efbf9190..4734707c 100644 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java @@ -30,5 +30,4 @@ public void testTimeoutParameter() throws Exception { "timeout", 1); ApiResponse result = cloudinary.api().resources(options); } - -} \ No newline at end of file +} From 6d2f9c9046410faa3dd6e0d3198bc479850e8c0f Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 8 Sep 2022 08:45:26 +0300 Subject: [PATCH 529/592] Fix upload with unicode characters --- .../com/cloudinary/http42/UploaderStrategy.java | 3 ++- .../com/cloudinary/http43/UploaderStrategy.java | 2 ++ .../com/cloudinary/http44/UploaderStrategy.java | 2 ++ .../com/cloudinary/http45/UploaderStrategy.java | 2 ++ .../cloudinary/test/AbstractUploaderTest.java | 6 ++++++ .../java/com/cloudinary/test/MockableTest.java | 1 + .../resources/\327\220\327\221\327\222.docx" | Bin 0 -> 12298 bytes 7 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 "cloudinary-test-common/src/main/resources/\327\220\327\221\327\222.docx" diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java index 9984f015..0e38365d 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java @@ -23,6 +23,7 @@ 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; @@ -71,7 +72,7 @@ public Map callApi(String action, Map params, Map options, Objec Charset utf8 = Charset.forName("UTF-8"); - MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); + 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) { diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java index bb748f8f..88ce4b90 100644 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java @@ -3,6 +3,7 @@ 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; @@ -87,6 +88,7 @@ public Map callApi(String action, Map params, Map options, Objec 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()) { diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java index e075db29..3afc8bce 100644 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java @@ -4,6 +4,7 @@ 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; @@ -88,6 +89,7 @@ public Map callApi(String action, Map params, Map options, Objec 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()) { diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java index 175653cf..f4712515 100644 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java +++ b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java @@ -22,6 +22,7 @@ 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; @@ -84,6 +85,7 @@ public Map callApi(String action, Map params, Map options, Objec 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()) { 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 14084d10..89b502fb 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 @@ -799,6 +799,12 @@ private void addToDeleteList(String type, String id) { ids.add(id); } + @Test + public void testUploadLocalUnicodeFilename() throws Exception { + Map result = cloudinary.uploader().upload(HEBREW_PDF, asMap("resource_type", "raw")); + assertTrue(((String)result.get("public_id")).contains(".docx")); + } + @Test public void testUploadFolderDecoupling() { //TODO: Need to build a unit testing infrastructure diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 7a9ef662..2308d3c2 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -15,6 +15,7 @@ public class MockableTest { + public static final String HEBREW_PDF = "../cloudinary-test-common/src/main/resources/אבג.docx"; public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; public static final String SRC_TEST_VIDEO = "http://res.cloudinary.com/demo/video/upload/dog.mp4"; public static final String SRC_TEST_RAW = "../cloudinary-test-common/src/main/resources/docx.docx"; diff --git "a/cloudinary-test-common/src/main/resources/\327\220\327\221\327\222.docx" "b/cloudinary-test-common/src/main/resources/\327\220\327\221\327\222.docx" new file mode 100644 index 0000000000000000000000000000000000000000..2022c4ca16d5b6b48dc0c5035fdca48bf07bb174 GIT binary patch literal 12298 zcmeHN1zQ}+)*hTdaDo$pyK8WF53a%8-CY8M2X}V}?g4_kJ4|rb;32>_*}HppH+R2Z zaBn})bWiuG_f&QDv8rbUXW^!@dNEopD`enR3YLJtVzgOZ$YeT{XtX9Y1S{$8r;Z_u=$b#67(!MX_pqZ z>HUmxDKo7Y9V?3_houf zMu@oa)wH}yjoh`YXDYa&D2R4mv8@$C6zaHjJOIz0eDEt-fok!Kk#>xNywwjWc+I@x zR%-_gX{iIL&w`G2bTU#x$BdG(6;Zp$8~ zmx5>6Lm^C7+%g=I9UARs0Wz9)to}0In)b@fa;}iQFO4vG%>x*D2P?O9` z9~<)pRIA&oa+uJ}5@kt!O^0XA#7LeIXIO!NciujA=$YSH-1i}b?nUm44~!4qraI_a zPkRx&xB?t!#G3W`8+P~^x!v+Z_}JFx@SX0S7#qh;%Ub#mihdfFQiv%Sh3Y{i%cI6hwvgq* zu%=*qA%nz`-WQBV+b_~1OhI$zI1c{`au17t-%X~j8~J)lEEznNAx|s zxSM#oJp!G88Cq*(UPYg1#nnMH_SrC+Eh(1CpAO z7(5l!6~&D1Sk^xn@%&zSD&E^MM8U}|%OTFVAhjlah;3lB%h%oEU&TyPW)GTyb~a88 z-)t{MK4@Xv2U-q(V}?kwP>#{wF&$BPXq)D!Xf!~y#&|M4n=Xj>;gI)1>$pgm?X`{Y zVs>3LSqMgZ<{h1dYcIPtRyN*yLI_@AZGJk-&`tv!Fo4wiwV@d4i zOeuu^@FqB!qBWMfNIQ|_(VxtbTS=y80v+*JeOmLC`2DmQ`)}bV`#G zFf`JGm*pHU>ffJ|WQY{XO4;*F;0(+_lLTH(t_NG~AeY#2Q5`CSrd=%5y(&xb^7Xs5 z=RStq9;S(8V{E5vX>)xf4DNq=zrA1hDoaXPQ@8l+rNkqI29$dcqTl`jJW_wr%mv;| z?D#oJT$!119PBM39QepBQE-y3Av$7p}4 zg_JP2PSa)7r|ZS*8fSM#8=V1h7=65vwfWHM%NHTg&SbZ~GTzn7uY-e^JGK7Y&QyMO z^Co^(>LBHql0q@{}-E zv}F3)-r82urZYlw+gl#Og-&0U%MQHrY@6QBHi$PBYVEK?`meItOXSZ4j~V;+;9Rgi z4obaa)<$rLlOOgx;Rd0qN{^kXG@rMcrRiRiLaRaDX4yTcX59bqs@RQt36ho@5F)3` zk`QF^QQ;_-l}@s4!{A;yh@wKKR!5=MXEy^5W$tK&dkB_48wzV|z%KRfh?6p7% z-HWlSjm~OGsiVpFt>=zaDNYmq94iyu5pKX)ui~6RN7Q{_wbufTd+F=yQz29zbQxPlhd)|Xr zKL6=t8n4V!reOhqp*H})E3h#9CN<9HCN?IFznz(XI+|lmnMiyI)X#9=h(cRNJjeD8 zD%htbzgaDCfEt2gI+U%7Dzhx6)|MNXLf>aQzJSh%6?xgRgDU)zxpzCB$mo+)22tGM zYbjRx>)sgMpcid{ssf+Oo?6RVTiKj8HXl>CB~lQ-#7ArMsXGv602fop3PHY3ulM)G9)xk6+nqea(1efz)_!);5pbL`zhC(Z=yeaq4FVpI+%CN7u( zWyB~tTF6dzq(Qn+DH=^mhf;{Sq=Pv%7R?p;qqLh@p|{|MK1m23j?(996(bR0qk);S z@92nVil+p`%MJ#a%sl*Ew}nTFWMs0=s}WC5BRtr{R>U-j#!ikRk4+!75rD<)^aDLT z4LN)Fdiibh)etJSTd-q7Mw1#xGXOTcZuxf%MdPOQ=ShnMR5n`9x9N|bb6tB^3r**` z`aUn)VGHH#5CsTeQ<0o<>JRKT9`)K5IIpb~m#wWg(i3gJTH(1PMNmlyA(YOD4O9WuPMkhDDIDlN*`vU{20EX1$`|~8-0L3}3LUkX zRc8x$T|X}PJ^tv0JacqT2_uTdLEPi!ceSm-;V{<(5PiE%;0i|_l5tK@%z>uHMf7|_ z3gzPyKvlc$xmdqNh6e}r-P1ou7!!;#n*2h<8YK!Rod`PErl}I$AN!h z%a+*LSes|@{M&o}pk_*rM4@1$G*0Sff)K9mX(cD?h#IkD2I5fN+reYv9M*b{fp5EqT&0&7q?Qt`bFCOcp)`cH58TEpeYl8r4zPD%Q|4f3bMqD7dg zjgx)&wn%G;Kpc^gE~_QQbQxU2e+NMxa{5{1)5d92_Q^_Y=rKf|>F%m4858ToR-im; zBlX$0Maddn`HHEj^e}PrGeg-aW;~eK+4@|)QADojLXk?tsTsUkeQ1c#;(*q@?F) z8kQRsNpHz*SGwvY^RR-cS9-W3s=f(8*qTF8pkJIRbvy1#P~jFEGOqhf{l!L&9b(Jq z+W|Ja51WFmVY39?WL?^BMbZ7&1vat0`-=Ft<;LSQxNUnOIM2|2Z71R%bhwh3dshl!i=4L1EGt6Gg3QFy4x z#Y&N5Ki#9YW*IoX#m6BpQ9d{4B!?==**1)o~i~UFaynKi-E3 zy`@hU-39T(uH`vB+10pwh}g=Pyuw-R+DUzx>aLybH`kJ@x60zEwy4uaJH;x#J8SQ5 zo#U9tY!>KNtZ1u>w=9B08CGS{TbO|B-xdk@P4t^slku)TQa4(#X%qD<(0s7ohD}T1 z+rt^xS%YIIWjkudxEq2VbBFyNJggEPDm6ejRG2G3RyW!l>qC}@SyVjzI3Q|xs)#O9 zv9~6e0Mu@nSMmBGI@Ca$owakN4jOW2=_b`=igz(!P&LftBi3+PB#=Cl*fQFS9lxiC z4*iy{YSP7w=4dmU&G#N{lDHBh&cw8KoN2pyK2x=m-x5Dit;4?**hHm>d{B%LU8hZ)kjrC+MCq66n> zhuARor&L{A7&G5e65a~+op}n4p-3kt2~c^sR4ZHFuX&apJzO7lRjYmPSQcT50B)9L zSMdcCJvi5R+f@%Vfz}4fl^<5a`%H?C)@!lm&rZu5Hr;NKAx3!3XG2ds+ZL!Aj@Ar@ zj&8LIT_^VwzenXP5#2y_&b{V$⋘;X}#{UdZ|??&JHiFm!G%av|L!EUhdLi+Qfd| zijv$Inpyo>eRrc;27(IBUh4S(*D=jAzXJ-prGpC;puwHG&c7+SoNB{kE z_v9r_t41S6t~}bx1&3I>?kx39D!N4^JR_vkl0)R zwFkIySe`t#L_<6>GY6(Oc^^|EPT1}on_f-?1#n2d?=5&Uvo7d-{`BqkE9k^Y4hX@{ z2SCVm(vuGwPT`Ir(Nzm3^`WpL0jx^)#RVudL~$wK$`K~TFAPowvREQ3;SFr%~1V#gB}ICKY&jAYP+)Rt^a#AyIzeiisn6X&5W4UO%4|^-PEFpGdj% zXpX@%-VWmbp#N#DL*>r=BL2KWsM1)fJ``$m6i!m?jLS>d`N{|Oa~nN34U>Xl6L*yN z6?fp(i|%GJl>~;;J2kOsMZ%ePoL~&r^=(2tff%nA(pv_-Wa$UEH$R$Ci z!cJd_kFQG6;Z~}vP?y?5hdTMPnmN}IBM>MW4$fjm^3^0 z@KB9Bq_T;BLkVjQ)OlW(YW0kfAHm*bo%g-aly*{yV%*(wV6h-=Fpqh;b>-=OLGbOF zqfUuK&-wO*nMI#s$@m9>V`guHJE(6}Y}knhG9RWGp}Su7{hoYzz~u%*6rT*I`oBhQJ;5{&A~b{*a+RhZ!CRK%(xTqRN0lTQvm zA6do{6{?{pMhC6B&JE5ktD$S=w%Q`rE>g_+PuZBDTN$n+_V^_|P&HbF6(Y-MkNV_H z+hW$Yi80D_9E;lRoAl@J?C2M(YAGr)cqDUk<61OOj0Ncsg!`kFG%hH~y?X=R0Ht>~a z=B#V#-NX8z`r8YFJ>W@iL$PbLf-&a=>I=;hDdC;IWZ&WXz0yhJ-JIUrv{Gcxe z`~r65Iv=0Xdb?RC9EoRUra;AON?kLG;v_!2gnX;|r{1q&Ji4dD`&f30^EtC(e4oEB zbpE(;jK{WPo`8apyi6eDC8Uoq^k8|tw-HE5_e_?|t4cI(lJrTqWAydFi;=A19eJt* za@tM0!}0BO;bA)WC^1xvF-EZmO>Usu@u$S?5hgQ1S0y2!Sxx%&6gnAPGq zBt*H!jr-8z`Yx=1FoXFdS~*k?a-GpGbhL|sJBaCn4pH1%8zA&G2`P8*R~`nA8Hy>F z!AUoF`CoThK41IoDm+<&7TTD;m$jn~o#SWMu`oD3vb2pHCSTKDel3)ZC-&4o?S@e`h$ah~by zV_COj*Im_k#1!+OylW$$*JkZgB^-6DvU6T<2Gtz;S-B;j>8BzwYEq55F1D`^?|LQZ zB1MIa(>zG%r(p}otld}88F!#Tl|1Pi4CD+XKBzLJs8LJ#4DWHWcyCVCD-vZyk2cPx zqb#_k#->X16z4OVrXP+`EA%FYlVoY>%VLvkc4G1R40_c2K2OLba369CYZe`z1#Nx6 zg>iW82ZB z7;vx+Y>}E)p(JZK47wXmzo6#uKC=xB) zIFO1lBjb_sFJ;l@LqjPF83j8jVW8CG-led*XFvv=(}jj9FVU)f7XPINgGX1M5#VsC48q+U_^>3Ao*hI<*okSPG<@r&i%)QU>ffs*mlH6ll;v z+y({|VVrU-wLk1hzis#w5_fyx4rGL12bqTB&Yv%Ly$?->g=KALVM^Hu2`I!>#hzo! z%PO-FB~+UU|6Ylgt@8NWpCYttZ8?09-ECgDTtt&Pq1R)~nr^RhwhGQ_bg8>+sutj7 zw(X%&Q8(epqEUZRq##kE?ZEN_aBaK=2nzIycxxk;r?rqy?3jC_Rj_l8yq2SsYSl+t zyAUB^{OOflbD!-Tizee_mHA<+#amh}IV;HySsoJyO?vaNwy0M<5{t`#WRj5~>&a+x zoNj?DN3{{Dxcm7^0?;RRtL93O8Ts_?oMfO{KZ|#sRFAryOjc>_u?5qCa(|8Y1{$G* zmT@hfRpZWW6j=w|GapzR7-TE!1NH^xIFm`Co2=iuhn*TyDC4btJ|mOug;+z_Jbb#L zXik>VH9d^Zg~%Goi+zDx$B%y3r0;#>mIj+uO2Xwsy1c@g*CPV!N(@ht8bL-%60c<% zp%Ocq8+xno#F0Q3X>RU@TU`a9;X-p5jyGx8e{-HR9LY%Yj9L-;@=Tu&<9jn|V(F>W z`+0+&_Ke5$u%35oD7di%xZm^XMlU4lX4$8ln>R*+kyg*i1C`#%p?Lm4KR$-Fy&Jb( zFLAvgG4)``C|2z7P-$B^FS&Jdzfiw4rmOS7opE^&MRwE;D>)y+!jQmLylT`#x@ypq z57PfaY~&6dw*B&qC^dYrmx+mn{3TEt7Ak+bt4k6F$2TMz2H!XJ=c!U2hExkugQ?6x zocj?YbEoFL%GPLCNq=g9QWPsxKAsueS4jTsaxtbgOe5mb{)~`17#ZDd>LD;~+rA&D(y0lzpQ216q)LE>QN9k9Y8O>BuFkTj1jx{!>!u`$bD zc6+zMYURslCP7fTJ$79zqcOq7XpO?DhAN{%`3eh&^DA5ag24p-MKe|4fuT$N*M3>o zCUFj!2l2%lq{i#}f|l$;`r{;tJlV9+FX_}2aF0P_;UIYa)z|HMQr4ocuYtiU8P+j6 z)$yDpF<#cw z{v>UW75d#8LZvv{Z1v)CbSrwGc~GusQH&HyJ)(i_B#mdNhifa;Omv(Dlk{! zKPm||Co2)d*rZ&U?39RbIIsG*fCyVYKm=-j@es^^kL_QOpSLC| zsHZ0X9({P#g=Ti1v#x1pn84vdyJ6>ZX?d%Xz=?K?&8(20$_x!>`Ev*So-#j7CFkqc zDW3U6qN9eyW5kvN7B1;l3?BqD?|y9MJs=aM^PlI+Tz8=EW>EVR-9$kTs35n6wiCXH zXz!_tvy})`-7`?XX&~)pb6-q!j$%xYRGxK)4s4qSzG~ltKO3G`<2v4~BCvt^^7?dm zRPD|2dX={|%xf6#x1BNkhf4|xbJvCblk5caUux1P+39lC2 zkkSg2u#6S>A!>_J-xBbaU(z;oDy>0R%*L*hvv=LeASr>AMLuTFGBFz#hJqhI4K2x? z$K$>KQ5Z^PS4uprV9Awe@Eax8KhbkN__+5^jJ-oxA|CoDWf4df|q?q=GUyM+!{?}C4Q(CzAJ3a{JK_8RetkU6eLOwIrpgyP{DHW*kwotkFvB9 z7>8yvRkl?u3PnC&-wZ%m%jM5Q)!nkVW>yrUbY5KFS#Wq>mdD5 zrVO*8S2;=TQ_U_HsG5<4&^ydVE7!v7s6{B`?_78A9x1aMDSRkr&c>cA_G;Tzf<5EU zR#n(>>NlhC;eps1>sMiH8<4urc>3N7xu-bywydyiOpHOq%X6)9xBAD?^FIdeX9(2t z^56`KFF2oq`A6XHW@7m6FZ%)+Gx}EGQW3!UZTDx9p|+x7OlmcQmjinJ?^oc$59UJ2 zTjC?jPiM}SOP%fvZ{weDw-|^ocgB~tF(7fTT5yow#rZF2;B*f4J*-$VVZ0FAK{S~X zVYm@AY-#RD<#ETKGZ9`;S7tFAjY1{2%My3NeAftb2sIGvKD-)SS7EjUso~%MN^?1fJ zxf84+T4Y%=3qPpb+DKI;G4D_(*jGwUeihW2fxxZ0@Wx8fqQ z=yGK9!myhN^2j{nKJZoOgFli`>`84cR(kTDMAo1{r|AXKKP{Y63wcE$!KERAZ~y@M zAM#C?I#kvOb|)992{jHSiSQk}lWxzvmM4h0(Bgz!}g$24b=@zOBU@(9C#S z+q(Wkj~N?K;ynk<+osf?i5=m!!eL7mWv(i@$90?SCLN1p)HWhqp(5MRS2!OaMm*(VbuGB4a4llsa^DKh5+zl{aOvp5;gJ3%#;yi>oG~l)5H> zB>npHt7?=W7Cu@wDqhD;t9d8)?Tp)X&KXbuEhWF9=3yv`f^z`!mAokDQw2)y5iQYa z@@3~{Px&llyj`A*UWMsyDCt{nmcAM0NJ?YwVKnTP=V;O%AesJ7;{_)r`RITjH+2uB zn!rD1K;bOca~#2xYp~Ryfis}sw5Ot@oxKyIk)7jj0s}59`d``;tV+OmUAsUg%+Rys zV}V$Z2n1&xYL_vq-k9-_&W*!DgTzTep+)&%r#?hCTF0WU!o*Bi&q`5UK2ndbAr~TmkK1eaCXRWRB3Ots?OL;&vL4y$AGLrnlNT2 z6(^H5UDv0n(0oM0<7wT~G$^kMMgf-(Y{hH@?w9=E+LCaFuP|LXUo%aFd~}Yx$-Et! z@oo_GAn30ll7FPq6HD0Um;Q2Io@(#>M2A@n@ni=a=+~PJVe>7yXZ%Ok;%B_BZx0sK z0kEK={#8&7?CpOVw*Sc~c-p{6hVqC!I6dD^dV!*JTKTR(4aNqjNZT8bJPR-z0g+)v z+bqF0_)Ub?6NlsTpNx7+uv+z8e`Z~x3@yo|zwB!tWUZg3wn=C4E44K{;*FHjAP3fI z?7{)>*#!iG_SShr3z$vG42)8nsPAd}^Q>zy9OP|eGM7W-$pUi?s;U}#D-(zamwOo^ z4EyY}Cuvkz1rTw6hO;)#b(Bhxb{oVN zjR9nd1=Z|@LV|P@ z2%pIHD|TTr&q6Y}BKUl9{>+L%K+=PQoqsNx{?|A8^ZXAb)QU2HXYltD&p)96Kms_b z`b)v*ufShx-~NQwfr}OXqY~~{_}^<`{saR6n+U(d|DVd3Upf6+AMqzI9B|a~kJS>t zviLQ-^d}2Ka9sHti@)ZYeue+Kf&3@j1LqI;zqgZr1^*hv{t0Fw`yKpCNc$^?U&F9J zIn&f#xC*{}G&JLNyo0KhgS0Pr6!`d9ehP32$VQ8a&n|86)HWnjSK@w1#55zq}b KuB`Mw@BR;s?&gvJ literal 0 HcmV?d00001 From 92af7ccd65b51fe5650c10344083565b023cabe0 Mon Sep 17 00:00:00 2001 From: cloudinary-bot Date: Mon, 12 Sep 2022 06:35:50 +0000 Subject: [PATCH 530/592] Version 1.33.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 fa46856c..7bb2294c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +1.33.0 / 2022-09-12 +================== + +* Add dynamic folders support +* Fix VideoTag not appending auth token +* Fix upload with Unicode character not appending a file extension +* Bump springboard version + 1.32.2 / 2022-05-10 =================== diff --git a/README.md b/README.md index 782d11df..48f45ecd 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.32.2 | V | +| 1.1.0 - 1.33.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-http44 - 1.32.2 + 1.33.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index a695de14..2229fc8f 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.32.2"; + public final static String VERSION = "1.33.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 9ee2713f..2fac0474 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.32.2 +version=1.33.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 4d5edd7c7304ede47a6345a79d085fa9994666c9 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:55:25 +0200 Subject: [PATCH 531/592] Fix create user tests --- .../java/com/cloudinary/test/AbstractAccountApiTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 0fe3c95a..8dc30fc9 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 @@ -422,19 +422,19 @@ private ApiResponse createUser(List subAccountsIds, Boolean enabled) thr } private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { - String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; return createUser("TestName", email, role, subAccountsIds); } private ApiResponse createUser(List subAccountsIds, Account.Role role, Map options) throws Exception { - String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, null, subAccountsIds, options); createdUserIds.add(user.get("id").toString()); return user; } private ApiResponse createUser(List subAccountsIds, Account.Role role, Boolean enabled) throws Exception { - String email = String.format("%s@%s.com", randomLetters(), randomLetters()); + String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, enabled, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); return user; From c0a910326f9b1e70ce1ab23780c2fab660c43a84 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 7 Feb 2023 07:17:58 +0200 Subject: [PATCH 532/592] Add support for `clear_invalid` parameter --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 6 +++++- .../main/java/com/cloudinary/test/AbstractApiTest.java | 10 ++++++++++ .../test/AbstractStructuredMetadataTest.java | 10 ++++++++++ 3 files changed, 25 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 41a95a18..487dd5b0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -172,8 +172,12 @@ public static final void processWriteParameters(Map options, Map putObject("detection", options, params); putObject("similarity_search", options, params); putObject("background_removal", options, params); - if (options.get("auto_tagging") != null) + if (options.get("auto_tagging") != null) { params.put("auto_tagging", ObjectUtils.asFloat(options.get("auto_tagging"))); + } + if (options.get("clear_invalid") != null) { + params.put("clear_invalid", options.get("clear_invalid")); + } } protected static String encodeAccessControl(Object accessControl) { 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 45c4a0b3..6fae5a41 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 @@ -725,6 +725,16 @@ 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(); + 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"))); + assertNotNull(((Map)apiResult.get("metadata")).get(fieldId2)); + } + @Test public void testUpdateCustomCoordinates() throws IOException, Exception { // should update custom coordinates 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 9aefb64b..b6e541a7 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.utils.ObjectUtils; import org.hamcrest.Matchers; import org.junit.*; import org.junit.rules.TestName; @@ -268,6 +269,15 @@ public void testUploaderUpdateMetadata() throws Exception { assertEquals(PRIVATE_PUBLIC_ID, ((List) result2.get("public_ids")).get(0).toString()); } + @Test + public void testUploaderUpdateMetadataClearInvalid() throws Exception { + StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata1"); + 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)); + assertNotNull(result); + } + @Test public void testSetField() throws Exception { SetMetadataField field = createSetField("test123"); From 84b11736178acc9f48066d60bc0412615b9213bb Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 7 Feb 2023 07:19:43 +0200 Subject: [PATCH 533/592] Add support for `media_metadata` parameter --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 2 +- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../main/java/com/cloudinary/taglib/CloudinaryUploadTag.java | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 4b0308ab..4579fcd1 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -163,7 +163,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"), options); + "accessibility_analysis", "versions", "media_metadata"), options); return response; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 487dd5b0..6ea073ed 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,7 @@ 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"}; + "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name", "media_metadata"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java index 9157c9db..6b065865 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUploadTag.java @@ -60,6 +60,7 @@ public class CloudinaryUploadTag extends SimpleTagSupport { private Boolean overwrite = null; private Boolean phash = null; protected boolean unsigned = false; + private Boolean mediaMetadata = null; public void doTag() throws JspException, IOException { Cloudinary cloudinary = Singleton.getCloudinary(); @@ -92,6 +93,7 @@ public void doTag() throws JspException, IOException { options.put("faces", faces); options.put("colors", colors); options.put("image_metadata", imageMetadata); + options.put("media_metadata", mediaMetadata); options.put("use_filename", useFilename); options.put("unique_filename", uniqueFilename); options.put("eager_async", eagerAsync); From be017b7be2fb0244945ebc41213bea565726657d Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 15 Feb 2023 07:16:20 +0200 Subject: [PATCH 534/592] Update Hyper SQL version --- samples/photo_album/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/photo_album/pom.xml b/samples/photo_album/pom.xml index 889de220..e30bf14b 100644 --- a/samples/photo_album/pom.xml +++ b/samples/photo_album/pom.xml @@ -102,7 +102,7 @@ org.hsqldb hsqldb - 2.2.9 + 2.7.1 From a5e275ec317172fce5551cea5bb54a0631a70d4c Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 29 May 2023 12:31:36 +0300 Subject: [PATCH 535/592] Add Search folders functionality --- .../main/java/com/cloudinary/Cloudinary.java | 4 ++++ .../src/main/java/com/cloudinary/Search.java | 2 +- .../java/com/cloudinary/SearchFolders.java | 19 +++++++++++++++++++ .../cloudinary/test/AbstractSearchTest.java | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/SearchFolders.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 2229fc8f..8e1320c6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -59,6 +59,10 @@ public Search search() { return new Search(this); } + public SearchFolders searchFolders() { + return new SearchFolders(this); + } + public static void registerUploaderStrategy(String className) { if (!UPLOAD_STRATEGIES.contains(className)) { UPLOAD_STRATEGIES.add(className); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index f16be7eb..1b2bbf28 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -10,7 +10,7 @@ public class Search { - private final Api api; + protected final Api api; private ArrayList> sortByParam; private ArrayList aggregateParam; private ArrayList withFieldParam; diff --git a/cloudinary-core/src/main/java/com/cloudinary/SearchFolders.java b/cloudinary-core/src/main/java/com/cloudinary/SearchFolders.java new file mode 100644 index 00000000..1e8bc5bd --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/SearchFolders.java @@ -0,0 +1,19 @@ +package com.cloudinary; + +import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; + +import java.util.Arrays; +import java.util.Map; + +public class SearchFolders extends Search { + + public SearchFolders(Cloudinary cloudinary) { + super(cloudinary); + } + + public ApiResponse execute() throws Exception { + Map options = ObjectUtils.asMap("content_type", "json"); + return this.api.callApi(Api.HttpMethod.POST, Arrays.asList("folders", "search"), this.toQuery(), options); + } +} 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 67d62967..ac30c2c4 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 @@ -9,6 +9,9 @@ import java.lang.reflect.Field; import java.util.*; +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; @@ -19,6 +22,7 @@ abstract public class AbstractSearchTest extends MockableTest { private static final String SEARCH_TAG = "search_test_tag_" + SUFFIX; public static final String[] UPLOAD_TAGS = {SDK_TEST_TAG, SEARCH_TAG}; private static final String SEARCH_TEST = "search_test_" + SUFFIX; + private static final String SEARCH_FOLDER = "search_folder_" + SUFFIX; private static final String SEARCH_TEST_1 = SEARCH_TEST + "_1"; private static final String SEARCH_TEST_2 = SEARCH_TEST + "_2"; private static String SEARCH_TEST_ASSET_ID_1; @@ -44,6 +48,11 @@ public static void setUpClass() throws Exception { public static void tearDownClass() throws Exception { Cloudinary cloudinary = new Cloudinary(); cloudinary.api().deleteResourcesByTag(SEARCH_TAG, null); + try { + cloudinary.api().deleteFolder(SEARCH_FOLDER, null); + } catch (Exception e){ + System.err.println(e.getMessage()); + } } @Before @@ -60,6 +69,14 @@ public void shouldFindResourcesByTag() throws Exception { assertEquals(3, resources.size()); } + @Test + public void shouldFindFolders() throws Exception { + cloudinary.api().createFolder(SEARCH_FOLDER, null); + Map result = cloudinary.searchFolders().expression(String.format("name:%s", SEARCH_FOLDER)).execute(); + final List folders = (List) result.get("folders"); + assertThat(folders, hasItem(hasEntry("name", SEARCH_FOLDER))); + } + @Test public void shouldFindResourceByPublicId() throws Exception { Map result = cloudinary.search().expression(String.format("public_id:%s", SEARCH_TEST_1)).execute(); From b6e5d96491760d81537e1808ed389f7883af6280 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:49:09 +0300 Subject: [PATCH 536/592] Add `toUrl() to Search API --- .../src/main/java/com/cloudinary/Search.java | 53 +++++++++++++++++-- .../src/main/java/com/cloudinary/Url.java | 39 +++++++------- .../cloudinary/test/AbstractSearchTest.java | 34 +++++++++--- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index 1b2bbf28..652e2cda 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -1,8 +1,12 @@ package com.cloudinary; import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; +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; @@ -16,6 +20,8 @@ public class Search { private ArrayList withFieldParam; private HashMap params; + private int ttl = 300; + Search(Cloudinary cloudinary) { this.api = cloudinary.api(); this.params = new HashMap(); @@ -24,6 +30,10 @@ public class Search { this.withFieldParam = new ArrayList(); } + public Search ttl(int ttl) { + this.ttl = ttl; + return this; + } public Search expression(String value) { this.params.put("expression", value); return this; @@ -68,9 +78,15 @@ public Search sortBy(String field, String dir) { public HashMap toQuery() { HashMap queryParams = new HashMap(this.params); - queryParams.put("with_field", withFieldParam); - queryParams.put("sort_by", sortByParam); - queryParams.put("aggregate", aggregateParam); + if (withFieldParam.size() > 0) { + queryParams.put("with_field", withFieldParam); + } + if(sortByParam.size() > 0) { + queryParams.put("sort_by", sortByParam); + } + if(aggregateParam.size() > 0) { + queryParams.put("aggregate", aggregateParam); + } return queryParams; } @@ -78,4 +94,35 @@ public ApiResponse execute() throws Exception { Map options = ObjectUtils.asMap("content_type", "json"); return this.api.callApi(Api.HttpMethod.POST, Arrays.asList("resources", "search"), this.toQuery(), options); } + + + public String toUrl() throws Exception { + return toUrl(null, null); + } + + public String toUrl(String nextCursor) throws Exception { + return toUrl(null, nextCursor); + } + /*** + Creates a signed Search URL that can be used on the client side. + ***/ + public String toUrl(Integer ttl, String nextCursor) throws Exception { + String nextCursorParam = nextCursor; + String apiSecret = api.cloudinary.config.apiSecret; + if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); + if(ttl == null) { + ttl = this.ttl; + } + HashMap queryParams = toQuery(); + if(nextCursorParam == null) { + nextCursorParam = (String) queryParams.get("next_cursor"); + } + queryParams.remove("next_cursor"); + JSONObject json = ObjectUtils.toJSON(queryParams); + String base64Query = Base64Coder.encodeURLSafeString(json.toString()); + String signature = StringUtils.encodeHexString(Util.hash(String.format("%d%s%s", ttl, base64Query, apiSecret), SignatureAlgorithm.SHA256)); + String prefix = Url.unsignedDownloadUrlPrefix(null,api.cloudinary.config); + + return String.format("%s/search/%s/%d/%s%s", prefix, signature, ttl, base64Query,nextCursorParam != null && !nextCursorParam.isEmpty() ? "/"+nextCursorParam : ""); + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 360193c0..a45382cd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -339,7 +339,6 @@ public String generate() { } public String generate(String source) { - boolean useRootPath = this.config.useRootPath; if (this.useRootPath != null) { useRootPath = this.useRootPath; @@ -405,8 +404,7 @@ public String generate(String source) { String resourceType = this.resourceType; if (resourceType == null) resourceType = "image"; String finalResourceType = finalizeResourceType(resourceType, type, urlSuffix, useRootPath, config.shorten); - String prefix = unsignedDownloadUrlPrefix(source, config.cloudName, config.privateCdn, config.cdnSubdomain, config.secureCdnSubdomain, config.cname, config.secure, config.secureDistribution); - + String prefix = unsignedDownloadUrlPrefix(source, config); String join = StringUtils.join(new String[]{prefix, finalResourceType, signature, transformationStr, version, source}, "/"); String url = StringUtils.mergeSlashesInUrl(join); @@ -507,49 +505,52 @@ public String finalizeResourceType(String resourceType, String type, String urlS return result; } - public String unsignedDownloadUrlPrefix(String source, String cloudName, boolean privateCdn, boolean cdnSubdomain, Boolean secureCdnSubdomain, String cname, boolean secure, String secureDistribution) { - if (this.config.cloudName.startsWith("/")) { - return "/res" + this.config.cloudName; + public static String unsignedDownloadUrlPrefix(String source, Configuration config) { + if (config.cloudName.startsWith("/")) { + return "/res" + config.cloudName; } - boolean sharedDomain = !this.config.privateCdn; + boolean sharedDomain = !config.privateCdn; String prefix; + String cloudName; + String secureDistribution = config.secureDistribution; + Boolean secureCdnSubdomain = null; - if (this.config.secure) { - if (StringUtils.isEmpty(this.config.secureDistribution) || this.config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { - secureDistribution = this.config.privateCdn ? this.config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; + if (config.secure) { + if (StringUtils.isEmpty(config.secureDistribution) || config.secureDistribution.equals(Cloudinary.OLD_AKAMAI_SHARED_CDN)) { + secureDistribution = config.privateCdn ? config.cloudName + "-res.cloudinary.com" : Cloudinary.SHARED_CDN; } if (!sharedDomain) { sharedDomain = secureDistribution.equals(Cloudinary.SHARED_CDN); } if (secureCdnSubdomain == null && sharedDomain) { - secureCdnSubdomain = this.config.cdnSubdomain; + secureCdnSubdomain = config.cdnSubdomain; } if (secureCdnSubdomain != null && secureCdnSubdomain == true) { - secureDistribution = this.config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); + secureDistribution = config.secureDistribution.replace("res.cloudinary.com", "res-" + shard(source) + ".cloudinary.com"); } prefix = "https://" + secureDistribution; - } else if (StringUtils.isNotBlank(this.config.cname)) { - String subdomain = this.config.cdnSubdomain ? "a" + shard(source) + "." : ""; - prefix = "http://" + subdomain + this.config.cname; + } else if (StringUtils.isNotBlank(config.cname)) { + String subdomain = config.cdnSubdomain ? "a" + shard(source) + "." : ""; + prefix = "http://" + subdomain + config.cname; } else { String protocol = "http://"; - cloudName = this.config.privateCdn ? this.config.cloudName + "-" : ""; + cloudName = config.privateCdn ? config.cloudName + "-" : ""; String res = "res"; - String subdomain = this.config.cdnSubdomain ? "-" + shard(source) : ""; + String subdomain = config.cdnSubdomain ? "-" + shard(source) : ""; String domain = ".cloudinary.com"; prefix = StringUtils.join(new String[]{protocol, cloudName, res, subdomain, domain}, ""); } if (sharedDomain) { - prefix += "/" + this.config.cloudName; + prefix += "/" + config.cloudName; } return prefix; } - private String shard(String input) { + private static String shard(String input) { CRC32 crc32 = new CRC32(); crc32.update(Util.getUTF8Bytes(input)); return String.valueOf((crc32.getValue() % 5 + 5) % 5 + 1); 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 ac30c2c4..1d0a2094 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,7 +1,9 @@ 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; @@ -71,10 +73,14 @@ public void shouldFindResourcesByTag() throws Exception { @Test public void shouldFindFolders() throws Exception { - cloudinary.api().createFolder(SEARCH_FOLDER, null); - Map result = cloudinary.searchFolders().expression(String.format("name:%s", SEARCH_FOLDER)).execute(); - final List folders = (List) result.get("folders"); - assertThat(folders, hasItem(hasEntry("name", SEARCH_FOLDER))); + Map createFolderResult = cloudinary.api().createFolder(SEARCH_FOLDER, null); + Thread.sleep(3000); + if ((Boolean) createFolderResult.get("success")) { + Map result = cloudinary.searchFolders().expression(String.format("name:%s", SEARCH_FOLDER)).execute(); + System.out.println("SUCCESS!"); + final List folders = (List) result.get("folders"); + assertThat(folders, hasItem(hasEntry("name", SEARCH_FOLDER))); + } } @Test @@ -152,7 +158,23 @@ public void shouldPaginateResourcesLimitedByTagAndOrderdByAscendingPublicId() th assertEquals(3, result.get("total_count")); assertEquals(SEARCH_TEST_2, resources.get(0).get("public_id")); assertNull(result.get("next_cursor")); + } - + @Test + public void testShouldBuildSearchUrl() throws Exception { + String nextCursor = "db27cfb02b3f69cb39049969c23ca430c6d33d5a3a7c3ad1d870c54e1a54ee0faa5acdd9f6d288666986001711759d10"; + Cloudinary cloudinaryToSearch = new Cloudinary("cloudinary://key:secret@test123"); + cloudinaryToSearch.config.secure = true; + + Search search = cloudinaryToSearch.search().expression("resource_type:image AND tags=kitten AND uploaded_at>1d AND bytes>1m").sortBy("public_id", "desc").maxResults(30); + String base64Query = "eyJleHByZXNzaW9uIjoicmVzb3VyY2VfdHlwZTppbWFnZSBBTkQgdGFncz1raXR0ZW4gQU5EIHVwbG9hZGVkX2F0PjFkIEFORCBieXRlcz4xbSIsIm1heF9yZXN1bHRzIjozMCwic29ydF9ieSI6W3sicHVibGljX2lkIjoiZGVzYyJ9XX0="; + String ttl300Signature = "431454b74cefa342e2f03e2d589b2e901babb8db6e6b149abf25bc0dd7ab20b7"; + String ttl1000Signature = "25b91426a37d4f633a9b34383c63889ff8952e7ffecef29a17d600eeb3db0db7"; + + assertEquals(String.format("https://res.cloudinary.com/%s/search/%s/%d/%s", cloudinaryToSearch.config.cloudName, ttl300Signature, 300, base64Query), search.toUrl()); + assertEquals(String.format("https://res.cloudinary.com/%s/search/%s/%d/%s/%s", cloudinaryToSearch.config.cloudName, ttl300Signature, 300, base64Query, nextCursor), search.toUrl(nextCursor)); + assertEquals(String.format("https://res.cloudinary.com/%s/search/%s/%d/%s/%s", cloudinaryToSearch.config.cloudName, ttl1000Signature, 1000, base64Query, nextCursor), search.toUrl(1000, nextCursor)); + 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, "")); } -} +} \ No newline at end of file From ec9362c9a436105f13ef8652aaf463ec7a5038c1 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:39:39 +0300 Subject: [PATCH 537/592] Add visual search support --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 11 +++++++++++ .../src/main/java/com/cloudinary/Util.java | 5 ++++- .../java/com/cloudinary/test/AbstractApiTest.java | 10 ++++++++++ .../com/cloudinary/test/AbstractUploaderTest.java | 4 +++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 4579fcd1..828b821b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -92,6 +92,17 @@ public ApiResponse resources(Map options) throws Exception { return response; } + public ApiResponse visualSearch(Map options) throws Exception { + List uri = new ArrayList(); + uri.add("resources/visual_search"); + uri.add("image"); + if (options.get("text") == null && options.get("image_asset_id") == null && options.get("image_url") == null) { + throw new IllegalArgumentException("Must supply image file, image url, image asset id or text"); + } + ApiResponse response = callApi(HttpMethod.GET, uri, options, options); + return response; + } + public ApiResponse resourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 6ea073ed..553ae203 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,7 @@ 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"}; + "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name", "media_metadata", "visual_search"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { @@ -178,6 +178,9 @@ public static final void processWriteParameters(Map options, Map if (options.get("clear_invalid") != null) { params.put("clear_invalid", options.get("clear_invalid")); } + if(options.get("visual_search") != null) { + params.put("visual_search", options.get("visual_search")); + } } protected static String encodeAccessControl(Object accessControl) { 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 6fae5a41..24cc490d 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 @@ -1222,4 +1222,14 @@ public void testFolderDecoupling() { assertEquals("new_asset_folder", params.get("asset_folder")); assertEquals(true, params.get("unique_display_name")); } + + @Test + public void testVisualSearch() { + //TODO: Need to build a unit testing infrastructure + Map params = new HashMap(); + Map options = asMap( + "visual_search", true); + Util.processWriteParameters(options, params); + assertEquals(true, params.get("visual_search")); + } } 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 89b502fb..1ce9a4b8 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 @@ -813,7 +813,8 @@ public void testUploadFolderDecoupling() { "public_id_prefix", "test_id_prefix", "asset_folder", "asset_folder_test", "display_name", "display_name_test", - "use_asset_folder_as_public_id_prefix", true); + "use_asset_folder_as_public_id_prefix", true, + "visual_search", true); Map uploadParams = Util.buildUploadParams(options); Assert.assertEquals("test_id_prefix", uploadParams.get("public_id_prefix")); @@ -821,5 +822,6 @@ public void testUploadFolderDecoupling() { Assert.assertEquals("asset_folder_test", uploadParams.get("asset_folder")); Assert.assertEquals("display_name_test", uploadParams.get("display_name")); Assert.assertEquals(true, uploadParams.get("use_asset_folder_as_public_id_prefix")); + Assert.assertEquals(true, uploadParams.get("visual_search")); } } From c62bba79f4da8715140c2f49506653ebd3b95f40 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:49:51 +0300 Subject: [PATCH 538/592] Fix Provisioning tests --- .travis.yml | 2 +- .../test/AbstractAccountApiTest.java | 44 +++++++++++++++++++ .../com/cloudinary/test/MockableTest.java | 6 +++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 15fbae01..510d55ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ branches: 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 -DCLOUDINARY_ACCOUNT_URL=$CLOUDINARY_ACCOUNT_URL ciTest -p cloudinary-${MODULE} -i +script: source tools/cloudinary_url.txt && ./gradlew -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${MODULE} -i notifications: 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 8dc30fc9..7513907a 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,6 +15,7 @@ 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(); @@ -35,12 +36,14 @@ public static void setUpClass() { @Before public void setUp() throws Exception { + assumeCloudinaryAccountURLExist(); System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); this.account = new Account(new Cloudinary()); } @AfterClass public static void tearDownClass() { + assumeCloudinaryAccountURLExist(); System.out.println("Start TearDownClass"); Account account = new Account(new Cloudinary()); for (String createdSubAccountId : createdSubAccountIds) { @@ -71,6 +74,7 @@ public static void tearDownClass() { @Test public void testPassingCredentialsThroughOptions() throws Exception { + assumeCloudinaryAccountURLExist(); int exceptions = 0; Map map = singletonMap("provisioning_api_secret", new Object()) ; @@ -104,6 +108,7 @@ public void testPassingCredentialsThroughOptions() throws Exception { // Sub accounts tests @Test public void testGetSubAccount() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse accountResponse = createSubAccount(); ApiResponse account = this.account.subAccount(accountResponse.get("id").toString(), null); assertNotNull(account); @@ -111,6 +116,7 @@ public void testGetSubAccount() throws Exception { @Test public void testGetSubAccounts() throws Exception { + assumeCloudinaryAccountURLExist(); createSubAccount(); ApiResponse accounts = account.subAccounts(null, null, null, null); assertNotNull(accounts); @@ -119,6 +125,7 @@ public void testGetSubAccounts() throws Exception { @Test public void testCreateSubAccount() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse result = createSubAccount(); assertNotNull(result); @@ -135,6 +142,7 @@ public void testCreateSubAccount() throws Exception { @Test public void testUpdateSubAccount() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse subAccount = createSubAccount(); String newCloudName = randomLetters(); ApiResponse result = account.updateSubAccount(subAccount.get("id").toString(), null, newCloudName, Collections.emptyMap(), null, null); @@ -144,6 +152,7 @@ public void testUpdateSubAccount() throws Exception { @Test public void testDeleteSubAccount() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse createResult = createSubAccount(); String id = createResult.get("id").toString(); ApiResponse result = account.deleteSubAccount(id, null); @@ -155,6 +164,7 @@ public void testDeleteSubAccount() throws Exception { // Users test @Test public void testGetUser() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(); String userId = user.get("id").toString(); ApiResponse result = account.user(userId, null); @@ -165,6 +175,7 @@ public void testGetUser() throws Exception { @Test public void testGetUsers() throws Exception { + assumeCloudinaryAccountURLExist(); String user1Id = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); String user2Id = createUser(Account.Role.MASTER_ADMIN).get("id").toString(); ApiResponse result = account.users(null, Arrays.asList(user1Id, user2Id), null, null, null); @@ -185,6 +196,7 @@ public void testGetUsers() throws Exception { @Test public void testGetPendingUsers() throws Exception { + assumeCloudinaryAccountURLExist(); String id = createUser(Account.Role.BILLING).get("id").toString(); ApiResponse pending = account.users(true, Collections.singletonList(id), null, null, null); @@ -199,6 +211,7 @@ public void testGetPendingUsers() throws Exception { @Test public void testGetUsersByPrefix() throws Exception { + assumeCloudinaryAccountURLExist(); final long timeMillis = System.currentTimeMillis(); final String userName = String.format("SDK TEST Get Users By Prefix %d", timeMillis); final String userEmail = String.format("sdk-test-get-users-by-prefix+%d@cloudinary.com", timeMillis); @@ -217,6 +230,7 @@ public void testGetUsersByPrefix() throws Exception { @Test public void testGetUsersBySubAccountIds() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse subAccount = createSubAccount(); final String subAccountId = subAccount.get("id").toString(); @@ -235,6 +249,7 @@ public void testGetUsersBySubAccountIds() throws Exception { @Test public void testGetUsersThrowsWhenSubAccountIdDoesntExist() throws Exception { + assumeCloudinaryAccountURLExist(); final String subAccountId = randomLetters(); expectedException.expectMessage("Cannot find sub account with id " + subAccountId); account.users(true, null, null, subAccountId, null); @@ -242,6 +257,7 @@ public void testGetUsersThrowsWhenSubAccountIdDoesntExist() throws Exception { @Test public void testCreateUser() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse createResult = createSubAccount(); ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString())); assertNotNull(result); @@ -249,6 +265,7 @@ public void testCreateUser() throws Exception { @Test public void testCreateUserWithOptions() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse createResult = createSubAccount(); ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), ObjectUtils.emptyMap()); assertNotNull(result); @@ -256,6 +273,7 @@ public void testCreateUserWithOptions() throws Exception { @Test public void testCreateUserEnabled() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse createResult = createSubAccount(); ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), true); assertTrue((Boolean) result.get("enabled")); @@ -263,6 +281,7 @@ public void testCreateUserEnabled() throws Exception { @Test public void testCreateUserDisabled() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse createResult = createSubAccount(); ApiResponse result = createUser(Collections.singletonList(createResult.get("id").toString()), false); assertFalse((Boolean) result.get("enabled")); @@ -270,6 +289,7 @@ public void testCreateUserDisabled() throws Exception { @Test public void testUpdateUser() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(Account.Role.ADMIN); String userId = user.get("id").toString(); String newName = randomLetters(); @@ -282,6 +302,7 @@ public void testUpdateUser() throws Exception { @Test public void testUpdateUserEnabled() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(Account.Role.ADMIN); String userId = user.get("id").toString(); String newName = randomLetters(); @@ -294,6 +315,7 @@ public void testUpdateUserEnabled() throws Exception { @Test public void testUpdateUserDisabled() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(Account.Role.ADMIN); String userId = user.get("id").toString(); String newName = randomLetters(); @@ -306,6 +328,7 @@ public void testUpdateUserDisabled() throws Exception { @Test public void testDeleteUser() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(Collections.emptyList()); String id = user.get("id").toString(); ApiResponse result = account.deleteUser(id, null); @@ -316,12 +339,14 @@ public void testDeleteUser() throws Exception { // groups @Test public void testCreateUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse group = createGroup(); assertNotNull(group); } @Test public void testUpdateUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse group = createGroup(); String newName = randomLetters(); ApiResponse result = account.updateUserGroup(group.get("id").toString(), newName, null); @@ -330,6 +355,7 @@ public void testUpdateUserGroup() throws Exception { @Test public void testDeleteUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse group = createGroup(); String id = group.get("id").toString(); ApiResponse result = account.deleteUserGroup(id, null); @@ -340,6 +366,7 @@ public void testDeleteUserGroup() throws Exception { @Test public void testAddUserToUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(); ApiResponse group = createGroup(); String userId = user.get("id").toString(); @@ -350,6 +377,7 @@ public void testAddUserToUserGroup() throws Exception { @Test public void testRemoveUserFromUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = createUser(Account.Role.MEDIA_LIBRARY_ADMIN); ApiResponse group = createGroup(); String groupId = group.get("id").toString(); @@ -362,6 +390,7 @@ public void testRemoveUserFromUserGroup() throws Exception { @Test public void testListUserGroups() throws Exception { + assumeCloudinaryAccountURLExist(); createGroup(); ApiResponse result = account.userGroups(); assertNotNull(result); @@ -370,6 +399,7 @@ public void testListUserGroups() throws Exception { @Test public void testListUserGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse group = createGroup(); ApiResponse result = account.userGroup(group.get("id").toString(), null); assertNotNull(result); @@ -377,6 +407,7 @@ public void testListUserGroup() throws Exception { @Test public void testListUsersInGroup() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user1 = createUser(); ApiResponse user2 = createUser(); ApiResponse group = createGroup(); @@ -395,6 +426,7 @@ public void testListUsersInGroup() throws Exception { // Helpers private ApiResponse createGroup() throws Exception { + assumeCloudinaryAccountURLExist(); String name = randomLetters(); ApiResponse userGroup = account.createUserGroup(name); createdGroupIds.add(userGroup.get("id").toString()); @@ -402,31 +434,38 @@ private ApiResponse createGroup() throws Exception { } private ApiResponse createUser() throws Exception { + assumeCloudinaryAccountURLExist(); return createUser(Collections.emptyList()); } private ApiResponse createUser(Account.Role role) throws Exception { + assumeCloudinaryAccountURLExist(); return createUser(Collections.emptyList(), role); } private ApiResponse createUser(List subAccountsIds) throws Exception { + assumeCloudinaryAccountURLExist(); return createUser(subAccountsIds, Account.Role.BILLING); } private ApiResponse createUser(List subAccountsIds, Map options) throws Exception { + assumeCloudinaryAccountURLExist(); return createUser(subAccountsIds, Account.Role.BILLING, options); } private ApiResponse createUser(List subAccountsIds, Boolean enabled) throws Exception { + assumeCloudinaryAccountURLExist(); return createUser(subAccountsIds, Account.Role.BILLING, enabled); } private ApiResponse createUser(List subAccountsIds, Account.Role role) throws Exception { + assumeCloudinaryAccountURLExist(); String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; return createUser("TestName", email, role, subAccountsIds); } private ApiResponse createUser(List subAccountsIds, Account.Role role, Map options) throws Exception { + assumeCloudinaryAccountURLExist(); String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, null, subAccountsIds, options); createdUserIds.add(user.get("id").toString()); @@ -434,6 +473,7 @@ private ApiResponse createUser(List subAccountsIds, Account.Role role, M } private ApiResponse createUser(List subAccountsIds, Account.Role role, Boolean enabled) throws Exception { + assumeCloudinaryAccountURLExist(); String email = "sdk+" + SDK_TEST_TAG + randomLetters() + "@cloudinary.com"; ApiResponse user = account.createUser("TestUserJava"+new Date().toString(), email, role, enabled, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); @@ -441,12 +481,14 @@ private ApiResponse createUser(List subAccountsIds, Account.Role role, B } private ApiResponse createUser(final String name, String email, Account.Role role, List subAccountsIds) throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse user = account.createUser(name, email, role, subAccountsIds, null); createdUserIds.add(user.get("id").toString()); return user; } private void deleteUser(String userId){ + assumeCloudinaryAccountURLExist(); try { account.deleteUser(userId, null); createdUserIds.remove(userId); @@ -457,12 +499,14 @@ private void deleteUser(String userId){ private ApiResponse createSubAccount() throws Exception { + assumeCloudinaryAccountURLExist(); ApiResponse subAccount = account.createSubAccount(randomLetters(), null, emptyMap(), true, null); createdSubAccountIds.add(subAccount.get("id").toString()); return subAccount; } private static String randomLetters() { + assumeCloudinaryAccountURLExist(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { sb.append((char) ('a' + rand.nextInt('z' - 'a' + 1))); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java index 2308d3c2..92b272dd 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java @@ -69,4 +69,10 @@ protected static boolean shouldTestFeature(String feature) { List sdkFeaturesList = Arrays.asList(sdkFeatures.split(",")); return sdkFeatures.contains(feature.toLowerCase()) || (sdkFeaturesList.size() == 1 && sdkFeaturesList.get(0).equalsIgnoreCase(Feature.ALL)); } + + static protected boolean assumeCloudinaryAccountURLExist() { + String cloudinaryAccountUrl = System.getProperty("CLOUDINARY_ACCOUNT_URL", System.getenv("CLOUDINARY_ACCOUNT_URL")); + assumeTrue(String.format("Use CLOUDINARY_ACCOUNT_URL environment variable to enable tests"), cloudinaryAccountUrl != null); + return cloudinaryAccountUrl != null; + } } From f8e0c32c8d19388c120c0c72a93fcff6b363b661 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Tue, 8 Aug 2023 18:41:15 +0300 Subject: [PATCH 539/592] Version 1.34.0 --- CHANGELOG.md | 11 +++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bb2294c..8c12c7b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ + +1.34.0 / 2023-08-08 +=================== + + * Add visual search support + * Add `toUrl() to Search API + * Add Search folders functionality + * Update Hyper SQL version + * Add support for `media_metadata` parameter + * Add support for `clear_invalid` parameter + 1.33.0 / 2022-09-12 ================== diff --git a/README.md b/README.md index 48f45ecd..8f66ff78 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.33.0 | V | +| 1.1.0 - 1.34.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-http44 - 1.33.0 + 1.34.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 8e1320c6..4ba99f40 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.33.0"; + public final static String VERSION = "1.34.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 2fac0474..7f41fc8c 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.33.0 +version=1.34.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 25c9cc77fdc17387b5aff5dd78b44aa913dfa298 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 9 Aug 2023 15:24:38 +0300 Subject: [PATCH 540/592] Add support for `on_success` upload parameter --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 2 +- .../java/com/cloudinary/test/AbstractUploaderTest.java | 7 +++++++ 2 files changed, 8 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 553ae203..19ae2c71 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -39,7 +39,7 @@ public static final Map buildUploadParams(Map options) { params.put("public_id_prefix", (String) options.get("public_id_prefix")); params.put("asset_folder", (String) options.get("asset_folder")); params.put("display_name", (String) options.get("display_name")); - + params.put("on_success", (String) options.get("on_success")); Object responsive_breakpoints = options.get("responsive_breakpoints"); if (responsive_breakpoints != null) { params.put("responsive_breakpoints", JSONObject.wrap(responsive_breakpoints)); 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 1ce9a4b8..bf590e13 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 @@ -763,6 +763,13 @@ public void testAccessControl() throws ParseException, IOException { assertEquals("2019-03-21T22:00:00Z", accessControlResponse.get(0).get("end")); } + @Test + public void testOnSuccessScript() throws Exception { + String tags = "[\"autocaption\"" + ",\"" + SDK_TEST_TAG + "\",\"" + UPLOADER_TAG + "\"]"; + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("on_success", "current_asset.update({tags:" + tags + "});")); + assertTrue(((List)result.get("tags")).contains("autocaption")); + } + @Test public void testQualityAnalysis() throws IOException { Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("quality_analysis", true, "tags", Arrays.asList(SDK_TEST_TAG, UPLOADER_TAG))); From 9c2e22bfdf0bb4e8850987f0c4f60cfcd6747fab Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:59:31 +0300 Subject: [PATCH 541/592] Add test for fetch video overlay --- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c46672d4..8782dfc3 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1257,7 +1257,9 @@ public void testOverlayOptions() { new FetchLayer().url("https://test").resourceType("image"), "fetch:aHR0cHM6Ly90ZXN0", new FetchLayer().url("https://test"), - "fetch:aHR0cHM6Ly90ZXN0"}; + "fetch:aHR0cHM6Ly90ZXN0", + new FetchLayer().url("https://test").resourceType("video"), + "video:fetch:aHR0cHM6Ly90ZXN0"}; for (int i = 0; i < tests.length; i += 2) { Object layer = tests[i]; From 3e97239ab357457c847727a1e119c00e12a39ce7 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:11:05 +0300 Subject: [PATCH 542/592] Update analytics token --- .../java/com/cloudinary/utils/Analytics.java | 27 +++++++++++++++---- .../cloudinary/analytics/AnalyticsTest.java | 26 +++++++++--------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index e20ae5fa..b0027889 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -8,18 +8,23 @@ public class Analytics { private String sdkTokenQueryKey = "_a"; //sdkTokenQueryKey private String sdkQueryDelimiter = "="; - public String algoVersion = "A"; + public String algoVersion = "C"; + public String prodcut = "A"; public String SDKCode = ""; // Java = G, Android = F public String SDKSemver = ""; // Calculate the SDK version . public String techVersion = ""; // Calculate the Java version. + public String osType; + public String osVersion; public Analytics() { - this("G", Cloudinary.VERSION,System.getProperty("java.version")); + this("G", Cloudinary.VERSION,System.getProperty("java.version"), "Z", "0.0"); } - public Analytics(String sdkCode, String sdkVersion, String techVersion) { + public Analytics(String sdkCode, String sdkVersion, String techVersion, String osType, String osVersion) { this.SDKCode = sdkCode; this.SDKSemver = sdkVersion; this.techVersion = techVersion; + this.osType = osType; + this.osVersion = osVersion; } public Analytics setSDKCode(String SDKCode) { @@ -43,7 +48,7 @@ public Analytics setTechVersion(String techVersion) { */ public String toQueryParam() { try { - return sdkTokenQueryKey + sdkQueryDelimiter + getAlgorithmVersion() + getSDKType() + getSDKVersion() + getTechVersion() + getSDKFeatureCode(); + return sdkTokenQueryKey + sdkQueryDelimiter + getAlgorithmVersion() + prodcut + getSDKType() + getSDKVersion() + getTechVersion() + getOsType() + getOsVersion() + getSDKFeatureCode(); } catch (Exception e) { return sdkTokenQueryKey + sdkQueryDelimiter + "E"; } @@ -52,18 +57,30 @@ public String toQueryParam() { private String getTechVersion() throws Exception { String[] techVersionString = techVersion.split("_"); String[] versions = techVersionString[0].split("\\."); + return versionArrayToString(versions); + } + + private String versionArrayToString(String[] versions) throws Exception { if (versions.length > 2) { versions = Arrays.copyOf(versions, versions.length - 1); } return getPaddedString(StringUtils.join(versions, ".")); } + private String getOsType() { + return (osType != null) ? osType : "Z"; //System.getProperty("os.name"); + } + + private String getOsVersion() throws Exception { + return (osVersion != null) ? versionArrayToString(osVersion.split("\\.")) : versionArrayToString(System.getProperty("os.version").split("\\.")); + } + private String getSDKType() { return SDKCode; } private String getAlgorithmVersion() { - return "A"; + return algoVersion; } private String getSDKFeatureCode() { 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 b45e76e0..fa277c15 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -29,19 +29,19 @@ public void testEncodeVersion() { analytics.setSDKSemver("1.24.0"); analytics.setTechVersion("12.0.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAlhAM0"); + Assert.assertEquals(result, "_a=CAGAlhAMZAA0"); analytics.setSDKSemver("12.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAMAM0"); + Assert.assertEquals(result, "_a=CAGAMAMZAA0"); analytics.setSDKSemver("43.21.26"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AG///AM0"); + Assert.assertEquals(result, "_a=CAG///AMZAA0"); analytics.setSDKSemver("0.0.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AGAAAAM0"); + Assert.assertEquals(result, "_a=CAGAAAAMZAA0"); analytics.setSDKSemver("43.21.27"); result = analytics.toQueryParam(); @@ -51,17 +51,17 @@ public void testEncodeVersion() { @Test public void testToQueryParam() { - Analytics analytics = new Analytics("F", "2.0.0", "1.8.0"); + Analytics analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AFAACMh0"); + Assert.assertEquals(result, "_a=CAFAACMhZ1J0"); } @Test public void testUrlWithAnalytics() { cloudinary.config.analytics = true; - cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0")); + cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0")); String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=AFAACMh0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=CAFAACMhZ1J0"); } @Test @@ -89,21 +89,21 @@ 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=AGAu5AM0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=CAGAu5AMZAA0"); } @Test public void testMiscAnalyticsObject() { cloudinary.config.analytics = true; - Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0"); + Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0", "Z", "1.34.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=AZAlhAM0"); + Assert.assertEquals(result, "_a=CAZAlhAMZ1J0"); } @Test public void testErrorAnalytics() { cloudinary.config.analytics = true; - Analytics analytics = new Analytics("Z", "1.24.0", "0"); + Analytics analytics = new Analytics("Z", "1.24.0", "0", "Z", "1.34.0"); String result = analytics.toQueryParam(); Assert.assertEquals(result, "_a=E"); } @@ -116,7 +116,7 @@ public void testUrlNoAnalyticsWithQueryParams() { cloudinary.config.cloudName = "test123"; cloudinary.config.analytics = true; - cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"))); + cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"), "Z", System.getProperty("os.version"))); 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"); From 8688da17335b34dd97083e8edc82ec01e051bf92 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Wed, 11 Oct 2023 14:40:20 +0300 Subject: [PATCH 543/592] Version 1.35.0 --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c12c7b9..b17c3b36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +1.35.0 / 2023-10-11 +=================== + + * Update analytics token + * Add support for `on_success` upload parameter 1.34.0 / 2023-08-08 =================== diff --git a/README.md b/README.md index 8f66ff78..82137d00 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.34.0 | V | +| 1.1.0 - 1.35.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-http44 - 1.34.0 + 1.35.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 4ba99f40..1b945567 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.34.0"; + public final static String VERSION = "1.35.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 7f41fc8c..9bd9f9d8 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.34.0 +version=1.35.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 5566e593d0720d10675ced2b42f403d35f100ba0 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 26 Nov 2023 14:12:43 +0200 Subject: [PATCH 544/592] Add support to use fetch format --- .../main/java/com/cloudinary/Configuration.java | 14 ++++++++++++++ .../src/main/java/com/cloudinary/Url.java | 9 ++++++++- .../java/com/cloudinary/test/CloudinaryTest.java | 7 +++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 9d3cc039..b0dcc41f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -40,6 +40,7 @@ public class Configuration { public Map properties = new HashMap(); public Boolean secureCdnSubdomain; public boolean useRootPath; + public boolean useFetchFormat; public int timeout; public boolean loadStrategies = true; public boolean clientHints = false; @@ -68,6 +69,7 @@ private Configuration( int proxyPort, Boolean secureCdnSubdomain, boolean useRootPath, + boolean useFetchFormat, int timeout, boolean loadStrategies, boolean forceVersion, @@ -90,6 +92,7 @@ private Configuration( this.proxyPort = proxyPort; this.secureCdnSubdomain = secureCdnSubdomain; this.useRootPath = useRootPath; + this.useFetchFormat = useFetchFormat; this.timeout = timeout; this.loadStrategies = loadStrategies; this.forceVersion = forceVersion; @@ -121,6 +124,7 @@ public void update(Map config) { this.proxyPort = ObjectUtils.asInteger(config.get("proxy_port"), 0); this.secureCdnSubdomain = ObjectUtils.asBoolean(config.get("secure_cdn_subdomain"), null); this.useRootPath = ObjectUtils.asBoolean(config.get("use_root_path"), false); + this.useFetchFormat = ObjectUtils.asBoolean(config.get("use_fetch_format"), false); 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); @@ -158,6 +162,7 @@ public Map asMap() { map.put("proxy_port", proxyPort); map.put("secure_cdn_subdomain", secureCdnSubdomain); map.put("use_root_path", useRootPath); + map.put("use_fetch_format", useFetchFormat); map.put("load_strategies", loadStrategies); map.put("timeout", timeout); map.put("client_hints", clientHints); @@ -190,6 +195,7 @@ public Configuration(Configuration other) { this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; + this.useFetchFormat = other.useFetchFormat; this.timeout = other.timeout; this.clientHints = other.clientHints; if (other.authToken != null) { @@ -306,6 +312,7 @@ public static class Builder { private int proxyPort; private Boolean secureCdnSubdomain; private boolean useRootPath; + private boolean useFetchFormat; private boolean loadStrategies = true; private int timeout; private boolean clientHints = false; @@ -347,6 +354,7 @@ public Configuration build() { proxyPort, secureCdnSubdomain, useRootPath, + useFetchFormat, timeout, loadStrategies, forceVersion, @@ -453,6 +461,11 @@ public Builder setUseRootPath(boolean useRootPath) { return this; } + public Builder setUseFetchFormat(boolean useFetchFormat) { + this.useFetchFormat = useFetchFormat; + return this; + } + public Builder setLoadStrategies(boolean loadStrategies) { this.loadStrategies = loadStrategies; return this; @@ -514,6 +527,7 @@ public Builder from(Configuration other) { this.proxyPort = other.proxyPort; this.secureCdnSubdomain = other.secureCdnSubdomain; this.useRootPath = other.useRootPath; + this.useFetchFormat = other.useFetchFormat; this.loadStrategies = other.loadStrategies; this.timeout = other.timeout; this.clientHints = other.clientHints; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index a45382cd..6517bd28 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -35,6 +35,7 @@ public class Url { String source = null; private String urlSuffix; private Boolean useRootPath; + private Boolean useFetchFormat; Map sourceTransformation = null; String[] sourceTypes = null; String fallbackContent = null; @@ -76,6 +77,7 @@ public Url clone() { cloned.sourceTypes = this.sourceTypes; cloned.urlSuffix = this.urlSuffix; cloned.useRootPath = this.useRootPath; + cloned.useFetchFormat = this.useFetchFormat; cloned.longUrlSignature = this.longUrlSignature; cloned.authToken = this.authToken; return cloned; @@ -172,6 +174,11 @@ public Url useRootPath(boolean useRootPath) { return this; } + public Url useFetchFormat(boolean useFetchFormat) { + this.useFetchFormat = useFetchFormat; + return this; + } + public Url cname(String cname) { this.config.cname = cname; return this; @@ -366,7 +373,7 @@ public String generate(String source) { } } - if (type != null && type.equals("fetch") && !StringUtils.isEmpty(format)) { + if ((type != null && type.equals("fetch") || (useFetchFormat != null && useFetchFormat)) && !StringUtils.isEmpty(format)) { transformation().fetchFormat(format); this.format = null; } 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 8782dfc3..0b042bf9 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -492,6 +492,13 @@ public void testFetchFormat() { assertEquals("http://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); + } + @Test public void testEffect() { // should support effect From 759b9e7725b18992c96dc10fbcb9b69e56b74904 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 3 Dec 2023 07:25:29 +0200 Subject: [PATCH 545/592] Fix encode url for fetch layer --- .../main/java/com/cloudinary/transformation/FetchLayer.java | 2 +- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java index 9c588a79..011a88e9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java @@ -9,7 +9,7 @@ public FetchLayer() { } public FetchLayer url(String remoteUrl) { - this.publicId = Base64Coder.encodeString(remoteUrl); + this.publicId = Base64Coder.encodeURLSafeString(remoteUrl);; return this; } 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 0b042bf9..f23acd60 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1266,7 +1266,10 @@ public void testOverlayOptions() { new FetchLayer().url("https://test"), "fetch:aHR0cHM6Ly90ZXN0", new FetchLayer().url("https://test").resourceType("video"), - "video:fetch:aHR0cHM6Ly90ZXN0"}; + "video:fetch:aHR0cHM6Ly90ZXN0", + new FetchLayer().url("https://www.test.com/test/JE01118-YGP900_1_lar.jpg?version=432023"), + "fetch:aHR0cHM6Ly93d3cudGVzdC5jb20vdGVzdC9KRTAxMTE4LVlHUDkwMF8xX2xhci5qcGc_dmVyc2lvbj00MzIwMjM=" + }; for (int i = 0; i < tests.length; i += 2) { Object layer = tests[i]; From f4fdd10967c57b680bd60a5c4dcc1c03ee74f7db Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Mon, 4 Dec 2023 08:37:00 +0200 Subject: [PATCH 546/592] Version 1.36.0 --- CHANGELOG.md | 6 ++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17c3b36..dce59e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +1.36.0 / 2023-12-04 +=================== + +* Fix encode url for fetch layer +* Add support to use fetch format + 1.35.0 / 2023-10-11 =================== diff --git a/README.md b/README.md index 82137d00..c40b5901 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.35.0 | V | +| 1.1.0 - 1.36.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-http44 - 1.35.0 + 1.36.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 1b945567..a35bdc2b 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.35.0"; + public final static String VERSION = "1.36.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 9bd9f9d8..7050ae5f 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.35.0 +version=1.36.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From cd2fc064ce98cbf54a2121da67c01621313886f1 Mon Sep 17 00:00:00 2001 From: Daniel Marcos Date: Tue, 9 Jan 2024 13:59:31 +0100 Subject: [PATCH 547/592] Add missing display_name parameter to update resource --- cloudinary-core/src/main/java/com/cloudinary/Util.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 19ae2c71..99fa608c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -166,6 +166,9 @@ public static final void processWriteParameters(Map options, Map if (options.get("unique_display_name") != null) { params.put("unique_display_name", options.get("unique_display_name")); } + if (options.get("display_name") != null) { + params.put("display_name", options.get("display_name")); + } putObject("ocr", options, params); putObject("raw_convert", options, params); putObject("categorization", options, params); From 36039520710e4fef7ee6e148a95471d27e1c1953 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 14 Jan 2024 09:15:58 +0200 Subject: [PATCH 548/592] Update analytics token --- .../java/com/cloudinary/utils/Analytics.java | 23 +++++++++++++++++-- .../cloudinary/analytics/AnalyticsTest.java | 20 +++++++++------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index b0027889..d734377e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -8,7 +8,7 @@ public class Analytics { private String sdkTokenQueryKey = "_a"; //sdkTokenQueryKey private String sdkQueryDelimiter = "="; - public String algoVersion = "C"; + public String algoVersion = "D"; public String prodcut = "A"; public String SDKCode = ""; // Java = G, Android = F public String SDKSemver = ""; // Calculate the SDK version . @@ -67,12 +67,19 @@ private String versionArrayToString(String[] versions) throws Exception { return getPaddedString(StringUtils.join(versions, ".")); } + private String versionArrayToOsString(String[] versions) throws Exception { + if (versions.length > 2) { + versions = Arrays.copyOf(versions, versions.length - 1); + } + return getOsVersionString(StringUtils.join(versions, ".")); + } + private String getOsType() { return (osType != null) ? osType : "Z"; //System.getProperty("os.name"); } private String getOsVersion() throws Exception { - return (osVersion != null) ? versionArrayToString(osVersion.split("\\.")) : versionArrayToString(System.getProperty("os.version").split("\\.")); + return (osVersion != null) ? versionArrayToOsString(osVersion.split("\\.")) : versionArrayToString(System.getProperty("os.version").split("\\.")); } private String getSDKType() { @@ -91,6 +98,18 @@ private String getSDKVersion() throws Exception { return getPaddedString(SDKSemver); } + private String getOsVersionString(String string) throws Exception { + String[] parts = string.split("\\."); + String result = ""; + for(int i = 0 ; i < parts.length ; i++) { + int num = Integer.parseInt(parts[i]); + String binaryString = Integer.toBinaryString(num); + binaryString = StringUtils.padStart(binaryString, 6, '0'); + result = result + Base64Map.values.get(binaryString); + } + return result; + } + private String getPaddedString(String string) throws Exception { String paddedReversedSemver = ""; int parts = string.split("\\.").length; 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 fa277c15..7b4369a0 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -29,19 +29,19 @@ public void testEncodeVersion() { analytics.setSDKSemver("1.24.0"); analytics.setTechVersion("12.0.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAGAlhAMZAA0"); + Assert.assertEquals(result, "_a=DAGAlhAMZAA0"); analytics.setSDKSemver("12.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAGAMAMZAA0"); + Assert.assertEquals(result, "_a=DAGAMAMZAA0"); analytics.setSDKSemver("43.21.26"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAG///AMZAA0"); + Assert.assertEquals(result, "_a=DAG///AMZAA0"); analytics.setSDKSemver("0.0.0"); result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAGAAAAMZAA0"); + Assert.assertEquals(result, "_a=DAGAAAAMZAA0"); analytics.setSDKSemver("43.21.27"); result = analytics.toQueryParam(); @@ -53,7 +53,11 @@ public void testEncodeVersion() { public void testToQueryParam() { Analytics analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAFAACMhZ1J0"); + Assert.assertEquals(result, "_a=DAFAACMhZBi0"); + + analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "16.3"); + result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=DAFAACMhZQD0"); } @Test @@ -61,7 +65,7 @@ public void testUrlWithAnalytics() { cloudinary.config.analytics = true; cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0")); String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=CAFAACMhZ1J0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=DAFAACMhZBi0"); } @Test @@ -89,7 +93,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=CAGAu5AMZAA0"); + Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=DAGAu5AMZAA0"); } @Test @@ -97,7 +101,7 @@ public void testMiscAnalyticsObject() { cloudinary.config.analytics = true; Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0", "Z", "1.34.0"); String result = analytics.toQueryParam(); - Assert.assertEquals(result, "_a=CAZAlhAMZ1J0"); + Assert.assertEquals(result, "_a=DAZAlhAMZBi0"); } @Test From c109e5e36a3e9cacf35bc8e6ee6eddaacb54b7cb Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 14 Jan 2024 09:24:19 +0200 Subject: [PATCH 549/592] Version 1.37.0 --- CHANGELOG.md | 6 ++++++ README.md | 6 +++--- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dce59e1a..72a14fd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +1.37.0 / 2024-01-14 +=================== + +* Update analytics token +* Add missing display name parameter + 1.36.0 / 2023-12-04 =================== diff --git a/README.md b/README.md index c40b5901..12989c75 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.36.0 | V | +| 1.1.0 - 1.37.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 : @@ -35,8 +35,8 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor ```xml com.cloudinary - cloudinary-http44 - 1.36.0 + cloudinary-http45 + 1.37.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index a35bdc2b..3d1b7168 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.36.0"; + public final static String VERSION = "1.37.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 7050ae5f..2202f4f5 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.36.0 +version=1.37.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 0476ad62e14a0dc87ecf2c2636e3e7972c4fbac1 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 15 Feb 2024 07:50:28 +0200 Subject: [PATCH 550/592] Add `notification_url` support to rename and destroy --- cloudinary-core/src/main/java/com/cloudinary/Uploader.java | 2 ++ .../java/com/cloudinary/test/AbstractUploaderTest.java | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index e9a6909a..d2ae11ea 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -234,6 +234,7 @@ public Map destroy(String publicId, Map options) throws IOException { params.put("type", (String) options.get("type")); params.put("public_id", publicId); params.put("invalidate", ObjectUtils.asBoolean(options.get("invalidate"), false).toString()); + params.put("notification_url", (String) options.get("notification_url")); return callApi("destroy", params, options, null); } @@ -249,6 +250,7 @@ public Map rename(String fromPublicId, String toPublicId, Map options) throws IO params.put("to_type", options.get("to_type")); params.put("context", ObjectUtils.asBoolean(options.get("context"), false).toString()); params.put("metadata", ObjectUtils.asBoolean(options.get("metadata"), false).toString()); + params.put("notification_url", (String) options.get("notification_url")); return callApi("rename", params, options, null); } 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 bf590e13..1c12748b 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 @@ -831,4 +831,11 @@ public void testUploadFolderDecoupling() { Assert.assertEquals(true, uploadParams.get("use_asset_folder_as_public_id_prefix")); Assert.assertEquals(true, uploadParams.get("visual_search")); } + + @Test + public void testNotificationUrl() { + Map options = asMap("notification_url", "https://www.test.com"); + Map uploadParams = Util.buildUploadParams(options); + Assert.assertEquals("https://www.test.com", uploadParams.get("notification_url")); + } } From b4694e7d1f5d8cc7894e980eb0fcc924f9a772c7 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:52:02 +0200 Subject: [PATCH 551/592] Add analytics feature flag support --- .../java/com/cloudinary/utils/Analytics.java | 18 ++++++++++++----- .../cloudinary/analytics/AnalyticsTest.java | 20 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java index d734377e..55001eb2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Analytics.java @@ -16,15 +16,18 @@ public class Analytics { public String osType; public String osVersion; + public String featureFlag = "0"; + public Analytics() { - this("G", Cloudinary.VERSION,System.getProperty("java.version"), "Z", "0.0"); + this("G", Cloudinary.VERSION,System.getProperty("java.version"), "Z", "0.0", "0"); } - public Analytics(String sdkCode, String sdkVersion, String techVersion, String osType, String osVersion) { + public Analytics(String sdkCode, String sdkVersion, String techVersion, String osType, String osVersion, String featureFlag) { this.SDKCode = sdkCode; this.SDKSemver = sdkVersion; this.techVersion = techVersion; this.osType = osType; this.osVersion = osVersion; + this.featureFlag = featureFlag; } public Analytics setSDKCode(String SDKCode) { @@ -42,13 +45,18 @@ public Analytics setTechVersion(String techVersion) { return this; } + public Analytics setFeatureFlag(String flag) { + this.featureFlag = flag; + return this; + } + /** * Function turn analytics variables into viable query parameter. * @return query param with analytics values. */ public String toQueryParam() { try { - return sdkTokenQueryKey + sdkQueryDelimiter + getAlgorithmVersion() + prodcut + getSDKType() + getSDKVersion() + getTechVersion() + getOsType() + getOsVersion() + getSDKFeatureCode(); + return sdkTokenQueryKey + sdkQueryDelimiter + getAlgorithmVersion() + prodcut + getSDKType() + getSDKVersion() + getTechVersion() + getOsType() + getOsVersion() + getSDKFeatureFlag(); } catch (Exception e) { return sdkTokenQueryKey + sdkQueryDelimiter + "E"; } @@ -90,8 +98,8 @@ private String getAlgorithmVersion() { return algoVersion; } - private String getSDKFeatureCode() { - return "0"; + private String getSDKFeatureFlag() { + return featureFlag; } private String getSDKVersion() throws Exception { 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 7b4369a0..6300947e 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -51,11 +51,11 @@ public void testEncodeVersion() { @Test public void testToQueryParam() { - Analytics analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0"); + Analytics analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0", "0"); String result = analytics.toQueryParam(); Assert.assertEquals(result, "_a=DAFAACMhZBi0"); - analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "16.3"); + analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "16.3", "0"); result = analytics.toQueryParam(); Assert.assertEquals(result, "_a=DAFAACMhZQD0"); } @@ -63,7 +63,7 @@ public void testToQueryParam() { @Test public void testUrlWithAnalytics() { cloudinary.config.analytics = true; - cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0")); + 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"); } @@ -99,7 +99,7 @@ public void testUrlWithNoAnalyticsNullAndTrue() { @Test public void testMiscAnalyticsObject() { cloudinary.config.analytics = true; - Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0", "Z", "1.34.0"); + Analytics analytics = new Analytics("Z", "1.24.0", "12.0.0", "Z", "1.34.0", "0"); String result = analytics.toQueryParam(); Assert.assertEquals(result, "_a=DAZAlhAMZBi0"); } @@ -107,7 +107,7 @@ public void testMiscAnalyticsObject() { @Test public void testErrorAnalytics() { cloudinary.config.analytics = true; - Analytics analytics = new Analytics("Z", "1.24.0", "0", "Z", "1.34.0"); + Analytics analytics = new Analytics("Z", "1.24.0", "0", "Z", "1.34.0", "0"); String result = analytics.toQueryParam(); Assert.assertEquals(result, "_a=E"); } @@ -120,13 +120,21 @@ public void testUrlNoAnalyticsWithQueryParams() { cloudinary.config.cloudName = "test123"; cloudinary.config.analytics = true; - cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"), "Z", System.getProperty("os.version"))); + 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"); cloudinary.config.privateCdn = false; } + @Test + public void testFeatureFlag() { + Analytics analytics = new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0", "0"); + analytics.setFeatureFlag("F"); + String result = analytics.toQueryParam(); + Assert.assertEquals(result, "_a=DAFAACMhZBiF"); + } + @After public void tearDown() { cloudinary.config.analytics = false; From c1abb9169c1a5e5b58932db314e45fd436a7f9bf Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 18 Feb 2024 08:22:05 +0200 Subject: [PATCH 552/592] Version 1.38.0 --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72a14fd8..0a520f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +1.38.0 / 2024-02-18 +=================== + +* Add `notification_url` support to rename and destroy + 1.37.0 / 2024-01-14 =================== diff --git a/README.md b/README.md index 12989c75..b4eb9244 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.37.0 | V | +| 1.1.0 - 1.38.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.37.0 + 1.38.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3d1b7168..af553631 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.37.0"; + public final static String VERSION = "1.38.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 2202f4f5..b3933bac 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.37.0 +version=1.38.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true 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 553/592] 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 554/592] 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 555/592] 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 556/592] 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 557/592] 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 558/592] 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 559/592] 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 560/592] 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 561/592] 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 562/592] 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 563/592] 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 564/592] 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 565/592] 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 566/592] 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 567/592] 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 568/592] 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 569/592] 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 570/592] 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 571/592] 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 572/592] 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 573/592] 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 574/592] 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 575/592] 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 576/592] 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 577/592] 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 578/592] 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 579/592] 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 580/592] 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 581/592] 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 582/592] 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 583/592] 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 584/592] 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 585/592] 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 586/592] 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 587/592] 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 588/592] 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 589/592] 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 590/592] 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 591/592] 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 592/592] 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