From 95dc2b4a059829ae7b53af190593455aee6e5848 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 5 Jun 2019 10:06:34 +0300 Subject: [PATCH 1/8] Add structured metadata APIs and entities - WIP --- .../src/main/java/com/cloudinary/Api.java | 129 ++++++++-- .../main/java/com/cloudinary/Uploader.java | 19 ++ .../src/main/java/com/cloudinary/Util.java | 2 + .../metadata/AbstractMetadataField.java | 71 ++++++ .../metadata/DateMetadataField.java | 9 + .../metadata/EnumMetadataField.java | 7 + .../cloudinary/metadata/IntMetadataField.java | 7 + .../metadata/MetadataDataSource.java | 47 ++++ .../metadata/MetadataFieldType.java | 14 ++ .../metadata/MetadataValidation.java | 80 +++++++ .../cloudinary/metadata/SetMetadataField.java | 9 + .../metadata/StringMetadataField.java | 7 + .../com/cloudinary/test/CloudinaryTest.java | 2 +- .../test/StructuredMetadataTest.java | 4 + .../test/AbstractStructuredMetadataTest.java | 220 ++++++++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 2 + 16 files changed, 606 insertions(+), 23 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java 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/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-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 d81dcd9d..8f190c9c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -5,6 +5,9 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; import com.cloudinary.api.exceptions.*; +import com.cloudinary.metadata.AbstractMetadataField; +import com.cloudinary.metadata.MetadataDataSource; +import com.cloudinary.metadata.StringMetadataField; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -15,6 +18,7 @@ public class Api { public enum HttpMethod {GET, POST, PUT, DELETE;} + public final static Map> CLOUDINARY_API_ERROR_CLASSES = new HashMap>(); static { @@ -30,6 +34,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 +83,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 +377,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 +401,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 +423,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 +489,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 +511,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
    • @@ -525,11 +533,11 @@ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, * @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
      • @@ -552,4 +560,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(AbstractMetadataField field) throws Exception { + List uri = Collections.singletonList("metadata_fields"); + return callApi(HttpMethod.POST, uri, Collections.singletonMap ("json", field), ObjectUtils.asMap ("content_type", "json")); + } + + /** + * List all the metadata field definitions (structure, not values) + * @return A map containing the list of field definitions. + * @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 externalId The if of the field to retrive + * @return The fields definitions. + * @throws Exception + */ + public ApiResponse metadataFieldByFieldId(String externalId) throws Exception { + return callApi(HttpMethod.GET, Arrays.asList("metadata_fields", externalId), Collections.emptyMap(), Collections.emptyMap()); + } + + /** + * Update the definitions of a single metadata field. + * @param externalId The id of the field to update + * @param field The field definition + * @return The updated fields definition. + * @throws Exception + */ + public ApiResponse updateMetadataField(String externalId, StringMetadataField field) throws Exception { + List uri = Arrays.asList("metadata_fields", externalId); + return callApi(HttpMethod.PUT, uri, Collections.singletonMap("json", 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("json", 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 TODO + * @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("ids", entriesExternalId) , Collections.emptyMap()); + } + + /** + * Delete a field definition. + * @param externalId 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 externalId) throws Exception { + List uri = Arrays.asList("metadata_fields", externalId); + 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/AbstractMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java new file mode 100644 index 00000000..c09f7976 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java @@ -0,0 +1,71 @@ +package com.cloudinary.metadata; + +import com.cloudinary.api.ApiResponse; +import org.cloudinary.json.JSONObject; + +import java.util.List; +import java.util.Map; + +public class AbstractMetadataField extends JSONObject { + + AbstractMetadataField(MetadataFieldType type) { + put("type", type.toString()); + } + + public static List from(Map response) { + return null; + } + + public MetadataFieldType getType() { + return MetadataFieldType.valueOf(optString("type").toUpperCase()); + } + + public String getExternalId() { + return optString("external_id"); + } + + public void setExternalId(String externalId) { + put("external_id", externalId); + } + + public String getLabel() { + return optString("label"); + } + + public void setLabel(String label) { + put("label", label); + } + + public boolean isMandatory() { + return optBoolean("mandatory"); + } + + public void setMandatory(Boolean mandatory) { + put("mandatory", mandatory); + } + + public T getDefaultValue() { + //noinspection unchecked + return (T)opt("default_value"); + } + + public void setDefaultValue(T defaultValue) { + put("default_value", defaultValue); + } + + public MetadataValidation getValidation() { + return (MetadataValidation) optJSONObject("validation"); + } + + public void setValidation(MetadataValidation validation) { + put("validation", validation); + } + + public MetadataDataSource getDataSource() { + return (MetadataDataSource) optJSONObject("datasource"); + } + + 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/DateMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java new file mode 100644 index 00000000..9da9ab8b --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java @@ -0,0 +1,9 @@ +package com.cloudinary.metadata; + +import java.util.Date; + +public class DateMetadataField extends AbstractMetadataField { + public DateMetadataField() { + super(MetadataFieldType.DATE); + } +} 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..c9bf5cd4 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java @@ -0,0 +1,7 @@ +package com.cloudinary.metadata; + +public class EnumMetadataField extends AbstractMetadataField{ + 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..ff839208 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java @@ -0,0 +1,7 @@ +package com.cloudinary.metadata; + +public class IntMetadataField extends AbstractMetadataField { + 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..fb07ff6d --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java @@ -0,0 +1,47 @@ +package com.cloudinary.metadata; + +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; + +import java.util.List; + +public class MetadataDataSource extends JSONObject { + public MetadataDataSource(List entries) { + put("values", new JSONArray(entries.toArray())); + } + + public static class Entry extends JSONObject { + public Entry(String externalId, String value){ + setExternalId(externalId); + setValue(value); + } + + public Entry(String value){ + this(null, value); + } + + public void setExternalId(String externalId) { + put("external_id", externalId); + } + + public String getExternalId() { + return optString("external_id"); + } + + public void setValue(String value) { + put("value", value); + } + + public String getValue() { + return optString("value"); + } + + public void setState(String state) { + put("state", state); + } + + public String getState() { + return optString("state"); + } + } +} 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..70e28e9d --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java @@ -0,0 +1,14 @@ +package com.cloudinary.metadata; + +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..c83bf26a --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java @@ -0,0 +1,80 @@ +package com.cloudinary.metadata; + +import org.cloudinary.json.JSONArray; +import org.cloudinary.json.JSONObject; + +import java.util.Date; +import java.util.List; + +public abstract class MetadataValidation extends JSONObject { + + public static class AndValidator extends MetadataValidation { + public AndValidator(List rules) { + put("type", "and"); + put("rules", new JSONArray(rules)); + } + } + + public static class StringLength extends MetadataValidation { + public StringLength(Integer min, Integer max) { + put("type", "strlen"); + put("min", min); + put("max", max); + } + } + + abstract static class ComparisonRule extends MetadataValidation { + ComparisonRule(String type, T value) { + this(type, value, null); + } + + ComparisonRule(String type, T value, Boolean equals) { + put("type", type); + put("value", value); + if (equals != null) { + put("equals", equals); + } + } + } + + public static class IntGreaterThan extends ComparisonRule { + public IntGreaterThan(Integer value) { + super("greater_than", value); + } + + public IntGreaterThan(Integer value, Boolean equals) { + super("greater_than", value, equals); + } + } + + public static class DateGreaterThan extends ComparisonRule { + public DateGreaterThan(Date value) { + super("greater_than", value); + } + + public DateGreaterThan(Date value, Boolean equals) { + super("greater_than", value, equals); + } + } + + public static class IntLessThan extends ComparisonRule { + public IntLessThan(Integer value) { + super("less_than", value); + } + + public IntLessThan(Integer value, Boolean equals) { + super("less_than", value, equals); + } + } + + public static class DateLessThan extends ComparisonRule { + public DateLessThan(Date value) { + super("less_than", value); + } + + public DateLessThan(Date value, Boolean equals) { + super("less_than", value, equals); + } + } +} + 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..3b60b07f --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java @@ -0,0 +1,9 @@ +package com.cloudinary.metadata; + +import java.util.List; + +public class SetMetadataField extends AbstractMetadataField> { + 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..f19b0167 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java @@ -0,0 +1,7 @@ +package com.cloudinary.metadata; + +public class StringMetadataField extends AbstractMetadataField { + public StringMetadataField() { + super(MetadataFieldType.STRING); + } +} \ 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 65e632eb..0142f521 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -99,7 +99,7 @@ public void testSecureDistribution() { @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); } 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..34db3664 --- /dev/null +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -0,0 +1,220 @@ +package com.cloudinary.test; + +import com.cloudinary.Api; +import com.cloudinary.Cloudinary; +import com.cloudinary.api.ApiResponse; +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 { + + + // TODO remove from this line + Api api = new Cloudinary().api(); + ApiResponse fields = api.listMetadataFields(); + List list = (List) api.listMetadataFields().get("fields"); + for (Object o : list) { + try { + api.deleteMetadataField(((Map) o).get("external_id").toString()); + } catch (Exception ignored) { + } + } + // TODO until this one!! + + 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 = createStringField("testCreateMetadata_1"); + ApiResponse result = getFieldResult(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 testListFields() throws Exception { + StringMetadataField stringField = createStringField("testListFields"); + getFieldResult(stringField); + + ApiResponse result = cloudinary.api().listMetadataFields(); + assertNotNull(result); + // TODO server returns array in json root, waiting for fix + + } + + @Test + public void testGetMetadata() throws Exception { + ApiResponse fieldResult = getFieldResult(createStringField("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 = getFieldResult(createStringField("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 = getFieldResult(createStringField("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 = getFieldResult(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 { + // TODO server responds with html page 404 + SetMetadataField setField = createSetField("testDeleteDatasourceEntries"); + ApiResponse fieldResult = getFieldResult(setField); + MetadataDataSource.Entry newEntry = new MetadataDataSource.Entry("id1", "new1"); + + api.updateMetadataFieldDatasource(fieldResult.get("external_id").toString(), Collections.singletonList(newEntry)); + ApiResponse result = api.deleteDatasourceEntries(fieldResult.get("external_id").toString(), Collections.singletonList("id1")); + assertNotNull(result); + assertEquals("new1", ((Map) ((List) result.get("values")).get(0)).get("value")); + } + + @Test + public void testUploadWithMetadata() throws Exception { + StringMetadataField field = createStringField("testUploadWithMetadata"); + ApiResponse fieldResult = getFieldResult(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)); + } + + private ApiResponse getFieldResult(AbstractMetadataField field) throws Exception { + ApiResponse apiResponse = api.addMetadataField(field); + metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); + return apiResponse; + } + + @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 = createStringField("testExplicitWithMetadata"); + ApiResponse fieldResult = getFieldResult(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)); + } + + @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 = createStringField("testUpdateWithMetadata"); + ApiResponse fieldResult = getFieldResult(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 = createStringField("testUploaderUpdateMetadata"); + ApiResponse fieldResult = getFieldResult(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 createStringField(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; + } +} 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 b7a3e923707aa272d912762d529bf54723a4916c Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 13 Jun 2019 16:09:27 +0300 Subject: [PATCH 2/8] Support structured metadata APIs (WIP) --- .../src/main/java/com/cloudinary/Api.java | 12 ++-- .../strategies/AbstractApiStrategy.java | 9 +++ .../com/cloudinary/test/CloudinaryTest.java | 2 +- .../com/cloudinary/http42/ApiStrategy.java | 3 +- .../com/cloudinary/http43/ApiStrategy.java | 3 +- .../com/cloudinary/http44/ApiStrategy.java | 3 +- .../test/AbstractStructuredMetadataTest.java | 58 +++++++------------ 7 files changed, 39 insertions(+), 51 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 8f190c9c..c720f5f6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -567,8 +567,8 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O * @throws Exception */ public ApiResponse addMetadataField(AbstractMetadataField field) throws Exception { - List uri = Collections.singletonList("metadata_fields"); - return callApi(HttpMethod.POST, uri, Collections.singletonMap ("json", field), ObjectUtils.asMap ("content_type", "json")); + return callApi(HttpMethod.POST, Collections.singletonList("metadata_fields"), + ObjectUtils.toMap(field), ObjectUtils.asMap ("content_type", "json")); } /** @@ -597,9 +597,9 @@ public ApiResponse metadataFieldByFieldId(String externalId) throws Exception { * @return The updated fields definition. * @throws Exception */ - public ApiResponse updateMetadataField(String externalId, StringMetadataField field) throws Exception { + public ApiResponse updateMetadataField(String externalId, AbstractMetadataField field) throws Exception { List uri = Arrays.asList("metadata_fields", externalId); - return callApi(HttpMethod.PUT, uri, Collections.singletonMap("json", field), Collections.singletonMap("content_type", "json")); + return callApi(HttpMethod.PUT, uri, ObjectUtils.toMap(field), Collections.singletonMap("content_type", "json")); } /** @@ -619,12 +619,12 @@ public ApiResponse updateMetadataFieldDatasource(String fieldExternalId, List entriesExternalId) throws Exception { List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource"); - return callApi(HttpMethod.DELETE, uri,Collections.singletonMap("ids", entriesExternalId) , Collections.emptyMap()); + return callApi(HttpMethod.DELETE, uri,Collections.singletonMap("external_ids", entriesExternalId) , Collections.emptyMap()); } /** 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..5a6e6375 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.Api; import com.cloudinary.Api.HttpMethod; import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; public abstract class AbstractApiStrategy { protected Api api; @@ -15,4 +16,12 @@ public void init(Api api) { @SuppressWarnings("rawtypes") public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; + + protected String getJsonStringFromParams(Map params) { + if (params.containsKey("json")){ + return params.get("json").toString(); + } + + return ObjectUtils.toJSON(params).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 0142f521..65e632eb 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -99,7 +99,7 @@ public void testSecureDistribution() { @Test public void testSecureDistributionOverwrite() { // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secureDistribution("something.else.com").generate("test"); + String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); assertEquals("https://something.else.com/test123/image/upload/test", result); } 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..519aaea7 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -91,8 +91,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, 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))); @@ -148,7 +130,7 @@ public void testUploadWithMetadata() throws Exception { assertEquals("123456", ((Map) result.get("metadata")).get(fieldId)); } - private ApiResponse getFieldResult(AbstractMetadataField field) throws Exception { + private ApiResponse addFieldToAccount(AbstractMetadataField field) throws Exception { ApiResponse apiResponse = api.addMetadataField(field); metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); return apiResponse; @@ -158,8 +140,8 @@ private ApiResponse getFieldResult(AbstractMetadataField field) 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 = createStringField("testExplicitWithMetadata"); - ApiResponse fieldResult = getFieldResult(field); + 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)); @@ -171,8 +153,8 @@ 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 = createStringField("testUpdateWithMetadata"); - ApiResponse fieldResult = getFieldResult(field); + 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)); @@ -182,8 +164,8 @@ public void testUpdateWithMetadata() throws Exception { @Test public void testUploaderUpdateMetadata() throws Exception { - StringMetadataField field = createStringField("testUploaderUpdateMetadata"); - ApiResponse fieldResult = getFieldResult(field); + 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); @@ -208,7 +190,7 @@ private SetMetadataField createSetField(String labelPrefix) { return setField; } - private StringMetadataField createStringField(String labelPrefix) throws Exception { + private StringMetadataField newFieldInstance(String labelPrefix) throws Exception { StringMetadataField field = new StringMetadataField(); String label = labelPrefix + "_" + SUFFIX; field.setLabel(label); From ed656b562020461ce08780a3e39f0a19d0b9dbb8 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 7 Jul 2019 17:48:35 +0300 Subject: [PATCH 3/8] Fix date serialization, Add doc strings, use latest API (after fixes), more complete tests. --- .../src/main/java/com/cloudinary/Api.java | 2 +- .../metadata/AbstractMetadataField.java | 98 +++++++++---- .../metadata/AbstractMetadataValuesField.java | 27 ++++ .../metadata/DateMetadataField.java | 31 +++++ .../metadata/EnumMetadataField.java | 5 +- .../cloudinary/metadata/IntMetadataField.java | 3 + .../metadata/MetadataDataSource.java | 39 ++++-- .../metadata/MetadataFieldType.java | 3 + .../metadata/MetadataValidation.java | 129 +++++++++++++++--- .../cloudinary/metadata/SetMetadataField.java | 5 +- .../metadata/StringMetadataField.java | 3 + .../strategies/AbstractApiStrategy.java | 8 -- .../com/cloudinary/utils/ObjectUtils.java | 20 ++- .../com/cloudinary/http42/ApiStrategy.java | 3 +- .../com/cloudinary/http43/ApiStrategy.java | 3 +- .../com/cloudinary/http44/ApiStrategy.java | 3 +- .../test/AbstractStructuredMetadataTest.java | 55 ++++++-- 17 files changed, 362 insertions(+), 75 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index c720f5f6..32c0d9df 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -612,7 +612,7 @@ public ApiResponse updateMetadataField(String externalId, AbstractMetadataField */ public ApiResponse updateMetadataFieldDatasource(String fieldExternalId, List entries) throws Exception { List uri = Arrays.asList("metadata_fields", fieldExternalId, "datasource"); - return callApi(HttpMethod.PUT, uri, Collections.singletonMap("json", entries), Collections.singletonMap("content_type", "json")); + return callApi(HttpMethod.PUT, uri, Collections.singletonMap("values", entries), Collections.singletonMap("content_type", "json")); } /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java index c09f7976..ff0138b4 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java @@ -1,71 +1,113 @@ package com.cloudinary.metadata; -import com.cloudinary.api.ApiResponse; import org.cloudinary.json.JSONObject; -import java.util.List; -import java.util.Map; +import java.text.ParseException; +/** + * Represents a single metadata field. Use one of the derived classes in the metadata API calls. + * @param + */ public class AbstractMetadataField extends JSONObject { - AbstractMetadataField(MetadataFieldType type) { - put("type", type.toString()); - } + 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 static List from(Map response) { - return null; + AbstractMetadataField(MetadataFieldType type) { + put(TYPE, type.toString()); } + /** + * The type of the field. + * @return String with the name of the type. + */ public MetadataFieldType getType() { - return MetadataFieldType.valueOf(optString("type").toUpperCase()); + return MetadataFieldType.valueOf(optString(TYPE).toUpperCase()); } + /** + * Get the id of the field. + * @return String, field id. + */ public String getExternalId() { - return optString("external_id"); + 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); + put(EXTERNAL_ID, externalId); } + /** + * Get the label of the field + * @return String, the label of the field. + */ public String getLabel() { - return optString("label"); + return optString(LABEL); } + /** + * Sets the label of the field + * @param label The label to set. + */ public void setLabel(String label) { - put("label", label); + put(LABEL, label); } + /** + * Cehcks whether the field is mandatory. + * @return Boolean indicating whether the field is mandatory. + */ public boolean isMandatory() { - return optBoolean("mandatory"); + 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); + put(MANDATORY, mandatory); } - public T getDefaultValue() { + /** + * 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"); + 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); + put(DEFAULT_VALUE, defaultValue); } + /** + * Get the validation rules of this field. + * @return The validation rules. + */ public MetadataValidation getValidation() { - return (MetadataValidation) optJSONObject("validation"); + 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); - } - - public MetadataDataSource getDataSource() { - return (MetadataDataSource) optJSONObject("datasource"); - } - - public void setDataSource(MetadataDataSource dataSource) { - put("datasource", dataSource); + put(VALIDATION, validation); } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java new file mode 100644 index 00000000..b2490209 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java @@ -0,0 +1,27 @@ +package com.cloudinary.metadata; + +/** + * This class is the abstract base for field classes based on a list of valid values (at the moment set+enum). + * @param The type of the value. + */ +public abstract class AbstractMetadataValuesField extends AbstractMetadataField { + AbstractMetadataValuesField(MetadataFieldType type) { + super(type); + } + + /** + * 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); + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java index 9da9ab8b..595f7ab0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java @@ -1,9 +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 AbstractMetadataField { + 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.toMetadataFormat(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.fromMetadataFormat(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 index c9bf5cd4..4102ed8e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java @@ -1,6 +1,9 @@ package com.cloudinary.metadata; -public class EnumMetadataField extends AbstractMetadataField{ +/** + * Represents a metadata field with 'Enum' type. + */ +public class EnumMetadataField extends AbstractMetadataValuesField{ 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 index ff839208..d2b07dae 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java @@ -1,5 +1,8 @@ package com.cloudinary.metadata; +/** + * Represents a metadata field with 'Int' type. + */ public class IntMetadataField extends AbstractMetadataField { 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 index fb07ff6d..043556cd 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java @@ -5,43 +5,66 @@ 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"); } - - public void setState(String state) { - put("state", state); - } - - public String getState() { - return optString("state"); - } } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java index 70e28e9d..34362f27 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java @@ -1,5 +1,8 @@ package com.cloudinary.metadata; +/** + * Enum represneting all the valid field types. + */ public enum MetadataFieldType { STRING, INTEGER, diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java index c83bf26a..8b6d5a79 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java @@ -1,79 +1,176 @@ 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)); + 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); + put(TYPE, STRLEN); + put(MIN, min); + put(MAX, max); } } + /** + * Base class for all comparison (greater than/less than) validation rules. + * @param + */ abstract static class ComparisonRule extends MetadataValidation { ComparisonRule(String type, T value) { this(type, value, null); } ComparisonRule(String type, T value, Boolean equals) { - put("type", type); - put("value", value); + put(TYPE, type); + putValue(value); if (equals != null) { - put("equals", equals); + 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); + 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); + 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); + 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); + super(GREATER_THAN, value, equals); + } + + @Override + protected void putValue(Date value) { + put(VALUE, ObjectUtils.toMetadataFormat(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); + 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); + 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); + 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); + super(LESS_THAN, value, equals); + } + + @Override + protected void putValue(Date value) { + put(VALUE, ObjectUtils.toMetadataFormat(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 index 3b60b07f..f6d8b56e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java @@ -2,7 +2,10 @@ import java.util.List; -public class SetMetadataField extends AbstractMetadataField> { +/** + * Represents a metadata field with 'Set' type. + */ +public class SetMetadataField extends AbstractMetadataValuesField> { 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 index f19b0167..df68ef21 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java @@ -1,5 +1,8 @@ package com.cloudinary.metadata; +/** + * Represents a metadata field with 'String' type. + */ public class StringMetadataField extends AbstractMetadataField { public StringMetadataField() { super(MetadataFieldType.STRING); 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 5a6e6375..a66f06b9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -16,12 +16,4 @@ public void init(Api api) { @SuppressWarnings("rawtypes") public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; - - protected String getJsonStringFromParams(Map params) { - if (params.containsKey("json")){ - return params.get("json").toString(); - } - - return ObjectUtils.toJSON(params).toString(); - } } 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..51ed5208 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 toMetadataFormat(Date defaultValue) { + return new SimpleDateFormat("yyyy-MM-dd").format(defaultValue); + } + + public static Date fromMetadataFormat(String toString) throws ParseException { + return new SimpleDateFormat("yyyy-MM-dd").parse(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 519aaea7..98c3a93b 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,8 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map 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"); @@ -102,7 +142,6 @@ public void testDeleteField() throws Exception { @Test public void testUpdateDatasource() throws Exception { - // TODO - expected an array SetMetadataField setField = createSetField("testUpdateDatasource"); ApiResponse fieldResult = addFieldToAccount(setField); MetadataDataSource.Entry newEntry = new MetadataDataSource.Entry("id1", "new1"); @@ -130,12 +169,6 @@ public void testUploadWithMetadata() throws Exception { assertEquals("123456", ((Map) result.get("metadata")).get(fieldId)); } - private ApiResponse addFieldToAccount(AbstractMetadataField field) throws Exception { - ApiResponse apiResponse = api.addMetadataField(field); - metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); - return apiResponse; - } - @Test public void testExplicitWithMetadata() throws Exception { Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, METADATA_UPLOADER_TAG))); @@ -169,7 +202,7 @@ public void testUploaderUpdateMetadata() throws Exception { 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()); + assertEquals("sample", ((List) result.get("public_ids")).get(0).toString()); } // Metadata test helpers @@ -199,4 +232,10 @@ private StringMetadataField newFieldInstance(String labelPrefix) throws Exceptio field.setDefaultValue("val_test"); return field; } + + private ApiResponse addFieldToAccount(AbstractMetadataField field) throws Exception { + ApiResponse apiResponse = api.addMetadataField(field); + metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); + return apiResponse; + } } From 342c92f8903ad6512d802045487fa22b26e290c6 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Sun, 7 Jul 2019 21:41:29 +0300 Subject: [PATCH 4/8] Add http client 4.2 and 4.3 to the StructuredMetadata tests. --- .../test/java/com/cloudinary/test/StructuredMetadataTest.java | 4 ++++ .../test/java/com/cloudinary/test/StructuredMetadataTest.java | 4 ++++ 2 files changed, 8 insertions(+) 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 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 From fdf8c8f068509be94958b730467ea25c10b252fe Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Mon, 8 Jul 2019 14:42:24 +0300 Subject: [PATCH 5/8] Rename utils methods. --- .../java/com/cloudinary/metadata/DateMetadataField.java | 4 ++-- .../java/com/cloudinary/metadata/MetadataValidation.java | 4 ++-- .../src/main/java/com/cloudinary/utils/ObjectUtils.java | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java index 595f7ab0..6fb624aa 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java @@ -20,7 +20,7 @@ public DateMetadataField() { */ @Override public void setDefaultValue(Date defaultValue) { - put(DEFAULT_VALUE, ObjectUtils.toMetadataFormat(defaultValue)); + put(DEFAULT_VALUE, ObjectUtils.toISO8601DateOnly(defaultValue)); } /** @@ -35,6 +35,6 @@ public Date getDefaultValue() throws ParseException { return null; } - return ObjectUtils.fromMetadataFormat(value.toString()); + return ObjectUtils.fromISO8601DateOnly(value.toString()); } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java index 8b6d5a79..b294f516 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java @@ -121,7 +121,7 @@ public DateGreaterThan(Date value, Boolean equals) { @Override protected void putValue(Date value) { - put(VALUE, ObjectUtils.toMetadataFormat(value)); + put(VALUE, ObjectUtils.toISO8601DateOnly(value)); } } @@ -170,7 +170,7 @@ public DateLessThan(Date value, Boolean equals) { @Override protected void putValue(Date value) { - put(VALUE, ObjectUtils.toMetadataFormat(value)); + put(VALUE, ObjectUtils.toISO8601DateOnly(value)); } } } 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 51ed5208..75bbc87c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -215,11 +215,11 @@ public static Long asLong(Object value, Long defaultValue) { } } - public static String toMetadataFormat(Date defaultValue) { - return new SimpleDateFormat("yyyy-MM-dd").format(defaultValue); + public static String toISO8601DateOnly(Date date) { + return new SimpleDateFormat("yyyy-MM-dd").format(date); } - public static Date fromMetadataFormat(String toString) throws ParseException { - return new SimpleDateFormat("yyyy-MM-dd").parse(toString); + public static Date fromISO8601DateOnly(String string) throws ParseException { + return new SimpleDateFormat("yyyy-MM-dd").parse(string); } } From 41671722d2fe5f8f31b6c18300dbacc0af92832b Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 10 Jul 2019 14:23:46 +0300 Subject: [PATCH 6/8] Simplify Metadata Field type hierarchy. --- .../src/main/java/com/cloudinary/Api.java | 25 +++++++++-------- .../metadata/AbstractMetadataValuesField.java | 27 ------------------- .../metadata/DateMetadataField.java | 2 +- .../metadata/EnumMetadataField.java | 2 +- .../cloudinary/metadata/IntMetadataField.java | 2 +- ...tMetadataField.java => MetadataField.java} | 24 +++++++++++++++-- .../metadata/MetadataValidation.java | 6 ++--- .../cloudinary/metadata/SetMetadataField.java | 2 +- .../metadata/StringMetadataField.java | 2 +- .../strategies/AbstractApiStrategy.java | 5 ++-- .../test/AbstractStructuredMetadataTest.java | 4 +-- 11 files changed, 45 insertions(+), 56 deletions(-) delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java rename cloudinary-core/src/main/java/com/cloudinary/metadata/{AbstractMetadataField.java => MetadataField.java} (82%) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 32c0d9df..c29fdad5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -5,9 +5,8 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.AuthorizationRequired; import com.cloudinary.api.exceptions.*; -import com.cloudinary.metadata.AbstractMetadataField; +import com.cloudinary.metadata.MetadataField; import com.cloudinary.metadata.MetadataDataSource; -import com.cloudinary.metadata.StringMetadataField; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -566,14 +565,14 @@ private ApiResponse updateResourcesAccessMode(String accessMode, String byKey, O * @return A map representing the newlay added field. * @throws Exception */ - public ApiResponse addMetadataField(AbstractMetadataField 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. + * @return A map containing the list of field definitions maps. * @throws Exception */ public ApiResponse listMetadataFields() throws Exception { @@ -582,23 +581,23 @@ public ApiResponse listMetadataFields() throws Exception { /** * Get a metadata field definition by id - * @param externalId The if of the field to retrive + * @param fieldExternalId The id of the field to retrieve * @return The fields definitions. * @throws Exception */ - public ApiResponse metadataFieldByFieldId(String externalId) throws Exception { - return callApi(HttpMethod.GET, Arrays.asList("metadata_fields", externalId), Collections.emptyMap(), Collections.emptyMap()); + 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 externalId The id of the field to update + * @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 externalId, AbstractMetadataField field) throws Exception { - List uri = Arrays.asList("metadata_fields", externalId); + 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")); } @@ -629,12 +628,12 @@ public ApiResponse deleteDatasourceEntries(String fieldExternalId, List /** * Delete a field definition. - * @param externalId The id of the field to delete + * @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 externalId) throws Exception { - List uri = Arrays.asList("metadata_fields", externalId); + 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/metadata/AbstractMetadataValuesField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java deleted file mode 100644 index b2490209..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataValuesField.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cloudinary.metadata; - -/** - * This class is the abstract base for field classes based on a list of valid values (at the moment set+enum). - * @param The type of the value. - */ -public abstract class AbstractMetadataValuesField extends AbstractMetadataField { - AbstractMetadataValuesField(MetadataFieldType type) { - super(type); - } - - /** - * 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); - } -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java index 6fb624aa..a4df9091 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java @@ -8,7 +8,7 @@ /** * Represents a metadata field with type 'date' */ -public class DateMetadataField extends AbstractMetadataField { +public class DateMetadataField extends MetadataField { public DateMetadataField() { super(MetadataFieldType.DATE); diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java index 4102ed8e..79f501c3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java @@ -3,7 +3,7 @@ /** * Represents a metadata field with 'Enum' type. */ -public class EnumMetadataField extends AbstractMetadataValuesField{ +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 index d2b07dae..23510210 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java @@ -3,7 +3,7 @@ /** * Represents a metadata field with 'Int' type. */ -public class IntMetadataField extends AbstractMetadataField { +public class IntMetadataField extends MetadataField { public IntMetadataField() { super(MetadataFieldType.INTEGER); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java similarity index 82% rename from cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java rename to cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java index ff0138b4..bae2b6c7 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/AbstractMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java @@ -8,7 +8,7 @@ * Represents a single metadata field. Use one of the derived classes in the metadata API calls. * @param */ -public class AbstractMetadataField extends JSONObject { +public class MetadataField extends JSONObject { public static final String DEFAULT_VALUE = "default_value"; public static final String EXTERNAL_ID = "external_id"; @@ -17,10 +17,14 @@ public class AbstractMetadataField extends JSONObject { public static final String TYPE = "type"; public static final String VALIDATION = "validation"; - AbstractMetadataField(MetadataFieldType type) { + 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. @@ -110,4 +114,20 @@ public MetadataValidation getValidation() { 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/MetadataValidation.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java index b294f516..f38b732e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataValidation.java @@ -58,12 +58,12 @@ public StringLength(Integer min, Integer max) { * Base class for all comparison (greater than/less than) validation rules. * @param */ - abstract static class ComparisonRule extends MetadataValidation { - ComparisonRule(String type, T value) { + public abstract static class ComparisonRule extends MetadataValidation { + public ComparisonRule(String type, T value) { this(type, value, null); } - ComparisonRule(String type, T value, Boolean equals) { + public ComparisonRule(String type, T value, Boolean equals) { put(TYPE, type); putValue(value); if (equals != null) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java index f6d8b56e..48d54823 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java @@ -5,7 +5,7 @@ /** * Represents a metadata field with 'Set' type. */ -public class SetMetadataField extends AbstractMetadataValuesField> { +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 index df68ef21..e7e03405 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java @@ -3,7 +3,7 @@ /** * Represents a metadata field with 'String' type. */ -public class StringMetadataField extends AbstractMetadataField { +public class StringMetadataField extends MetadataField { public StringMetadataField() { super(MetadataFieldType.STRING); } 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 a66f06b9..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,10 @@ package com.cloudinary.strategies; -import java.util.Map; - import com.cloudinary.Api; import com.cloudinary.Api.HttpMethod; import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; + +import java.util.Map; public abstract class AbstractApiStrategy { protected Api api; 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 33f93114..0bb70cbe 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,12 +5,10 @@ import com.cloudinary.api.ApiResponse; import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.metadata.*; -import com.cloudinary.utils.ObjectUtils; import org.junit.*; import org.junit.rules.TestName; import java.io.IOException; -import java.text.ParseException; import java.util.*; import static com.cloudinary.utils.ObjectUtils.asMap; @@ -233,7 +231,7 @@ private StringMetadataField newFieldInstance(String labelPrefix) throws Exceptio return field; } - private ApiResponse addFieldToAccount(AbstractMetadataField field) throws Exception { + private ApiResponse addFieldToAccount(MetadataField field) throws Exception { ApiResponse apiResponse = api.addMetadataField(field); metadataFieldExternalIds.add(apiResponse.get("external_id").toString()); return apiResponse; From 542c1a99da6cde1b0c1e47218c0580db961bd122 Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Wed, 7 Aug 2019 13:44:39 +0300 Subject: [PATCH 7/8] Rename tests, add validation to explicit test. --- .../test/AbstractStructuredMetadataTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 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 0bb70cbe..da6e6eda 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 @@ -66,7 +66,7 @@ public void testCreateMetadata() throws Exception { } @Test - public void testDateFieldBehaviour() throws Exception { + public void testDateFieldDefaultValueValidation() throws Exception { // now minus 3 days hours. Date max = new Date(); Date min = new Date(max.getTime() - 72 * 60 * 60 * 1000); @@ -178,6 +178,17 @@ public void testExplicitWithMetadata() throws Exception { 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 From be62150db32709e8938b0239d9e5c4f3822baeba Mon Sep 17 00:00:00 2001 From: Nitzan Jaitman Date: Thu, 8 Aug 2019 10:32:40 +0300 Subject: [PATCH 8/8] Improve metadata listing test. --- .../com/cloudinary/test/AbstractStructuredMetadataTest.java | 2 ++ 1 file changed, 2 insertions(+) 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 da6e6eda..d370ce5c 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 @@ -109,6 +109,8 @@ public void testListFields() throws Exception { ApiResponse result = cloudinary.api().listMetadataFields(); assertNotNull(result); + assertNotNull(result.get("metadata_fields")); + assertTrue(((List)result.get("metadata_fields")).size() > 0); } @Test