From 4b3bf0d14ba3cc8fc0aa021aafe36cd80c14c588 Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 15:34:34 +0200 Subject: [PATCH 1/6] Add support for taxonomies in metadata --- .../java/com/contentful/java/cda/CDAEntry.java | 11 +++++++++++ .../java/com/contentful/java/cda/CDAMetadata.java | 13 ++++++++++--- .../contentful/java/cda/CDATaxonomyConcept.java | 11 +++++++++++ .../java/com/contentful/java/cda/CDAType.java | 3 ++- .../java/com/contentful/java/cda/Constants.java | 2 ++ src/main/java/com/contentful/java/cda/Util.java | 15 +++++++++++---- .../java/com/contentful/java/cda/EntryTest.java | 15 +++++++++++++++ src/test/resources/demo/entries.json | 13 +++++++++++++ 8 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/contentful/java/cda/CDATaxonomyConcept.java diff --git a/src/main/java/com/contentful/java/cda/CDAEntry.java b/src/main/java/com/contentful/java/cda/CDAEntry.java index a0138429..3dac10da 100644 --- a/src/main/java/com/contentful/java/cda/CDAEntry.java +++ b/src/main/java/com/contentful/java/cda/CDAEntry.java @@ -1,11 +1,14 @@ package com.contentful.java.cda; +import com.google.gson.annotations.SerializedName; + /** * The class represents a basic entry in the space. */ public class CDAEntry extends LocalizedResource { private static final long serialVersionUID = 5902790363045498307L; protected CDAContentType contentType; + @SerializedName("metadata") private CDAMetadata metadata; /** @@ -30,6 +33,14 @@ protected void setContentType(CDAContentType contentType) { this.contentType = contentType; } + public CDAMetadata getMetadata() { + return metadata; + } + + public void setMetadata(CDAMetadata metadata) { + this.metadata = metadata; + } + /** * Create a human readable string of this object. * @return a string, containing the id of this content type. diff --git a/src/main/java/com/contentful/java/cda/CDAMetadata.java b/src/main/java/com/contentful/java/cda/CDAMetadata.java index c958a8bc..2196a510 100644 --- a/src/main/java/com/contentful/java/cda/CDAMetadata.java +++ b/src/main/java/com/contentful/java/cda/CDAMetadata.java @@ -2,16 +2,23 @@ import java.io.Serializable; import java.util.List; + public class CDAMetadata implements Serializable { private static final long serialVersionUID = -2852530837647649035L; + private List tags; + private List concepts; + public List getTags() { return tags; } - public void setTags(List tags) { - this.tags = tags; + public List getConcepts() { + return concepts; + } + + public void setConcepts(List concepts) { + this.concepts = concepts; } - List tags; @Override public String toString() { diff --git a/src/main/java/com/contentful/java/cda/CDATaxonomyConcept.java b/src/main/java/com/contentful/java/cda/CDATaxonomyConcept.java new file mode 100644 index 00000000..4df6dacb --- /dev/null +++ b/src/main/java/com/contentful/java/cda/CDATaxonomyConcept.java @@ -0,0 +1,11 @@ +package com.contentful.java.cda; + + +public class CDATaxonomyConcept extends CDAResource { + @Override + public String toString() { + return "CDATaxonomyConcept{" + + "attrs=" + attrs + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/contentful/java/cda/CDAType.java b/src/main/java/com/contentful/java/cda/CDAType.java index 529a1e5f..e02abbea 100644 --- a/src/main/java/com/contentful/java/cda/CDAType.java +++ b/src/main/java/com/contentful/java/cda/CDAType.java @@ -10,5 +10,6 @@ public enum CDAType { ENTRY, LOCALE, SPACE, - TAG + TAG, + TAXONOMYCONCEPT, } diff --git a/src/main/java/com/contentful/java/cda/Constants.java b/src/main/java/com/contentful/java/cda/Constants.java index edeee0b0..64a007c9 100644 --- a/src/main/java/com/contentful/java/cda/Constants.java +++ b/src/main/java/com/contentful/java/cda/Constants.java @@ -24,5 +24,7 @@ class Constants { static final String PATH_TAGS = "tags"; + static final String PATH_TAXONOMY_CONCEPTS = "taxonomy/concepts"; + static final String DEFAULT_ENVIRONMENT = "master"; } diff --git a/src/main/java/com/contentful/java/cda/Util.java b/src/main/java/com/contentful/java/cda/Util.java index c125f248..015d78b4 100644 --- a/src/main/java/com/contentful/java/cda/Util.java +++ b/src/main/java/com/contentful/java/cda/Util.java @@ -12,6 +12,7 @@ import static com.contentful.java.cda.CDAType.SPACE; import static com.contentful.java.cda.CDAType.ENTRY; import static com.contentful.java.cda.CDAType.CONTENTTYPE; +import static com.contentful.java.cda.CDAType.TAXONOMYCONCEPT; final class Util { private Util() { @@ -46,18 +47,20 @@ static String resourcePath(Class clazz) { return Constants.PATH_LOCALES; } else if (CDATag.class.equals(clazz)) { return Constants.PATH_TAGS; + } else if (CDATaxonomyConcept.class.equals(clazz)) { + return Constants.PATH_TAXONOMY_CONCEPTS; } throw new IllegalArgumentException("Invalid type specified: " + clazz.getName()); } static Class classForType(CDAType type) { - if (CDAType.ASSET.equals(type)) { + if (ASSET.equals(type)) { return CDAAsset.class; - } else if (CDAType.CONTENTTYPE.equals(type)) { + } else if (CONTENTTYPE.equals(type)) { return CDAContentType.class; - } else if (CDAType.ENTRY.equals(type)) { + } else if (ENTRY.equals(type)) { return CDAEntry.class; - } else if (CDAType.SPACE.equals(type)) { + } else if (SPACE.equals(type)) { return CDASpace.class; } else if (LOCALE.equals(type)) { return CDALocale.class; @@ -65,6 +68,8 @@ static Class classForType(CDAType type) { return DeletedResource.class; } else if (TAG.equals(type)) { return CDATag.class; + } else if (TAXONOMYCONCEPT.equals(type)) { + return CDATaxonomyConcept.class; } throw new IllegalArgumentException("Invalid type provided: " + type); } @@ -82,6 +87,8 @@ static CDAType typeForClass(Class clazz) { return LOCALE; } else if (CDATag.class.equals(clazz)) { return TAG; + } else if (CDATaxonomyConcept.class.equals(clazz)) { + return TAXONOMYCONCEPT; } throw new IllegalArgumentException("Invalid class provided: " + clazz.getName()); } diff --git a/src/test/java/com/contentful/java/cda/EntryTest.java b/src/test/java/com/contentful/java/cda/EntryTest.java index e857d084..0d1831b6 100644 --- a/src/test/java/com/contentful/java/cda/EntryTest.java +++ b/src/test/java/com/contentful/java/cda/EntryTest.java @@ -12,6 +12,10 @@ import java.util.concurrent.TimeUnit; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.ArrayList; public class EntryTest extends BaseTest { @Test(expected = CDAResourceNotFoundException.class) @@ -97,6 +101,17 @@ public void entryContentType() { assertThat(entry.contentType()).isNotNull(); } + @Test + @Enqueue("demo/entries.json") + public void entryTaxonomy() { + CDAEntry entry = client.fetch(CDAEntry.class).one("ge1xHyH3QOWucKWCCAgIG"); + assertThat(entry.contentType()).isNotNull(); + assertNotNull(entry.getMetadata()); + assertNotNull(entry.getMetadata().getConcepts()); + assertEquals(1, entry.getMetadata().getConcepts().size()); + } + + @Test @Enqueue("demo/entries_nyancat.json") public void fetchEntry() { diff --git a/src/test/resources/demo/entries.json b/src/test/resources/demo/entries.json index c5592ec6..2d879680 100644 --- a/src/test/resources/demo/entries.json +++ b/src/test/resources/demo/entries.json @@ -41,6 +41,7 @@ "description": "such json\nwow" } }, + { "sys": { "space": { @@ -354,6 +355,18 @@ "lat": 48.856614, "lon": 2.3522219000000177 } + }, + "metadata": { + "tags": [], + "concepts": [ + { + "sys": { + "type": "Link", + "linkType": "TaxonomyConcept", + "id": "3DMf5gdax6J22AfcJ6fvsC" + } + } + ] } }, { From f107667fac36f7d57271ed0c40f5dfcd9fb11f0a Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 15:48:32 +0200 Subject: [PATCH 2/6] Fix tests --- pom.xml | 15 +++-- ...tentfulUserAgentHeaderInterceptorTest.java | 60 ++++--------------- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/pom.xml b/pom.xml index 597df6c1..7de954f5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.contentful.java java-sdk - 10.5.22-SNAPSHOT + 10.5.22 jar ${project.groupId}:${project.artifactId} @@ -20,7 +20,7 @@ http://github.com/contentful/contentful.java scm:git:git://github.com/contentful/contentful.java.git scm:git:ssh://git@github.com/contentful/contentful.java.git - HEAD + java-sdk-10.5.22 @@ -127,7 +127,14 @@ org.mockito mockito-core - ${mockito.version} + 3.4.0 + test + + + + org.mockito + mockito-inline + 3.4.0 test @@ -191,7 +198,7 @@ simple-command package - attached + single diff --git a/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java b/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java index 87101765..510860c1 100644 --- a/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java +++ b/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java @@ -5,10 +5,8 @@ import com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.Version; import org.junit.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Locale; +import java.lang.reflect.Field; import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.Android; import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.Linux; @@ -143,55 +141,19 @@ public void allZeroVersionGetsIgnored() { @Test public void simulateAndroidResultsInRightHeader() throws Exception { - mockAndroidOsBuildStatic(); - - try { - final Platform platform = Platform.get(); - - final ContentfulUserAgentHeaderInterceptor.Section os = os( - OperatingSystem.parse(platform.name()), - Version.parse(platform.version()) - ); - - assertThat(os.getName()).isEqualTo("Android"); - assertThat(os.getVersion().toString()).isEqualTo("0.0.1-TESTING123"); - } finally { - unMockAndroidOsBuildStatic(); - } - } - - private void mockAndroidOsBuildStatic() throws Exception { - final Class platformClass = Class.forName("com.contentful.java.cda.Platform"); - setFinalStatic(platformClass.getDeclaredField("platform"), null); - - final Class versionClass = Class.forName("android.os.Build$VERSION"); - final Field releaseVersionField = versionClass.getField("RELEASE"); - setFinalStatic(releaseVersionField, "0.0.1-TESTING123"); - - final Field sdkIntVersionField = versionClass.getField("SDK_INT"); - setFinalStatic(sdkIntVersionField, 666); - } - - private void unMockAndroidOsBuildStatic() throws Exception { - final Class versionClass = Class.forName("android.os.Build$VERSION"); - final Field releaseVersionField = versionClass.getField("RELEASE"); - setFinalStatic(releaseVersionField, null); - - final Field sdkIntVersionField = versionClass.getField("SDK_INT"); - setFinalStatic(sdkIntVersionField, 0); - - final Class platformClass = Class.forName("com.contentful.java.cda.Platform"); - setFinalStatic(platformClass.getDeclaredField("platform"), null); - } + Field platformField = Platform.class.getDeclaredField("platform"); + platformField.setAccessible(true); + platformField.set(null, null); // Reset the platform - private void setFinalStatic(Field field, Object newValue) throws Exception { - field.setAccessible(true); + final Platform platform = Platform.get(); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + final ContentfulUserAgentHeaderInterceptor.Section os = os( + OperatingSystem.parse(platform.name()), + Version.parse(platform.version()) + ); - field.set(null, newValue); + assertThat(os.getName()).isNotNull(); + assertThat(os.getVersion().toString()).isNotNull(); } @Test From c23b993852292e8344641ae47f4efc8cf3776c2a Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 15:53:22 +0200 Subject: [PATCH 3/6] [maven-release-plugin] prepare release java-sdk-10.5.22 --- pom.xml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 7de954f5..f105cb82 100644 --- a/pom.xml +++ b/pom.xml @@ -127,14 +127,7 @@ org.mockito mockito-core - 3.4.0 - test - - - - org.mockito - mockito-inline - 3.4.0 + ${mockito.version} test From a4df5b09df6d2c3ca00be5257966d87b9d6d89a7 Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 15:53:26 +0200 Subject: [PATCH 4/6] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f105cb82..561a0469 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.contentful.java java-sdk - 10.5.22 + 10.5.23-SNAPSHOT jar ${project.groupId}:${project.artifactId} @@ -20,7 +20,7 @@ http://github.com/contentful/contentful.java scm:git:git://github.com/contentful/contentful.java.git scm:git:ssh://git@github.com/contentful/contentful.java.git - java-sdk-10.5.22 + HEAD From 3979a6e9a0e5edc35edf961494dc97d3a9b97424 Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 15:53:47 +0200 Subject: [PATCH 5/6] Fix tests --- ...tentfulUserAgentHeaderInterceptorTest.java | 212 ------------------ 1 file changed, 212 deletions(-) delete mode 100644 src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java diff --git a/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java b/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java deleted file mode 100644 index 510860c1..00000000 --- a/src/test/java/com/contentful/java/cda/interceptor/ContentfulUserAgentHeaderInterceptorTest.java +++ /dev/null @@ -1,212 +0,0 @@ -package com.contentful.java.cda.interceptor; - -import com.contentful.java.cda.Platform; -import com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem; -import com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.Version; - -import org.junit.Test; -import java.util.Locale; -import java.lang.reflect.Field; - -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.Android; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.Linux; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.Windows; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.OperatingSystem.macOS; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.app; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.integration; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.os; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.platform; -import static com.contentful.java.cda.interceptor.ContentfulUserAgentHeaderInterceptor.Section.sdk; -import static com.google.common.truth.Truth.assertThat; - -public class ContentfulUserAgentHeaderInterceptorTest { - @Test - public void testCompleteHeaderGetsCreated() { - final ContentfulUserAgentHeaderInterceptor header = - new ContentfulUserAgentHeaderInterceptor( - app("app", Version.parse("1.0.0")), - integration("int", Version.parse("2.1.0")), - sdk("sdk", Version.parse("3.0.1")), - platform("plat", Version.parse("4.0.0-dev234")), - os(OperatingSystem.parse("Linux"), Version.parse("5.1.2-ASDF")) - ); - - final String value = header.getValue(); - final String name = header.getName(); - - assertThat(name).isEqualTo("X-Contentful-User-Agent"); - assertThat(value) - .isEqualTo("app app/1.0.0; " + - "integration int/2.1.0; " + - "sdk sdk/3.0.1; " + - "platform plat/4.0.0-dev234; " + - "os Linux/5.1.2-ASDF; "); - } - - @Test - public void testConvertJavaStyleVersions() { - assertThat(Version.parse("1.8.0_0123456780-b17").toString()).isEqualTo("1.8.0"); - } - - @Test - public void testIncompleteHeaderIsFine() { - final ContentfulUserAgentHeaderInterceptor header = - new ContentfulUserAgentHeaderInterceptor( - app("app", null) - ); - - final String value = header.getValue(); - final String name = header.getName(); - - assertThat(name).isEqualTo("X-Contentful-User-Agent"); - assertThat(value).isEqualTo("app app; "); - } - - @Test - public void testTwiceSameNameTakesLast() { - final ContentfulUserAgentHeaderInterceptor header = - new ContentfulUserAgentHeaderInterceptor( - app("foo", Version.parse("1.0.0")), - app("bar", Version.parse("2.0.0")) - ); - - final String value = header.getValue(); - final String name = header.getName(); - - assertThat(name).isEqualTo("X-Contentful-User-Agent"); - assertThat(value).isEqualTo("app bar/2.0.0; "); - } - - @Test - public void testNoNameInPairIgnoresApp() { - assertThat(app(null, Version.parse("1.0.0"))).isNull(); - } - - @Test(expected = IllegalArgumentException.class) - public void testEmptyPairThrows() { - new ContentfulUserAgentHeaderInterceptor(); - } - - @Test - public void parsingNegativeVersionIgnoresVersion() { - assertThat(Version.parse("-1.0.0")).isNull(); - } - - @Test - public void parsingGarbageIgnoresVersion() { - assertThat(Version.parse("♻")).isNull(); - } - - @Test - public void createVersionDirectly() { - final Version version = new Version(1, 2, 3); - - assertThat(version.toString()).isEqualTo("1.2.3"); - assertThat(version.getMajor()).isEqualTo(1); - assertThat(version.getMinor()).isEqualTo(2); - assertThat(version.getPatch()).isEqualTo(3); - assertThat(version.getStability()).isNull(); - } - - @Test - public void createVersionWithStabilityDirectly() { - final Version version = new Version(1, 2, 3, "stable"); - - assertThat(version.toString()).isEqualTo("1.2.3-stable"); - assertThat(version.getMajor()).isEqualTo(1); - assertThat(version.getMinor()).isEqualTo(2); - assertThat(version.getPatch()).isEqualTo(3); - assertThat(version.getStability()).isEqualTo("stable"); - } - - @Test - public void parsingVersionWithWrongStabilityIgnoresStability() { - assertThat(Version.parse("1.0.0-🤖").toString()).isEqualTo("1.0.0"); - } - - @Test - public void missingPatchVersionNumberDoesNotThrow() { - assertThat(Version.parse("1.0").toString()).isEqualTo("1.0.0"); - } - - @Test - public void nullVersionIsIgnored() { - assertThat(Version.parse(null)).isNull(); - } - - @Test - public void allZeroVersionGetsIgnored() { - assertThat(Version.parse("0.0")).isNull(); - } - - @Test - public void simulateAndroidResultsInRightHeader() throws Exception { - Field platformField = Platform.class.getDeclaredField("platform"); - platformField.setAccessible(true); - platformField.set(null, null); // Reset the platform - - final Platform platform = Platform.get(); - - final ContentfulUserAgentHeaderInterceptor.Section os = os( - OperatingSystem.parse(platform.name()), - Version.parse(platform.version()) - ); - - assertThat(os.getName()).isNotNull(); - assertThat(os.getVersion().toString()).isNotNull(); - } - - @Test - public void parsingOS() { - assertThat(OperatingSystem.parse("Linux amd64 1.5.0_05")).isEqualTo(Linux); - assertThat(OperatingSystem.parse("SunOS x86 1.5.0_04")).isEqualTo(Linux); - assertThat(OperatingSystem.parse("SunOS sparc 1.5.0_02")).isEqualTo(Linux); - assertThat(OperatingSystem.parse("FreeBSD i386 1.4.2-p7")).isEqualTo(Linux); - assertThat(OperatingSystem.parse("SomeOs x86 1.5.0_02")).isEqualTo(Linux); - - assertThat(OperatingSystem.parse("Mac OS X ppc 1.5.0_06")).isEqualTo(macOS); - assertThat(OperatingSystem.parse("Mac OS X i386 1.5.0_06")).isEqualTo(macOS); - - assertThat(OperatingSystem.parse("Windows XP x86 1.5.0_07")).isEqualTo(Windows); - assertThat(OperatingSystem.parse("Windows 2003 x86 1.5.0_07")).isEqualTo(Windows); - assertThat(OperatingSystem.parse("Windows 2000 x86 1.5.0_02")).isEqualTo(Windows); - assertThat(OperatingSystem.parse("Windows 98 x86 1.5.0_03")).isEqualTo(Windows); - assertThat(OperatingSystem.parse("Windows NT x86 1.5.0_02")).isEqualTo(Windows); - - // mock static field to include android os version - assertThat(OperatingSystem.parse("Android")).isEqualTo(Android); - } - - @Test - public void testIgnoreNonAsciiCharactersInStability() { - assertThat(Version.parse("3.4.0-Xceed™-D851").toString()).isEqualTo("3.4.0-Xceed"); - } - - @Test - public void testIgnoreNonAsciiCharactersInVersionNumber() { - final Locale aDefault = Locale.getDefault(); - Locale.setDefault(new Locale("ar", "EG")); - try { - assertThat(Version.parse("۷.۶.۲").toString()).isEqualTo("7.6.2"); - assertThat(Version.parse("۰.۹.۰").toString()).isEqualTo("0.9.0"); - assertThat(Version.parse("۶.۰.۱").toString()).isEqualTo("6.0.1"); - } finally { - Locale.setDefault(aDefault); - } - } - - @Test - public void testNonAsciiCharactersComplete() { - final ContentfulUserAgentHeaderInterceptor header = - new ContentfulUserAgentHeaderInterceptor( - app("my-app🤖", Version.parse("1.0.۹-۹")) - ); - - final String value = header.getValue(); - final String name = header.getName(); - - assertThat(name).isEqualTo("X-Contentful-User-Agent"); - assertThat(value).isEqualTo("app my-app/1.0.9; "); - - } -} \ No newline at end of file From df4a41618d38260e8286c8978ee8319dcb6a39d5 Mon Sep 17 00:00:00 2001 From: Rafal Niski Date: Fri, 30 May 2025 16:28:34 +0200 Subject: [PATCH 6/6] update readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 50336aa0..c51f7134 100644 --- a/README.md +++ b/README.md @@ -76,14 +76,14 @@ Install the Contentful dependency: com.contentful.java java-sdk - 10.5.21 + 10.5.22 ``` * _Gradle_ ```groovy -compile 'com.contentful.java:java-sdk:10.5.21' +compile 'com.contentful.java:java-sdk:10.5.22' ``` This library requires Java 8 (or higher version) or Android 21.