diff --git a/README.md b/README.md index 60962d6..e795367 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ Table actorTable = base.table("Actors", Actor.class); Actor newActor = new Actor(); newActor.setName("Neuer Actor"); Actor test = actorTable.create(newActor); + ``` Detailed example see [TableDestroyTest.java](https://github.com/Sybit-Education/airtable.java/blob/develop/src/test/java/com/sybit/airtable/TableCreateRecordTest.java) @@ -370,6 +371,7 @@ We use [Gradle](https://gradle.org) to compile and package project: + build jar: `./gradlew jar` (The built JARs will be placed under `build/libs`.) ## Testing + There are JUnit tests and integration tests to verify the API. The integration tests are based on the Airtable template [Movies](https://airtable.com/templates/groups-clubs-and-hobbies/exprTnrH3YV8Vv9BI/favorite-movies) which could be created in your account. For testing, the JSON-responses are mocked by [WireMock](http://wiremock.org/). diff --git a/build.gradle b/build.gradle index 0a25ca1..1607991 100644 --- a/build.gradle +++ b/build.gradle @@ -143,7 +143,6 @@ task integrationTest(type: Test) { } } - publishing { publications { diff --git a/src/test/java/com/sybit/airtable/AirtableTest.java b/src/itest/java/com/sybit/airtable/AirtableTest.java similarity index 100% rename from src/test/java/com/sybit/airtable/AirtableTest.java rename to src/itest/java/com/sybit/airtable/AirtableTest.java diff --git a/src/itest/java/com/sybit/airtable/TableSelectJacksonOMTest.java b/src/itest/java/com/sybit/airtable/TableSelectJacksonOMTest.java new file mode 100644 index 0000000..709e6ef --- /dev/null +++ b/src/itest/java/com/sybit/airtable/TableSelectJacksonOMTest.java @@ -0,0 +1,138 @@ +package com.sybit.airtable; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.mashape.unirest.http.ObjectMapper; +import com.sybit.airtable.exception.AirtableException; +import com.sybit.airtable.movies.ActorSerializedNames; +import com.sybit.airtable.movies.Movie; +import com.sybit.airtable.mock.WireMockBaseTest; +import org.apache.http.client.HttpResponseException; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Created by kobisuissa on 18/04/17. + */ +public class TableSelectJacksonOMTest extends WireMockBaseTest { + + @Before + public void setup() throws AirtableException { + airtable.configure(new ObjectMapper() { + + final com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); + + @Override + public T readValue(final String value, final Class valueType) { + try { + return objectMapper.readValue(value, valueType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public String writeValue(final Object value) { + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + }); + airtable.setEndpointUrl("http://localhost:8080/v0"); + + //set 404 as default + stubFor(any(anyUrl()) + .atPriority(10) + .willReturn(aResponse() + .withStatus(404) + .withBody("{\"error\":{\"type\":\"NOT_FOUND\",\"message\":\"Not found\"}}"))); + + } + + @Test + public void testSelectTable() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + + List retval = base.table("Movies", Movie.class).select(); + assertNotNull(retval); + assertEquals(10, retval.size()); + Movie mov = retval.get(0); + assertEquals("Sister Act", mov.getName()); + } + + @Test + public void testSelectTableMaxRecords() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + + List retval = base.table("Movies", Movie.class).select(2); + assertNotNull(retval); + assertEquals(2, retval.size()); + Movie mov = retval.get(0); + assertEquals("Sister Act", mov.getName()); + } + + @Test + public void testSelectTableSorted() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + Table table = base.table("Movies", Movie.class); + + List retval = table.select(new Sort("Name", Sort.Direction.asc)); + assertNotNull(retval); + assertEquals(10, retval.size()); + Movie mov = retval.get(0); + assertEquals("Billy Madison", mov.getName()); + + retval = table.select(new Sort("Name", Sort.Direction.desc)); + assertNotNull(retval); + assertEquals(10, retval.size()); + mov = retval.get(0); + assertEquals("You've got Mail", mov.getName()); + + } + + @Test + public void testSelectTableView() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + + List retval = base.table("Movies", Movie.class).select("Main View"); + assertNotNull(retval); + assertEquals(10, retval.size()); + Movie mov = retval.get(0); + assertEquals("The Godfather", mov.getName()); + } + + @Test(expected = AirtableException.class) + public void testSelectNonExistingTable() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + + List retval = base.table("NotExists", Movie.class).select(); + assertNotNull(retval); + } + + @Test + public void testSelectWithSerializedNames() throws AirtableException, HttpResponseException { + + Base base = airtable.base("appe9941ff07fffcc"); + + List retval = base.table("SerializedNames", ActorSerializedNames.class).select(); + assertNotNull(retval); + assertEquals("Marlon Brando", retval.get(0).getName()); + } + +} diff --git a/src/main/java/com/sybit/airtable/Airtable.java b/src/main/java/com/sybit/airtable/Airtable.java index 46682d6..be13a52 100644 --- a/src/main/java/com/sybit/airtable/Airtable.java +++ b/src/main/java/com/sybit/airtable/Airtable.java @@ -7,6 +7,7 @@ package com.sybit.airtable; +import com.mashape.unirest.http.ObjectMapper; import com.mashape.unirest.http.Unirest; import com.sybit.airtable.converter.ListConverter; import com.sybit.airtable.converter.MapConverter; @@ -54,11 +55,24 @@ public class Airtable { * Configure, AIRTABLE_API_KEY passed by Java property, enviroment variable * or within credentials.properties. * - * @return configured Airtable object. + * @return An Airtable instance configured with GsonObjectMapper * @throws com.sybit.airtable.exception.AirtableException Missing API-Key */ @SuppressWarnings("UnusedReturnValue") public Airtable configure() throws AirtableException { + return this.configure(new GsonObjectMapper()); + } + + /** + * Configure, AIRTABLE_API_KEY passed by Java property, enviroment variable + * or within credentials.properties. + * + * @param objectMapper A custom ObjectMapper implementation + * @return An Airtable instance configured with supplied ObjectMapper + * @throws com.sybit.airtable.exception.AirtableException Missing API-Key + */ + @SuppressWarnings("UnusedReturnValue") + public Airtable configure(ObjectMapper objectMapper) throws AirtableException { LOG.info( "System-Property: Using Java property '-D" + AIRTABLE_API_KEY + "' to get apikey."); String airtableApi = System.getProperty(AIRTABLE_API_KEY); @@ -71,7 +85,7 @@ public Airtable configure() throws AirtableException { airtableApi = getCredentialProperty(AIRTABLE_API_KEY); } - return this.configure(airtableApi); + return this.configure(airtableApi, objectMapper); } @@ -80,12 +94,25 @@ public Airtable configure() throws AirtableException { * Configure Airtable. * * @param apiKey API-Key of Airtable. - * @return + * @return An Airtable instance configured with GsonObjectMapper * @throws com.sybit.airtable.exception.AirtableException Missing API-Key */ @SuppressWarnings("WeakerAccess") public Airtable configure(String apiKey) throws AirtableException { - return configure(new Configuration(apiKey, Configuration.ENDPOINT_URL)); + return configure(apiKey, new GsonObjectMapper()); + } + + /** + * Configure Airtable. + * + * @param apiKey API-Key of Airtable. + * @param objectMapper A custom ObjectMapper implementation + * @return + * @throws com.sybit.airtable.exception.AirtableException Missing API-Key + */ + @SuppressWarnings("WeakerAccess") + public Airtable configure(String apiKey, ObjectMapper objectMapper) throws AirtableException { + return configure(new Configuration(apiKey, Configuration.ENDPOINT_URL), objectMapper); } /** @@ -96,6 +123,19 @@ public Airtable configure(String apiKey) throws AirtableException { */ @SuppressWarnings("WeakerAccess") public Airtable configure(Configuration config) throws AirtableException { + return configure(config, new GsonObjectMapper()); + } + + + /** + * + * @param config + * @param objectMapper A custom ObjectMapper implementation + * @return + * @throws com.sybit.airtable.exception.AirtableException Missing API-Key or Endpoint + */ + @SuppressWarnings("WeakerAccess") + public Airtable configure(Configuration config, ObjectMapper objectMapper) throws AirtableException { if(config.getApiKey() == null) { throw new AirtableException("Missing Airtable API-Key"); } @@ -113,22 +153,21 @@ public Airtable configure(Configuration config) throws AirtableException { setProxy(config.getEndpointUrl()); // Only one time - Unirest.setObjectMapper(new GsonObjectMapper()); + Unirest.setObjectMapper(objectMapper); - // Add specific Converter for Date DateTimeConverter dtConverter = new DateConverter(); ListConverter lConverter = new ListConverter(); MapConverter thConverter = new MapConverter(); - + lConverter.setListClass(Attachment.class); thConverter.setMapClass(Thumbnail.class); dtConverter.setPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - + ConvertUtils.register(dtConverter, Date.class); ConvertUtils.register(lConverter, List.class); ConvertUtils.register(thConverter, Map.class); - + return this; } @@ -142,8 +181,8 @@ public Airtable configure(Configuration config) throws AirtableException { private void setProxy(String endpointUrl) { final String httpProxy = System.getenv("http_proxy"); if(httpProxy != null - && (endpointUrl.contains("127.0.0.1") - || endpointUrl.contains("localhost"))) { + && (endpointUrl.contains("127.0.0.1") + || endpointUrl.contains("localhost"))) { LOG.info("Use Proxy: ignored for 'localhost' ann '127.0.0.1'"); Unirest.setProxy(null); } else if(httpProxy != null) { diff --git a/src/main/java/com/sybit/airtable/converter/MapConverter.java b/src/main/java/com/sybit/airtable/converter/MapConverter.java index 968f787..e47df32 100644 --- a/src/main/java/com/sybit/airtable/converter/MapConverter.java +++ b/src/main/java/com/sybit/airtable/converter/MapConverter.java @@ -131,5 +131,4 @@ public void setMapClass(Class aClass) { public Class getMapClass(){ return this.mapClass; } - } diff --git a/src/test/java/com/sybit/airtable/movies/Actor.java b/src/test/java/com/sybit/airtable/movies/Actor.java index 6a1e8a4..7142299 100644 --- a/src/test/java/com/sybit/airtable/movies/Actor.java +++ b/src/test/java/com/sybit/airtable/movies/Actor.java @@ -7,6 +7,7 @@ package com.sybit.airtable.movies; + import com.sybit.airtable.movies.*; import com.google.gson.annotations.SerializedName; import com.sybit.airtable.vo.Attachment; diff --git a/src/test/java/com/sybit/airtable/movies/Movie.java b/src/test/java/com/sybit/airtable/movies/Movie.java index f0a638f..a4bb657 100644 --- a/src/test/java/com/sybit/airtable/movies/Movie.java +++ b/src/test/java/com/sybit/airtable/movies/Movie.java @@ -6,6 +6,7 @@ */ package com.sybit.airtable.movies; + import com.sybit.airtable.movies.*; import com.google.gson.annotations.SerializedName; import com.sybit.airtable.vo.Attachment;