diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a4fa88..ca4d346a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## Version 3.16.0 + +### Date: 31-July-2024 + +- Taxonomy Support + +--- + ## Version 3.15.1 ### Date: 24-June-2024 diff --git a/README.md b/README.md index 62856166..47498c8b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Or, To add the Contentstack Android SDK to your existing project manually, perform the steps given below: -1. [Download the Android SDK](https://docs.contentstack.com/platforms/android/android_sdk_latest) +1. [Download the Android SDK](https://github.com/contentstack/contentstack-android/archive/refs/heads/master.zip) and extract the ZIP file to your local disk. 2. Add references/dependencies using Eclipse/Android Studio: diff --git a/contentstack/build.gradle b/contentstack/build.gradle index 9e9f1fb9..878f55f8 100755 --- a/contentstack/build.gradle +++ b/contentstack/build.gradle @@ -10,7 +10,7 @@ android.buildFeatures.buildConfig true mavenPublishing { publishToMavenCentral(SonatypeHost.DEFAULT) signAllPublications() - coordinates("com.contentstack.sdk", "android", "3.15.1") + coordinates("com.contentstack.sdk", "android", "3.16.0") pom { name = "contentstack-android" @@ -67,6 +67,8 @@ android { exclude("META-INF/notice.txt") exclude("META-INF/ASL2.0") exclude("META-INF/*.kotlin_module") + exclude("META-INF/LICENSE.md") + exclude("META-INF/LICENSE-notice.md") } testOptions { @@ -99,7 +101,7 @@ android { defaultConfig { // Required when setting minSdkVersion to 20 or lower multiDexEnabled true - minSdkVersion 23 + minSdk 24 versionCode 1 versionName "1.0" useLibrary 'org.apache.http.legacy' @@ -137,6 +139,7 @@ android { } configurations { archives } dependencies { + androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' def multidex = "2.0.1" def volley = "1.2.1" def junit = "4.13.2" @@ -144,6 +147,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "com.android.volley:volley:$volley" implementation "junit:junit:$junit" + // For AGP 7.4+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' testImplementation 'junit:junit:4.13.2' @@ -154,6 +158,27 @@ dependencies { // implementation 'com.squareup.okio:okio:3.9.0' implementation 'com.github.rjeschke:txtmark:0.12' + // // Retrofit + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation 'com.squareup.retrofit2:converter-gson' + // // OkHttp + implementation 'com.squareup.okhttp3:okhttp' + // implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' + + constraints { + implementation('com.squareup.retrofit2:converter-gson:2.9.0') { + because 'gson 2.8.5 used by retrofit has a vulnerability' + } + implementation('com.google.code.gson:gson@2.8.9') { + because 'gson 2.8.5 used by retrofit has a vulnerability' + } + implementation('com.squareup.okhttp3:okhttp:4.9.3') { + because 'kotlin stdlib 1.4.10 used by okhttp has a vulnerability' + } + implementation('org.jetbrains.kotlin:kotlin-stdlib@1.6.0') { + because 'kotlin stdlib 1.4.10 used by okhttp has a vulnerability' + } + } } tasks.register('clearJar', Delete) { delete 'build/libs/contentstack.jar' } tasks.register('unzip', Copy) { diff --git a/contentstack/src/androidTest/java/com/contentstack/sdk/TaxonomyTestCase.java b/contentstack/src/androidTest/java/com/contentstack/sdk/TaxonomyTestCase.java new file mode 100644 index 00000000..a3548b54 --- /dev/null +++ b/contentstack/src/androidTest/java/com/contentstack/sdk/TaxonomyTestCase.java @@ -0,0 +1,151 @@ +package com.contentstack.sdk; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.runners.MethodSorters; +import org.junit.*; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static junit.framework.TestCase.*; + +import android.content.Context; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; + +import okhttp3.Request; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Response; + + +public class TaxonomyTestCase { + + private static Stack stack; + private final static String TAG = TaxonomyTestCase.class.getSimpleName(); + + + @BeforeClass + public static void oneTimeSetUp() throws Exception { + Context appContext = ApplicationProvider.getApplicationContext(); + Config config = new Config(); + String DEFAULT_HOST = BuildConfig.host; + config.setHost(DEFAULT_HOST); + stack = Contentstack.stack(appContext, BuildConfig.APIKey, BuildConfig.deliveryToken, BuildConfig.environment, config); + } + + @Test + public void testInstance() { + assertNotNull(stack); + } + + @Test + public void operationIn() { + Taxonomy taxonomy = stack.taxonomy(); + List listOfItems = new ArrayList<>(); + listOfItems.add("maroon"); + listOfItems.add("red"); + Request req = taxonomy.in("taxonomies.color", listOfItems).makeRequest().request(); + assertEquals("GET", req.method()); + assertEquals("cdn.contentstack.io", req.url().host()); + assertEquals("/v3/taxonomies/entries", req.url().encodedPath()); + assertEquals("query={\"taxonomies.color\":{\"$in\":[\"maroon\",\"red\"]}}", req.url().query()); + } + + @Test + public void operationOr() throws JSONException, IOException { +// query={ $or: [ +// { "taxonomies.taxonomy_uid_1" : "term_uid1" }, +// { "taxonomies.taxonomy_uid_2" : "term_uid2" } +// ]} + Taxonomy taxonomy = stack.taxonomy(); + List listOfItems = new ArrayList<>(); + JSONObject item1 = new JSONObject(); + item1.put("taxonomies.color", "orange"); + JSONObject item2 = new JSONObject(); + item2.put("taxonomies.country", "zambia"); + listOfItems.add(item1); + listOfItems.add(item2); + taxonomy.or(listOfItems); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"$or\":[{\"taxonomies.color\":\"orange\"},{\"taxonomies.country\":\"zambia\"}]}", req.url().query()); + + } + + @Test + public void operatorAnd() throws JSONException { + Taxonomy taxonomy = stack.taxonomy(); + List listOfItems = new ArrayList<>(); + JSONObject items1 = new JSONObject(); + items1.put("taxonomies.color", "green"); + JSONObject items2 = new JSONObject(); + items2.put("taxonomies.country", "india"); + listOfItems.add(items1); + listOfItems.add(items2); + taxonomy.and(listOfItems); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"$and\":[{\"taxonomies.color\":\"green\"},{\"taxonomies.country\":\"india\"}]}", req.url().query()); + } + + + @Test + public void operationExists() throws IOException { + Taxonomy taxonomy = stack.taxonomy().exists("taxonomies.color", true); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"taxonomies.color\":{\"$exists\":true}}", req.url().query()); + } + + + @Test + public void operationEqualAndBelow() throws IOException { + Taxonomy taxonomy = stack.taxonomy().equalAndBelow("taxonomies.color", "red"); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"taxonomies.color\":{\"$eq_below\":\"red\"}}", req.url().query()); + } + + + @Test + public void operationEqualAbove() { + Taxonomy taxonomy = stack.taxonomy().equalAbove("taxonomies.appliances", "led"); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"taxonomies.appliances\":{\"$eq_above\":\"led\"}}", req.url().query()); + + } + + @Test + public void above() { + Taxonomy taxonomy = stack.taxonomy().above("taxonomies.appliances", "led"); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"taxonomies.appliances\":{\"$above\":\"led\"}}", req.url().query()); + } + + @Test + public void below() { + Taxonomy taxonomy = stack.taxonomy().below("taxonomies.appliances", "TV"); + Request req = taxonomy.makeRequest().request(); + assertEquals("query={\"taxonomies.appliances\":{\"$below\":\"TV\"}}", req.url().query()); + } + + @Test + public void aboveAPI() { + Taxonomy taxonomy = stack.taxonomy().below("taxonomies.color", "red"); + Request req = taxonomy.makeRequest().request(); + taxonomy.find(new TaxonomyCallback() { + @Override + public void onResponse(JSONObject response, Error error) { + Log.d("Result",response.toString()); + } + }); + assertEquals("query={\"taxonomies.color\":{\"$below\":\"red\"}}", req.url().query()); + } + + +} + diff --git a/contentstack/src/main/java/com/contentstack/sdk/APIService.java b/contentstack/src/main/java/com/contentstack/sdk/APIService.java new file mode 100644 index 00000000..37b5eaf0 --- /dev/null +++ b/contentstack/src/main/java/com/contentstack/sdk/APIService.java @@ -0,0 +1,23 @@ +package com.contentstack.sdk; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.HeaderMap; +import retrofit2.http.Query; +import retrofit2.http.Url; + +import java.util.LinkedHashMap; +import java.util.Map; + + +public interface APIService { + @GET + Call getRequest( + @Url String url, @HeaderMap LinkedHashMap headers); + + @GET("v3/taxonomies/entries") + Call getTaxonomy( + @HeaderMap Map headers, + @Query("query") String query); +} diff --git a/contentstack/src/main/java/com/contentstack/sdk/Asset.java b/contentstack/src/main/java/com/contentstack/sdk/Asset.java index 828e609d..bbac9b8b 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/Asset.java +++ b/contentstack/src/main/java/com/contentstack/sdk/Asset.java @@ -385,8 +385,8 @@ public void fetch(FetchResultCallback callback) { urlQueries.put("environment", headers.get("environment")); } String mainStringForMD5 = urlEndpoint + new JSONObject().toString() + headers.toString(); - String md5Value = new SDKUtil().getMD5FromString(mainStringForMD5.trim()); - File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + md5Value); + String shaValue = new SDKUtil().getSHAFromString(mainStringForMD5.trim()); + File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + shaValue); switch (cachePolicyForCall) { case IGNORE_CACHE: diff --git a/contentstack/src/main/java/com/contentstack/sdk/AssetLibrary.java b/contentstack/src/main/java/com/contentstack/sdk/AssetLibrary.java index e47cf2b0..48a081d8 100644 --- a/contentstack/src/main/java/com/contentstack/sdk/AssetLibrary.java +++ b/contentstack/src/main/java/com/contentstack/sdk/AssetLibrary.java @@ -215,8 +215,8 @@ public void fetchAll(FetchAssetsCallback assetsCallback) { urlQueries.put("environment", headers.get("environment")); } String mainStringForMD5 = URL + new JSONObject().toString() + headers.toString(); - String md5Value = new SDKUtil().getMD5FromString(mainStringForMD5.trim()); - File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + md5Value); + String shaValue = new SDKUtil().getSHAFromString(mainStringForMD5.trim()); + File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + shaValue); switch (cachePolicyForCall) { case IGNORE_CACHE: fetchFromNetwork(URL, urlQueries, headers, cacheFile.getPath(), assetsCallback); diff --git a/contentstack/src/main/java/com/contentstack/sdk/Config.java b/contentstack/src/main/java/com/contentstack/sdk/Config.java index 4391548a..f55a2203 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/Config.java +++ b/contentstack/src/main/java/com/contentstack/sdk/Config.java @@ -2,7 +2,11 @@ import android.text.TextUtils; +import java.net.Proxy; import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import okhttp3.ConnectionPool; /** @@ -17,6 +21,10 @@ public class Config { protected String environment = null; protected String branch = null; protected String[] earlyAccess = null; + protected Proxy proxy = null; + protected ConnectionPool connectionPool = new ConnectionPool(); + protected String endpoint; + /** @@ -179,5 +187,55 @@ public String getEnvironment() { return environment; } + /** + * Proxy can be set like below. + * + * @param proxy Proxy setting, typically a type (http, socks) and a socket address. A Proxy is an immutable object + *
+ *
+ * Example:
+ *
+ * + * java.net.Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxyHost", "proxyPort")); + * java.net.Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("sl.theproxyvpn.io", 80)); Config + * config = new Config(); config.setProxy(proxy); + * + */ + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + /** + * Returns the Proxy instance + * + * @return Proxy + */ + public Proxy getProxy() { + return this.proxy; + } + + /** + * Manages reuse of HTTP and HTTP/2 connections for reduced network latency. HTTP requests that * share the same + * {@link okhttp3.Address} may share a {@link okhttp3.Connection}. This class implements the policy * of which + * connections to keep open for future use. + * + * @param maxIdleConnections the maxIdleConnections default value is 5 + * @param keepAliveDuration the keepAliveDuration default value is 5 + * @param timeUnit the timeUnit default value is TimeUnit. MINUTES + * @return ConnectionPool + */ + public ConnectionPool connectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { + this.connectionPool = new ConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit); + return this.connectionPool; + } + + protected String getEndpoint() { + return endpoint + "/" + getVersion() + "/"; + } + + protected void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + } diff --git a/contentstack/src/main/java/com/contentstack/sdk/Entry.java b/contentstack/src/main/java/com/contentstack/sdk/Entry.java index 64987ec8..f315392c 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/Entry.java +++ b/contentstack/src/main/java/com/contentstack/sdk/Entry.java @@ -1087,9 +1087,9 @@ public void fetch(EntryResultCallBack callBack) { } String mainStringForMD5 = URL + new JSONObject().toString() + headerAll.toString(); - String md5Value = new SDKUtil().getMD5FromString(mainStringForMD5.trim()); + String shaValue = new SDKUtil().getSHAFromString(mainStringForMD5.trim()); - File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + md5Value); + File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + shaValue); switch (cachePolicyForCall) { diff --git a/contentstack/src/main/java/com/contentstack/sdk/Query.java b/contentstack/src/main/java/com/contentstack/sdk/Query.java index 7c540415..ceed7910 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/Query.java +++ b/contentstack/src/main/java/com/contentstack/sdk/Query.java @@ -1595,8 +1595,8 @@ protected void execQuery(SingleQueryResultCallback callBack, QueryResultsCallBac mainJSON.put("query", urlQueries); mainJSON.put("_method", SDKConstant.RequestMethod.GET.toString()); String mainStringForMD5 = URL + mainJSON.toString() + headers.toString(); - String md5Value = new SDKUtil().getMD5FromString(mainStringForMD5.trim()); - File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + md5Value); + String shaValue = new SDKUtil().getSHAFromString(mainStringForMD5.trim()); + File cacheFile = new File(SDKConstant.cacheFolderName + File.separator + shaValue); CachePolicy cachePolicy = CachePolicy.NETWORK_ONLY;//contentTypeInstance.stackInstance.globalCachePolicyForCall; if (cachePolicyForCall != null) { cachePolicy = cachePolicyForCall; diff --git a/contentstack/src/main/java/com/contentstack/sdk/SDKUtil.java b/contentstack/src/main/java/com/contentstack/sdk/SDKUtil.java index acf0723a..72cf092f 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/SDKUtil.java +++ b/contentstack/src/main/java/com/contentstack/sdk/SDKUtil.java @@ -134,15 +134,15 @@ public static JSONObject getJsonFromCacheFile(File file) { * To encrypt given value. * * @param value string - * @return MD5 value + * @return SHA-256 value */ - public String getMD5FromString(String value) { + public String getSHAFromString(String value) { String output; output = value.toString().trim(); if (value.length() > 0) { try { // Create MD5 Hash - MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); + MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256"); digest.reset(); digest.update(output.getBytes()); byte messageDigest[] = digest.digest(); diff --git a/contentstack/src/main/java/com/contentstack/sdk/Stack.java b/contentstack/src/main/java/com/contentstack/sdk/Stack.java index a7f0c0c3..c695da21 100755 --- a/contentstack/src/main/java/com/contentstack/sdk/Stack.java +++ b/contentstack/src/main/java/com/contentstack/sdk/Stack.java @@ -9,6 +9,7 @@ import org.json.JSONObject; import java.io.UnsupportedEncodingException; +import java.net.Proxy; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -22,6 +23,11 @@ import java.util.Objects; import java.util.TimeZone; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + /** * To fetch stack level information of your application from Contentstack server. *

@@ -41,10 +47,12 @@ public class Stack implements INotifyClass { protected Config config; protected ArrayMap headerGroupApp; protected JSONObject syncParams = null; + protected Retrofit retrofit; protected String skip = null; protected String limit = null; protected String localeCode; private SyncResultCallBack syncCallBack; + protected APIService service; protected Stack() { @@ -87,10 +95,29 @@ protected void setConfig(Config config) { } } } + String endpoint = config.PROTOCOL + config.URL; + this.config.setEndpoint(endpoint); + client(endpoint); + + } + private void client(String endpoint) { + Proxy proxy = this.config.getProxy(); + ConnectionPool pool = this.config.connectionPool; + OkHttpClient client = new OkHttpClient.Builder() + .proxy(proxy) + .connectionPool(pool) + .build(); + + Retrofit retrofit = new Retrofit.Builder().baseUrl(endpoint) + .client(client) + .build(); + + this.service = retrofit.create(APIService.class); } + /** * Represents a {@link ContentType}.
* Create {@link ContentType} instance. @@ -163,6 +190,15 @@ public AssetLibrary assetLibrary() { return library; } + /** + * Create {@link Taxonomy} instance. + * @return + */ + public Taxonomy taxonomy(){ + return new Taxonomy(this.service,this.config,this.localHeader); + } + + /** * Get stack application key * diff --git a/contentstack/src/main/java/com/contentstack/sdk/Taxonomy.java b/contentstack/src/main/java/com/contentstack/sdk/Taxonomy.java new file mode 100644 index 00000000..c3386ae7 --- /dev/null +++ b/contentstack/src/main/java/com/contentstack/sdk/Taxonomy.java @@ -0,0 +1,368 @@ +package com.contentstack.sdk; + +import android.util.ArrayMap; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Response; + + +/** + * + *
+ * Taxonomy : + * Taxonomy, currently in the Early Access Phase simplifies + * the process of organizing content in your system, making + * it effortless to find and retrieve information. + * @implSpec To implement the taxonomy use below code + *

+ *     {@code
+ *     Stack stack = Contentstack.stack("API_KEY", "DELIVERY_TOKEN", "ENVIRONMENT");
+ *     Taxonomy taxonomy = stack.taxonomy()
+ *     }
+ * 
+ * + * + */ +public class Taxonomy { + protected ArrayMap headers; + protected APIService service; + protected JSONObject query = new JSONObject(); + protected Config config; + + /** + * Instantiates a new Taxonomy. + * + * @param service the service of type {@link APIService} + * @param config the config of type {@link Config} + * @param headers the headers of the {@link LinkedHashMap} + */ + protected Taxonomy(APIService service, Config config, ArrayMap headers) { + this.service = service; + this.headers = headers; + this.config = config; + } + + + /** + * Get all entries for a specific taxonomy that satisfy the given conditions provided in the '$in' query. + * Your query should be as follows: + *

+ *

+     * {"taxonomies.taxonomy_uid" : { "$in" : ["term_uid1" , "term_uid2" ] }}
+     * 
+ *

+ * Example: If you want to retrieve entries with the color taxonomy applied and linked to the term red and/or yellow. + *

+ *

+     * {"taxonomies.color" : { "$in" : ["red" , "yellow" ] }}
+     * 
+ * + * @param taxonomy the key of the taxonomy to query + * @param listOfItems the list of taxonomy fields + * @return an instance of the Taxonomy with the specified conditions added to the query + */ + public Taxonomy in(String taxonomy, List listOfItems) { + JSONObject innerObj = new JSONObject(); + try { + JSONArray jsonArray = new JSONArray(listOfItems); + innerObj.put("$in", jsonArray); + this.query.put(taxonomy, innerObj); + } catch (JSONException e) { + throw new RuntimeException(e); + } + return this; + } + + + /** + * OR Operator : + *

+ * Get all entries for a specific taxonomy that satisfy at least one of the given conditions provided in the “$or” query. + *

+ * Your query should be as follows: + *

+ *

+     *
+     * { $or: [
+     * { "taxonomies.taxonomy_uid_1" : "term_uid1" },
+     * { "taxonomies.taxonomy_uid_2" : "term_uid2" }
+     * ]}
+     *
+     * 
+ * Example: If you want to retrieve entries with either the color or size taxonomy applied and linked to the terms yellow and small, respectively. + *
+ *
+     *
+     * { $or: [
+     * { "taxonomies.color" : "yellow" },
+     * { "taxonomies.size" : "small" }
+     * ]}
+     *
+     * 
+ * + * @param listOfItems the list of items + * @return instance {@link Taxonomy} + */ + public Taxonomy or(List listOfItems) { + if(listOfItems != null){ + try { + JSONArray jsonArray = new JSONArray(listOfItems); + this.query.put("$or", jsonArray); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * AND Operator : + *

+ * Get all entries for a specific taxonomy that satisfy all the conditions provided in the “$and” query. + *

+ * Your query should be as follows: + * + *

+     * {
+     * $and: [
+     * { "taxonomies.taxonomy_uid_1" : "term_uid1" },
+     * { "taxonomies.taxonomy_uid_2" : "term_uid2" }
+     * ]
+     * }
+     * 
+ * Example: If you want to retrieve entries with the color and computers taxonomies applied and linked to the terms red and laptop, respectively. + * + *
+     * {
+     * $and: [
+     * { "taxonomies.color" : "red" },
+     * { "taxonomies.computers" : "laptop" }
+     * ]
+     * }
+     * 
+ * + * @param listOfItems the list of items to that you want to include in the query string + * @return instance of the Taxonomy + */ + public Taxonomy and(List listOfItems) { + if(listOfItems != null) { + try { + JSONArray jsonArray = new JSONArray(listOfItems); + this.query.put("$and", jsonArray); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * Exists Operator : + *

+ * Get all entries for a specific taxonomy that if the value of the field, mentioned in the condition, exists. + *

+ * Your query should be as follows: + *

+     * {"taxonomies.taxonomy_uid" : { "$exists": true }}
+     * 
+ * Example: If you want to retrieve entries with the color taxonomy applied. + *
+     * {"taxonomies.color" : { "$exists": true }}
+     * 
+ * + * @param taxonomy the taxonomy + * @param value the value of the field + * @return instance of Taxonomy + */ + public Taxonomy exists( String taxonomy,Boolean value) { + if(!taxonomy.isEmpty() && value != null) { + JSONObject json = new JSONObject(); + try { + json.put("$exists", value); + this.query.put(taxonomy, json); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * Equal and Below Operator : + *

+ * Get all entries for a specific taxonomy that match a specific term and all its descendant terms, requiring only the target term and a specified level. + *

+ * Note: If you don't specify the level, the default behavior is to retrieve terms up to level 10. + * + *

{"taxonomies.taxonomy_uid" : { "$eq_below": "term_uid", "level" : 2}}
+ * + * Example: If you want to retrieve all entries with terms nested under blue, such as navy blue and sky blue, while also matching entries with the target term blue. + * + *
{"taxonomies.color" : { "$eq_below": "blue" }}
+ * + * @param taxonomy the taxonomy + * @param termsUid the term uid + * @return instance of Taxonomy + */ + public Taxonomy equalAndBelow(String taxonomy, String termsUid) { + if(!taxonomy.isEmpty() && !termsUid.isEmpty()){ + JSONObject param = new JSONObject(); + try { + param.put("$eq_below", termsUid); + this.query.put(taxonomy, param); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + /** + * Below Operator + *
+ *

+ * Get all entries for a specific taxonomy that match all of their descendant terms by specifying only the target term and a specific level. + *
+ * Note: If you don't specify the level, the default behavior is to retrieve terms up to level 10. + *
+ *

{"taxonomies.taxonomy_uid" : { "$below": "term_uid", "level" : 2}}
+ * + * Example: If you want to retrieve all entries containing terms nested under blue, such as navy blue and sky blue, but exclude entries that solely have the target term blue. + * + *
{"taxonomies.color" : { "$below": "blue" }}
+ * + * @param taxonomy the taxonomy + * @param termsUid the terms uid + * @return instance of Taxonomy + */ + public Taxonomy below (String taxonomy, String termsUid) { + if(!taxonomy.isEmpty() && !termsUid.isEmpty()) { + JSONObject param = new JSONObject(); + try { + param.put("$below", termsUid); + this.query.put(taxonomy, param); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * Equal and Above Operator : + *

+ * Get all entries for a specific taxonomy that match a specific term and all its ancestor terms, requiring only the target term and a specified level. + * + * Note: If you don't specify the level, the default behavior is to retrieve terms up to level 10. + *

+ *

{"taxonomies.taxonomy_uid": { "$eq_above": "term_uid", "level": 2 }}
+ *

+ * Example: If you want to obtain all entries that include the term led and its parent term tv. + *

+ *

{"taxonomies.appliances": { "$eq_above": "led"}}
+ * + * @param taxonomy the taxonomy + * @param termUid the term uid + * @return instance of Taxonomy + */ + public Taxonomy equalAbove( String taxonomy, String termUid) { + if(!taxonomy.isEmpty() && !termUid.isEmpty()) { + JSONObject innerMap = new JSONObject(); + try { + innerMap.put("$eq_above", termUid); + this.query.put(taxonomy, innerMap); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * Above Operator : + *

+ * Get all entries for a specific taxonomy that match only the parent term(s) of a specified target term, excluding the target term itself. You can also specify a specific level. + *

+ * Note: If you don't specify the level, the default behavior is to retrieve terms up to level 10. + * + *

{ "taxonomies.taxonomy_uid": { "$above": "term_uid", "level": 2 }}
+ *

+ * Example: If you wish to match entries with the term tv but exclude the target term led. + * + *

{"taxonomies.appliances": { "$above": "led" }}
+ * + * @param taxonomy the taxonomy + * @param termUid the term uid + * @return instance of {@link Taxonomy} + */ + public Taxonomy above( String taxonomy, String termUid) { + if(!taxonomy.isEmpty() && !termUid.isEmpty()) { + JSONObject innerMap = new JSONObject(); + try { + innerMap.put("$above", termUid); + this.query.put(taxonomy, innerMap); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + return this; + } + + + /** + * To verify the payload + * + * @return instance of Call + */ + protected Call makeRequest() { + return this.service.getTaxonomy(this.headers, this.query.toString()); + } + + /** + * Find. + * + * @param callback the callback + */ + public void find(TaxonomyCallback callback) { + try { + Response response = makeRequest().execute(); + + if (response.isSuccessful()) { + JSONObject responseJSON = new JSONObject(response.body().string()); + callback.onResponse(responseJSON, null); + } else { + JSONObject responseJSON = new JSONObject(response.errorBody().string()); + Error error = new Error(); + error.setErrorMessage(responseJSON.optString("error_message")); + error.setErrorCode(responseJSON.optInt("error_code")); + + callback.onResponse(null, error); + } + + } catch (IOException | JSONException e) { + throw new RuntimeException(e); + } + } + + +} + + diff --git a/contentstack/src/main/java/com/contentstack/sdk/TaxonomyCallback.java b/contentstack/src/main/java/com/contentstack/sdk/TaxonomyCallback.java new file mode 100644 index 00000000..3145ea40 --- /dev/null +++ b/contentstack/src/main/java/com/contentstack/sdk/TaxonomyCallback.java @@ -0,0 +1,14 @@ +package com.contentstack.sdk; + + +import org.json.JSONObject; + +public interface TaxonomyCallback { + /** + * This method is called wen API response gets received + * @param response the response of type JSON + * @param error the error of type @{@link Error} + */ + void onResponse(JSONObject response, Error error); + +}