com.contentstack.sdk.Stack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java Show documentation
Show all versions of java Show documentation
Java SDK for Contentstack Content Delivery API
package com.contentstack.sdk;
import com.contentstack.sdk.utility.CSAppConstants;
import com.contentstack.sdk.utility.CSController;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
*
* MIT License
*
* Copyright (c) 2012 - 2019 Contentstack
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
public class Stack {
private static final String TAG = "Stack";
public static Level loggerLevel = Level.DEBUG;
public static boolean LOG_TAKE_OFF_STACKTRACE = true;
private String stackApiKey = null;
protected LinkedHashMap localHeader = null;
private String imageTransformationUrl;
private LinkedHashMap imageParams = new LinkedHashMap<>();
protected String URLSCHEMA = "https://";
protected String URL = "cdn.contentstack.io";
protected String VERSION = "v3";
protected Config config;
protected String SYNC_KEY = "sync";
protected String content_type;
private JSONObject syncParams = null;
protected String sync_token = null;
protected String pagination_token = null;
protected String contentType;
protected String localeCode ;
protected PublishType publishType;
protected String start_from_date;
private SyncResultCallBack syncCallBack;
protected HashMap headerGroup_app;
private Stack(){ }
public static enum PublishType
{
entry_published,
entry_unpublished ,
entry_deleted ,
asset_published ,
asset_unpublished ,
asset_deleted ,
content_type_deleted
}
protected Stack(String stackApiKey) {
this.stackApiKey = stackApiKey;
this.localHeader = new LinkedHashMap<>();
}
protected void setConfig(Config config){
this.config = config;
URLSCHEMA = config.URLSCHEMA;
URL = config.URL;
VERSION = config.VERSION;
if(!config.environment.isEmpty()){
setHeader("environment", config.environment);
}
if (!config.region.name().isEmpty()){
String region = config.region.name().toLowerCase();
if (!region.equalsIgnoreCase("us")){
if(URL.equalsIgnoreCase("cdn.contentstack.io")) {
URL = "cdn.contentstack.com";
}
URL = region+"-"+URL;
}
}
}
public ContentType contentType(String contentTypeName){
this.content_type = contentType;
ContentType contentType = new ContentType(contentTypeName);
contentType.setStackInstance(this);
return contentType;
}
public Asset asset(String uid){
Asset asset = new Asset(uid);
asset.setStackInstance(this);
return asset;
}
protected Asset asset(){
Asset asset = new Asset();
asset.setStackInstance(this);
return asset;
}
public AssetLibrary assetLibrary(){
AssetLibrary library = new AssetLibrary();
library.setStackInstance(this);
return library;
}
public String getApplicationKey(){ return stackApiKey;}
public String getAccessToken(){ return localHeader != null ? (String)localHeader.get("access_token") : null;};
public void removeHeader(String key){
if(!key.isEmpty()){
localHeader.remove(key);
}
}
public void setHeader(String key, String value) {
if (!key.isEmpty() && !value.isEmpty()) {
localHeader.put(key, value);
}
}
/**
* @param image_url
* on which we want to manipulate.
* @param parameters
* It is an second parameter in which we want to place different manipulation key and value in array form
* @return String
*
* ImageTransform function is define for image manipulation with different
* parameters in second parameter in array form
*
*
Example :
*
* //'blt5d4sample2633b' is a dummy Stack API key
* //'blt6d0240b5sample254090d' is dummy access token.
* Stack stack = Contentstack.stack(context, "blt5d4sample2633b", "blt6d0240b5sample254090d", "stag", false);
* // resize the image by specifying width and height
* LinkedHashMap imageParams = new LinkedHashMap();
* imageParams.put("width", 100);
* imageParams.put("height",100);
* imageUrl = Stack.ImageTransform(image_url, parameters);
* stack.ImageTransform(image_url, parameters);
*
*
*/
public String ImageTransform(String image_url, LinkedHashMap parameters) {
imageTransformationUrl = image_url;
imageParams = parameters;
return getImageUrl();
}
private String getImageUrl() {
if (imageParams == null || imageParams.size() == 0) {
return imageTransformationUrl;
}
imageParams.forEach((key, value) -> {
try {
final String encodedKey = URLEncoder.encode(key.toString(), "UTF-8");
final String encodedValue = URLEncoder.encode(value.toString(), "UTF-8");
if (!imageTransformationUrl.contains("?")) {
imageTransformationUrl += "?" + encodedKey + "=" + encodedValue;
} else {
imageTransformationUrl += "&" + encodedKey + "=" + encodedValue;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
});
return imageTransformationUrl;
}
/**
* @param params query parameters
* @param callback ContentTypesCallback
* This call returns comprehensive information of all the content types available in a particular stack in your account.
*
*
Example :
*
*
* JSONObject params = new JSONObject();
* params.put("include_snippet_schema", true);
* params.put("limit", 3);
* stack.getContentTypes(new ContentTypesCallback() {
* public void onCompletion(ContentTypesModel contentTypesModel, Error error) {
* if (error == null){
* // do your stuff.
* }
* }
* });
*
*/
public void getContentTypes(JSONObject params, final ContentTypesCallback callback) {
try {
String URL = "/" + this.VERSION + "/content_types";
HashMap headers = getHeader(localHeader);
if (params == null){ params = new JSONObject(); }
Iterator keys = params.keys();
while(keys.hasNext()) {
// loop to get the dynamic key
String key = (String)keys.next();
// get the value of the dynamic key
Object value = params.opt(key);
// do something here with the value...
params.put(key, value);
}
if (headers.containsKey("environment")) {
params.put("environment", headers.get("environment"));
params.put("include_count", true);
}
fetchContentTypes(URL, params, headers, callback );
}catch (Exception e){
Error error = new Error();
error.setErrorMessage(CSAppConstants.ErrorMessage_JsonNotProper);
callback.onRequestFail(ResponseType.UNKNOWN, error);
}
}
/**
* @param syncCallBack returns callback for sync result.
* The Sync request performs a complete sync of your app data.
* It returns all the published entries and assets of the specified stack in response.
* The response also contains a sync token, which you need to store,
* since this token is used to get subsequent delta updates later.
*
*
Example :
*
*
* stack.sync(SyncResultCallBack syncCallBack){ }
*
*
*/
public void sync(SyncResultCallBack syncCallBack){
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
/**
*
* @param pagination_token If the response is paginated, use the pagination token under this parameter.
* @param syncCallBack returns callback for sync result
*
* If the result of the initial sync (or subsequent sync) contains more than 100 records,
* the response would be paginated. It provides pagination token in the response. However,
* you do not have to use the pagination token manually to get the next batch,
* the SDK does that automatically until the sync is complete.
* Pagination token can be used in case you want to fetch only selected batches.
* It is especially useful if the sync process is interrupted midway (due to network issues, etc.).
* In such cases, this token can be used to restart the sync process from where it was interrupted.
*
*
Example :
*
*
* // dummy pagination_token = "blt7f35951d259183fba680e1";
* stack.syncPaginationToken(pagination_token, new SyncResultCallBack()) {}
*
*
*
*/
public void syncPaginationToken(String pagination_token, SyncResultCallBack syncCallBack){
this.pagination_token = pagination_token;
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("pagination_token", pagination_token);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
/**
* @param sync_token Use the sync token that you received in the previous/initial sync under this parameter.
* @param syncCallBack returns callback for sync result
*
* You can use the sync token (that you receive after initial sync) to get the updated content next time.
* The sync token fetches only the content that was added after your last sync,
* and the details of the content that was deleted or updated.
*
*
*
Example :
*
*
* //dummy sync_token = "blt28937206743728463";
* stack.syncToken(sync_token, new SyncResultCallBack() ){ }
*
*
*/
public void syncToken(String sync_token, SyncResultCallBack syncCallBack){
this.sync_token = sync_token;
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("sync_token", sync_token);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
/**
* @param from_date Enter the start date for initial sync.
* @param syncCallBack Returns callback for sync result.
*
* You can also initialize sync with entries published after a specific date. To do this, use syncWithDate
* and specify the start date as its value.
*
*
Example :
*
*
* // dummy date final Date start_date = sdf.parse("2018-10-07");
* stack.syncFromDate(start_date, new SyncResultCallBack()) { }
*
*
*
*/
public void syncFromDate(Date from_date, SyncResultCallBack syncCallBack){
start_from_date = convertUTCToISO(from_date);
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("start_from", start_from_date);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
private String convertUTCToISO(Date date) {
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
dateFormat.setTimeZone(tz);
return dateFormat.format(date);
}
/**
*
* @param content_type Provide uid of your content_type
* @param syncCallBack Returns callback for sync result.
*
* You can also initialize sync with entries of only specific content_type.
* To do this, use syncContentType and specify the content type uid as its value.
* However, if you do this, the subsequent syncs will only include the entries of the specified content_type.
*
*
Example :
*
*
* // dummy content_type like "session"
* stack.syncContentType(String content_type, new SyncResultCallBack()){ }
*
*
*
*/
public void syncContentType(String content_type, SyncResultCallBack syncCallBack){
this.contentType = content_type;
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("content_type_uid", contentType);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
/**
* @param language Select the required locale from the Language class.
* @param syncCallBack Returns callback for sync result.
*
* You can also initialize sync with entries of only specific locales.
* To do this, use syncLocale and specify the locale code as its value.
* However, if you do this, the subsequent syncs will only include the entries of the specified locales.
*
*
Example :
*
*
* // dummy language- Language.ENGLISH_UNITED_STATES
* stackInstance.syncLocale(Language.ENGLISH_UNITED_STATES, new SyncResultCallBack() ) { }
*
*
*/
public void syncLocale(Language language, SyncResultCallBack syncCallBack) {
this.localeCode = getLanguageCode(language);
if (syncParams == null) {
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("locale", localeCode);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
private String getLanguageCode(Language language){
String localeCode = null;
if (language != null) {
Language languageName = Language.valueOf(language.name());
int localeValue = languageName.ordinal();
LanguageCode[] languageCodeValues = LanguageCode.values();
localeCode = languageCodeValues[localeValue].name();
localeCode = localeCode.replace("_", "-");
}
return localeCode;
}
/**
*
* @param type - Use the type parameter to get a specific type of content
like ( asset_published, entry_published, asset_unpublished, asset_deleted, entry_unpublished, entry_deleted, content_type_deleted.)
*
*
* @param syncCallBack returns callback for sync result.
*
* Use the type parameter to get a specific type of content. You can pass one of the following values:
* asset_published, entry_published, asset_unpublished, asset_deleted, entry_unpublished, entry_deleted, content_type_deleted.
* If you do not specify any value, it will bring all published entries and published assets.
*
*
Example :
*
*
* stackInstance.syncPublishType(Stack.PublishType.entry_published, new SyncResultCallBack()) { }
*
*
*/
public void syncPublishType(PublishType type, SyncResultCallBack syncCallBack){
this.publishType = type;
if (syncParams == null){ syncParams = new JSONObject(); }
try {
syncParams.put("init", true);
syncParams.put("type", publishType);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
/**
*
* @param contentType your content type id
* @param from_date start date
* @param language language as {@link Language}
* @param type type as PublishType
* @param syncCallBack Callback
*
* You can also initialize sync with entries that satisfy multiple parameters.
* To do this, use syncWith and specify the parameters.
* However, if you do this, the subsequent syncs will only include the entries of the specified parameters
*
*
Example :
*
*
* stackInstance.sync(String contentType, Date from_date, Language language, PublishType type, SyncResultCallBack syncCallBack) { }
*
*
*
*/
public void sync( String contentType, Date from_date, Language language, PublishType type, SyncResultCallBack syncCallBack){
start_from_date = convertUTCToISO(from_date);
this.contentType = contentType;
this.publishType = type;
this.localeCode = getLanguageCode(language);
if (syncParams == null){
syncParams = new JSONObject();
}
try {
syncParams.put("init", true);
syncParams.put("start_from", this.start_from_date);
syncParams.put("content_type_uid", this.contentType);
syncParams.put("type", publishType);
syncParams.put("locale", this.localeCode);
} catch (JSONException e) {
e.printStackTrace();
}
this.requestSync(syncCallBack);
}
private void requestSync(final SyncResultCallBack callback){
try {
String URL = "/" + this.VERSION + "/stacks/" + SYNC_KEY;
HashMap headers = getHeader(localHeader);
JSONObject urlQueries = new JSONObject();
if (headers.containsKey("environment")) {
syncParams.put("environment", headers.get("environment"));
}
urlQueries = syncParams;
fetchFromNetwork(URL, syncParams, headers, callback );
}catch (Exception e){
Error error = new Error();
error.setErrorMessage(CSAppConstants.ErrorMessage_JsonNotProper);
callback.onRequestFail(ResponseType.UNKNOWN, error);
}
}
private void fetchContentTypes(String urlString, JSONObject content_type_param, HashMap headers, ContentTypesCallback callback) {
if(callback != null) {
HashMap urlParams = getUrlParams(content_type_param);
new CSBackgroundTask(this, CSController.FETCHCONTENTTYPES, urlString, headers, urlParams, new JSONObject(), CSAppConstants.callController.CONTENTTYPES.toString(), false, CSAppConstants.RequestMethod.GET, callback);
}
}
private void fetchFromNetwork(String urlString, JSONObject urlQueries, HashMap headers, SyncResultCallBack callback) {
if(callback != null) {
HashMap urlParams = getUrlParams(urlQueries);
new CSBackgroundTask(this, CSController.FETCHSYNC, urlString, headers, urlParams, new JSONObject(), CSAppConstants.callController.SYNC.toString(), false, CSAppConstants.RequestMethod.GET, callback);
}
}
private HashMap getUrlParams(JSONObject urlQueriesJSON) {
HashMap hashMap = new HashMap<>();
if(urlQueriesJSON != null && urlQueriesJSON.length() > 0){
Iterator iter = urlQueriesJSON.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
Object value = urlQueriesJSON.opt(key);
hashMap.put(key, value);
} catch (Exception e) {
Stack.log(TAG,"------setQueryJson"+e.toString());
}
}
return hashMap;
}
return null;
}
private HashMap getHeader(HashMap localHeader) {
HashMap mainHeader = headerGroup_app;
HashMap classHeaders = new HashMap<>();
if(localHeader != null && localHeader.size() > 0){
if(mainHeader != null && mainHeader.size() > 0) {
for (Map.Entry entry : localHeader.entrySet()) {
String key = entry.getKey();
classHeaders.put(key, entry.getValue());
}
for (Map.Entry entry : mainHeader.entrySet()) {
String key = entry.getKey();
if(!classHeaders.containsKey(key)) {
classHeaders.put(key, entry.getValue());
}
}
return classHeaders;
}else{
return localHeader;
}
}else{
return headerGroup_app;
}
}
public static void log(String TAG, String logMsg) {
Logger logger = LogManager.getLogger(TAG);
logger.setLevel(loggerLevel);
if (LOG_TAKE_OFF_STACKTRACE){ logger.debug(logMsg); }
}
}