Skip to content

Commit

Permalink
Support for Custom Metaproperties (#126)
Browse files Browse the repository at this point in the history
* fix testcases for windows, remove deprecated initMocks in favour of openMocks, add gitignore to prevent target/ files to be checked into git

* replace other initMocks with openMocks calls

* implement custom JsonDeserializer to support "property_" prefixed custom metadata

---------

Co-authored-by: Jonas Dickel <[email protected]>
  • Loading branch information
jdickel and Jonas Dickel authored Aug 6, 2024
1 parent 81cfc4b commit 151c4cb
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 23 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
12 changes: 8 additions & 4 deletions src/main/java/com/bynder/sdk/api/ApiFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@
*/
package com.bynder.sdk.api;

import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import com.bynder.sdk.configuration.Configuration;
import com.bynder.sdk.configuration.HttpConnectionSettings;
import com.bynder.sdk.exception.BynderRuntimeException;
import com.bynder.sdk.model.Media;
import com.bynder.sdk.service.BynderClient;
import com.bynder.sdk.util.BooleanTypeAdapter;
import com.bynder.sdk.util.MediaTypeAdapter;
import com.bynder.sdk.util.StringConverterFactory;
import com.bynder.sdk.util.Utils;
import com.google.gson.GsonBuilder;

import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import okhttp3.Request;
Expand All @@ -22,10 +29,6 @@
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
* Factory to create API clients.
*/
Expand All @@ -51,6 +54,7 @@ public static BynderApi createBynderClient(final Configuration configuration) {
.addConverterFactory(GsonConverterFactory.create(
new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanTypeAdapter())
.registerTypeAdapter(Media.class, new MediaTypeAdapter())
.create())
)
.client(createOkHttpClient(configuration))
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/bynder/sdk/model/Media.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ public class Media {
* versions equal to true.
*/
private List<MediaItem> mediaItems;
/**
* Custom properties not having an PropertyOption. Key is the property name and
* value the value(s) of that property.
*/
private Map<String, List<String>> customMetaproperties;

public String getId() {
return id;
Expand Down Expand Up @@ -216,4 +221,8 @@ public Map<String, Double> getFocusPoint() {
public List<MediaItem> getMediaItems() {
return mediaItems;
}

public Map<String, List<String>> getCustomMetaproperties() {
return customMetaproperties;
}
}
66 changes: 66 additions & 0 deletions src/main/java/com/bynder/sdk/util/MediaTypeAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.bynder.sdk.util;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.bynder.sdk.model.Media;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class MediaTypeAdapter implements JsonDeserializer<Media> {

private static final String PROPERTY_PREFIX = "property_";
private static final String CUSTOM_METAPROPERTY_FIELDNAME = "customMetaproperties";

private final Gson gson;

public MediaTypeAdapter() {
this.gson = new GsonBuilder().registerTypeAdapter(Boolean.class, new BooleanTypeAdapter()).create();
}

@Override
public Media deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
Media media = gson.fromJson(jsonObject, Media.class);

Map<String, List<String>> metaproperties = new LinkedHashMap<>();

for (Map.Entry<String, JsonElement> elementJson : jsonObject.entrySet()) {
if (elementJson.getKey().startsWith(PROPERTY_PREFIX)) {
String propertyName = elementJson.getKey().substring(PROPERTY_PREFIX.length());
List<String> values = metaproperties.getOrDefault(metaproperties, new ArrayList<>());
if (elementJson.getValue().isJsonArray()) {
for (JsonElement element : elementJson.getValue().getAsJsonArray()) {
values.add(element.getAsString());
}
} else {
values.add(elementJson.getValue().getAsString());
}
metaproperties.put(propertyName, values);
}
}
setMetaproperties(media, metaproperties);
return media;
}

private void setMetaproperties(Media media, Map<String, List<String>> metaproperties) {
try {
Field metapropertiesField = media.getClass().getDeclaredField(CUSTOM_METAPROPERTY_FIELDNAME);
metapropertiesField.setAccessible(true);
metapropertiesField.set(media, metaproperties);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
// does not occur unless Media class is changed
}
}

}
2 changes: 1 addition & 1 deletion src/test/java/com/bynder/sdk/api/ApiFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class ApiFactoryTest {

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
Mockito.when(configuration.getBaseUrl()).thenReturn(new URL(BASE_URL));
Mockito.when(configuration.getHttpConnectionSettings())
.thenReturn(new HttpConnectionSettings());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ConfigurationTest {

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class HttpConnectionSettingsTest {

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/bynder/sdk/service/BynderClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class BynderClientTest {

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
Mockito.when(configuration.getBaseUrl()).thenReturn(new URL(BASE_URL));
Mockito.when(configuration.getHttpConnectionSettings())
.thenReturn(new HttpConnectionSettings());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class AssetServiceImplTest {

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
assetService = AssetService.Builder.create(bynderApi, queryDecoder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class CollectionServiceImplTest {

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
collectionService = CollectionService.Builder.create(bynderApi, queryDecoder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class OAuthServiceImplTest {

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.openMocks(this);
when(oauthClient.getAccessToken(anyMap())).thenReturn(Observable.just(Response.success(token)));
when(configuration.getBaseUrl()).thenReturn(new URL(EXPECTED_BASE_URL));
when(configuration.getOAuthSettings()).thenReturn(oAuthSettings);
Expand Down
75 changes: 75 additions & 0 deletions src/test/java/com/bynder/sdk/util/MediaTypeAdapterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2017 Bynder B.V. All rights reserved.
*
* Licensed under the MIT License. See LICENSE file in the project root for full license
* information.
*
* JUnit framework component copyright (c) 2002-2017 JUnit. All Rights Reserved. Licensed under
* Eclipse Public License - v 1.0. You may obtain a copy of the License at
* https://www.eclipse.org/legal/epl-v10.html.
*/
package com.bynder.sdk.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;

import org.junit.Test;

import com.bynder.sdk.model.Media;
import com.google.gson.JsonParser;

/**
* Tests the {@link BooleanTypeAdapter} class methods.
*/
public class MediaTypeAdapterTest {


/**
* Given JSON
*
* {
* "id": "FF5DB884-5665-4127-88D0116D8EB379FE",
* "isPublic": 0,
* "property_Articlenumber": "16773",
* "property_Language": [
* "Neutral"
* ]
* }
*
*/
private final String givenApiResponse = "{ \"id\": \"FF5DB884-5665-4127-88D0116D8EB379FE\", \"isPublic\": 0, \"property_Articlenumber\": \"16773\", \"property_Language\": [ \"Neutral\" ]}";


/**
* Tests that
* {@link MediaTypeAdapter#deserialize(com.google.gson.JsonElement, java.lang.reflect.Type, com.google.gson.JsonDeserializationContext)}
* correctly converts all "property_" prefixed json fields into a custom Map<String,List<String>> when deserializing the Json response returned by the
* API.
*/
@Test
public void deserializeWithMediaTypeAdapter() {
MediaTypeAdapter mediaTypeAdapter = new MediaTypeAdapter();

Media actualMedia = mediaTypeAdapter.deserialize(JsonParser.parseString(givenApiResponse), null, null);

// common default field like ID
assertEquals("FF5DB884-5665-4127-88D0116D8EB379FE", actualMedia.getId());

// boolean using BooleanTypeAdapter inside MediaTypeAdapter
assertEquals(Boolean.FALSE, actualMedia.isPublic());

// new custom "property_" which is returned as string from API#

assertNotNull(actualMedia.getCustomMetaproperties());
assertTrue(actualMedia.getCustomMetaproperties().containsKey("Articlenumber"));
assertEquals(Arrays.asList("16773"), actualMedia.getCustomMetaproperties().get("Articlenumber"));

// new custom "property_" which is returned as array from API
assertNotNull(actualMedia.getCustomMetaproperties());
assertTrue(actualMedia.getCustomMetaproperties().containsKey("Language"));
assertEquals(Arrays.asList("Neutral"), actualMedia.getCustomMetaproperties().get("Language"));
}
}
28 changes: 16 additions & 12 deletions src/test/java/com/bynder/sdk/util/RXUtilsTest.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
package com.bynder.sdk.util;

import com.bynder.sdk.exception.HttpResponseException;
import io.reactivex.Observable;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import org.junit.Before;
import org.junit.Test;
import retrofit2.Response;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;

import com.bynder.sdk.exception.HttpResponseException;

import io.reactivex.Observable;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import retrofit2.Response;

public class RXUtilsTest {

Expand Down Expand Up @@ -93,10 +97,10 @@ public void readFileChunks() throws IOException {
}

@Test
public void readFile() throws IOException {
String path = ClassLoader.getSystemResource("config.properties").getPath();
public void readFile() throws IOException, URISyntaxException {
URI path = ClassLoader.getSystemResource("config.properties").toURI();
byte[] expected = Files.readAllBytes(Paths.get(path));
byte[] actual = RXUtils.readFile(path).blockingGet();
byte[] actual = RXUtils.readFile(Paths.get(path).toString()).blockingGet();
assertArrayEquals(expected, actual);
}

Expand Down

0 comments on commit 151c4cb

Please sign in to comment.