Package com.erudika.para.rest

Source Code of com.erudika.para.rest.ParaClient

/*
* Copyright 2013-2014 Erudika. http://erudika.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For issues and patches go to: https://github.com/erudika
*/
package com.erudika.para.rest;

import com.amazonaws.Request;
import com.erudika.para.core.App;
import com.erudika.para.core.ParaObject;
import com.erudika.para.core.Tag;
import com.erudika.para.utils.Config;
import com.erudika.para.utils.Pager;
import com.erudika.para.utils.Utils;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The Java REST client.
*
* @author Alex Bogdanovski [alex@erudika.com]
*/
public final class ParaClient {

  private static final Logger logger = LoggerFactory.getLogger(ParaClient.class);
  private static final String DEFAULT_ENDPOINT = "http://localhost:8080";
  private static final String DEFAULT_PATH = "/v1/";
  private static final String GET = HttpMethod.GET;
  private static final String PUT = HttpMethod.PUT;
  private static final String POST = HttpMethod.POST;
  private static final String DELETE = HttpMethod.DELETE;
  private final Signer signer = new Signer();
  private final Client client;
  private String endpoint;
  private String path;
  private String accessKey;
  private String secretKey;

  public ParaClient(String accessKey, String secretKey) {
    ClientConfig clientConfig = new ClientConfig();
    clientConfig.register(GenericExceptionMapper.class);
    clientConfig.register(new JacksonJsonProvider(Utils.getJsonMapper()));
    clientConfig.connectorProvider(new JettyConnectorProvider());
    this.client = ClientBuilder.newClient(clientConfig);
    this.accessKey = accessKey;
    this.secretKey = secretKey;
  }

  public void setEndpoint(String endpoint) {
    this.endpoint = endpoint;
  }

  /**
   * Returns the {@link App} for the current access key (appid).
   * @return the App object
   */
  public App getApp() {
    return read(Utils.type(App.class), accessKey);
  }

  /**
   * Returns the endpoint URL
   * @return the endpoint
   */
  public String getEndpoint() {
    if (StringUtils.isBlank(endpoint)) {
      return DEFAULT_ENDPOINT;
    } else {
      return endpoint;
    }
  }

  /**
   * Sets the API request path
   * @param path a new path
   */
  public void setApiPath(String path) {
    this.path = path;
  }

  /**
   * Returns the API request path
   * @return the request path without parameters
   */
  public String getApiPath() {
    if (StringUtils.isBlank(path)) {
      return DEFAULT_PATH;
    } else {
      if (!path.endsWith("/")) {
        path += "/";
      }
      return path;
    }
  }

  @SuppressWarnings("unchecked")
  private <T> T getEntity(Response res, Class<?> type) {
    if (res != null) {
      if (res.getStatus() == Response.Status.OK.getStatusCode()
          || res.getStatus() == Response.Status.CREATED.getStatusCode()
          || res.getStatus() == Response.Status.NOT_MODIFIED.getStatusCode()) {
          return res.hasEntity() ? res.readEntity((Class<T>) type) : null;
      } else if (res.getStatus() != Response.Status.NOT_FOUND.getStatusCode()
          && res.getStatus() != Response.Status.NOT_MODIFIED.getStatusCode()
          && res.getStatus() != Response.Status.NO_CONTENT.getStatusCode()) {
        Map<String, Object> error = res.hasEntity() ? res.readEntity(Map.class) : null;
        if (error != null && error.containsKey("code")) {
          String msg = error.containsKey("message") ? (String) error.get("message") : "error";
          msg = msg.concat(" ").concat(Integer.toString((Integer) error.get("code")));
          throw new WebApplicationException(msg, (Integer) error.get("code"));
        }
      }
    }
    return null;
  }

  private Response invokeGet(String resourcePath, MultivaluedMap<String, String> params) {
    return invoke(GET, resourcePath, null, params, null);
  }

  private Response invokePost(String resourcePath, Entity<?> entity) {
    return invoke(POST, resourcePath, null, null, entity);
  }

  private Response invokePut(String resourcePath, Entity<?> entity) {
    return invoke(PUT, resourcePath, null, null, entity);
  }

  private Response invokeDelete(String resourcePath, MultivaluedMap<String, String> params) {
    return invoke(DELETE, resourcePath, null, params, null);
  }

  private Response invoke(String httpMethod, String resourcePath,
      Map<String, String> headers, MultivaluedMap<String, String> params, Entity<?> entity) {

    if (StringUtils.isBlank(accessKey) || StringUtils.isBlank(secretKey)) {
      logger.warn("Blank access key or secret key!");
      accessKey = "";
      secretKey = "";
    }

    if (httpMethod == null) {
      httpMethod = GET;
    }
    if (resourcePath == null) {
      resourcePath = "";
    } else if (resourcePath.startsWith("/")) {
      resourcePath = resourcePath.substring(1);
    }

    String reqPath = getApiPath() + resourcePath;
    WebTarget target = client.target(getEndpoint()).path(reqPath);
    InputStream in = null;
    Entity<?> jsonPayload = null;
    Map<String, String> sigParams = new HashMap<String, String>();

    if (params != null) {
      for (Map.Entry<String, List<String>> param : params.entrySet()) {
        String key = param.getKey();
        List<String> value = param.getValue();
        if (value != null && !value.isEmpty() && value.get(0) != null) {
          target = target.queryParam(key, value.toArray());
          sigParams.put(key, value.get(0));
        }
      }
    }

    Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON);

    if (headers != null) {
      for (Map.Entry<String, String> header : headers.entrySet()) {
        builder.header(header.getKey(), header.getValue());
      }
    }

    if (entity != null) {
      try {
        byte[] entt = Utils.getJsonWriter().writeValueAsBytes(entity.getEntity());
        in = new BufferedInputStream(new ByteArrayInputStream(entt));
        jsonPayload = Entity.json(new String(entt, Config.DEFAULT_ENCODING));
      } catch (IOException ex) {
        logger.error(null, ex);
      }
    }

    Request<?> signed = signer.sign(httpMethod, getEndpoint(), reqPath, headers, sigParams, in, accessKey, secretKey);

    builder.header(HttpHeaders.AUTHORIZATION, signed.getHeaders().get(HttpHeaders.AUTHORIZATION)).
        header("X-Amz-Date", signed.getHeaders().get("X-Amz-Date"));

    if (jsonPayload != null) {
      return builder.method(httpMethod, jsonPayload);
    } else {
      return builder.method(httpMethod);
    }
  }

  private MultivaluedMap<String, String> pagerToParams(Pager... pager) {
    MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
    if (pager != null && pager.length > 0) {
      Pager p = pager[0];
      if (p != null) {
        map.put("page", Collections.singletonList(Long.toString(p.getPage())));
        map.put("desc", Collections.singletonList(Boolean.toString(p.isDesc())));
        map.put("limit", Collections.singletonList(Integer.toString(p.getLimit())));
        if (p.getSortby() != null) {
          map.put("sort", Collections.singletonList(p.getSortby()));
        }
      }
    }
    return map;
  }

  @SuppressWarnings("unchecked")
  private <P extends ParaObject> List<P> getItemsFromList(List<?> result) {
    if (result != null && !result.isEmpty()) {
      // this isn't very efficient but there's no way to know what type of objects we're reading
      ArrayList<P> objects = new ArrayList<P>();
      for (Object map : result) {
        P p = Utils.setAnnotatedFields((Map<String, Object>) map);
        if (p != null) {
          objects.add(p);
        }
      }
      return objects;
    }
    return Collections.emptyList();
  }

  @SuppressWarnings("unchecked")
  private <P extends ParaObject> List<P> getItems(Map<String, Object> result, Pager... pager) {
    if (result != null && !result.isEmpty() && result.containsKey("items")) {
      if (pager != null && pager.length > 0 && pager[0] != null && result.containsKey("totalHits")) {
        pager[0].setCount(((Integer) result.get("totalHits")).longValue());
      }
      return (List<P>) getItemsFromList((List<?>) result.get("items"));
    }
    return Collections.emptyList();
  }

  /////////////////////////////////////////////
  //         PERSISTENCE
  /////////////////////////////////////////////

  /**
   * Persists an object to the data store.
   * @param <P> the type of object
   * @param obj the domain object
   * @return the same object with assigned id or null if not created.
   */
  public <P extends ParaObject> P create(P obj) {
    if (obj == null) {
      return null;
    }
    return getEntity(invokePost(obj.getType(), Entity.json(obj)), obj.getClass());
  }

  /**
   * Retrieves an object from the data store.
   * @param <P> the type of object
   * @param type the type of the object
   * @param id the id of the object
   * @return the retrieved object or null if not found
   */
  public <P extends ParaObject> P read(String type, String id) {
    if (StringUtils.isBlank(type) || StringUtils.isBlank(id)) {
      return null;
    }
    return getEntity(invokeGet(type.concat("/").concat(id), null), Utils.toClass(type));
  }

  /**
   * Updates an object permanently.
   * @param <P> the type of object
   * @param obj the object to update
   * @return the updated object
   */
  public <P extends ParaObject> P update(P obj) {
    if (obj == null) {
      return null;
    }
    return getEntity(invokePut(obj.getObjectURI(), Entity.json(obj)), obj.getClass());
  }

  /**
   * Deletes an object permanently.
   * @param <P> the type of object
   * @param obj the object
   */
  public <P extends ParaObject> void delete(P obj) {
    if (obj == null) {
      return;
    }
    invokeDelete(obj.getObjectURI(), null);
  }

  /**
   * Saves multiple objects to the data store.
   * @param <P> the type of object
   * @param objects the list of objects to save
   * @return a list of objects
   */
  public <P extends ParaObject> List<P> createAll(List<P> objects) {
    if (objects == null || objects.isEmpty() || objects.get(0) == null) {
      return Collections.emptyList();
    }
    return getItemsFromList((List<?>) getEntity(invokePost("_batch", Entity.json(objects)), List.class));
  }

  /**
   * Retrieves multiple objects from the data store.
   * @param <P> the type of object
   * @param keys a list of object ids
   * @return a list of objects
   */
  public <P extends ParaObject> List<P> readAll(List<String> keys) {
    if (keys == null || keys.isEmpty()) {
      return Collections.emptyList();
    }
    MultivaluedMap<String, String> ids = new MultivaluedHashMap<String, String>();
    ids.put("ids", keys);
    return getItemsFromList((List<?>) getEntity(invokeGet("_batch", ids), List.class));
  }

  /**
   * Updates multiple objects.
   * @param <P> the type of object
   * @param objects the objects to update
   * @return a list of objects
   */
  public <P extends ParaObject> List<P> updateAll(List<P> objects) {
    if (objects == null || objects.isEmpty()) {
      return Collections.emptyList();
    }
    return getItemsFromList((List<?>) getEntity(invokePut("_batch", Entity.json(objects)), List.class));
  }

  /**
   * Deletes multiple objects.
   * @param <P> the type of object
   * @param keys the ids of the objects to delete
   */
  public <P extends ParaObject> void deleteAll(List<String> keys) {
    if (keys == null || keys.isEmpty()) {
      return;
    }
    MultivaluedMap<String, String> ids = new MultivaluedHashMap<String, String>();
    ids.put("ids", keys);
    invokeDelete("_batch", ids);
  }

  /**
   * Returns a list all objects found for the given type.
   * The result is paginated so only one page of items is returned, at a time.
   * @param <P> the type of object
   * @param type the type of objects to search for
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> List<P> list(String type, Pager... pager) {
    if (StringUtils.isBlank(type)) {
      return Collections.emptyList();
    }
    return getItems((Map<String, Object>) getEntity(invokeGet(type, pagerToParams(pager)), Map.class), pager);
  }

  /////////////////////////////////////////////
  //         SEARCH
  /////////////////////////////////////////////

  /**
   * Simple id search.
   * @param <P> type of the object
   * @param id the id
   * @return the object if found or null
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> P findById(String id) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle(Config._ID, id);
    List<P> list = getItems(find("id", params));
    return list.isEmpty() ? null : list.get(0);
  }

  /**
   * Simple multi id search.
   * @param <P> type of the object
   * @param ids a list of ids to search for
   * @return the object if found or null
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> List<P> findByIds(List<String> ids) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.put("ids", ids);
    return (List<P>) getItems(find("ids", params));
  }

  /**
   * Search for {@link com.erudika.para.core.Address} objects in a radius of X km from a given point.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param query the query string
   * @param radius the radius of the search circle
   * @param lat latitude
   * @param lng longitude
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findNearby(String type, String query, int radius, double lat, double lng,
      Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("latlng", lat + "," + lng);
    params.putSingle("radius", Integer.toString(radius));
    params.putSingle("q", query);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("nearby", params), pager);
  }

  /**
   * Searches for objects that have a property which value starts with a given prefix.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param field the property name of an object
   * @param prefix the prefix
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findPrefix(String type, String field, String prefix, Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("field", field);
    params.putSingle("prefix", prefix);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("prefix", params), pager);
  }

  /**
   * Simple query string search. This is the basic search method.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param query the query string
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findQuery(String type, String query, Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("q", query);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("", params), pager);
  }

  /**
   * Searches for objects that have similar property values to a given text. A "find like this" query.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param filterKey exclude an object with this key from the results (optional)
   * @param fields a list of property names
   * @param liketext text to compare to
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findSimilar(String type, String filterKey, String[] fields, String liketext,
      Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.put("fields", fields == null ? null : Arrays.asList(fields));
    params.putSingle("filterid", filterKey);
    params.putSingle("like", liketext);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("similar", params), pager);
  }

  /**
   * Searches for objects tagged with one or more tags.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param tags the list of tags
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findTagged(String type, String[] tags, Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.put("tags", tags == null ? null : Arrays.asList(tags));
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("tagged", params), pager);
  }

  /**
   * Searches for {@link com.erudika.para.core.Tag} objects.
   * This method might be deprecated in the future.
   * @param <P> type of the object
   * @param keyword the tag keyword to search for
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findTags(String keyword, Pager... pager) {
    keyword = (keyword == null) ? "*" : keyword.concat("*");
    return findWildcard(Utils.type(Tag.class), "tag", keyword, pager);
  }

  /**
   * Searches for objects having a property value that is in list of possible values.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param field the property name of an object
   * @param terms a list of terms (property values)
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findTermInList(String type, String field, List<String> terms, Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("field", field);
    params.put("terms", terms);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("in", params), pager);
  }

  /**
   * Searches for objects that have properties matching some given values. A terms query.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param terms a map of fields (property names) to terms (property values)
   * @param matchAll match all terms. If true - AND search, if false - OR search
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findTerms(String type, Map<String, ?> terms, boolean matchAll,
      Pager... pager) {
    if (terms == null) {
      return Collections.emptyList();
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("matchall", Boolean.toString(matchAll));
    ArrayList<String> list = new ArrayList<String>();
    for (Map.Entry<String, ? extends Object> term : terms.entrySet()) {
      String key = term.getKey();
      Object value = term.getValue();
      if (value != null) {
        list.add(key.concat(Config.SEPARATOR).concat(value.toString()));
      }
    }
    if (!terms.isEmpty()) {
      params.put("terms", list);
    }
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("terms", params), pager);
  }

  /**
   * Searches for objects that have a property with a value matching a wildcard query.
   * @param <P> type of the object
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param field the property name of an object
   * @param wildcard wildcard query string. For example "cat*".
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of objects found
   */
  public <P extends ParaObject> List<P> findWildcard(String type, String field, String wildcard, Pager... pager) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("field", field);
    params.putSingle("q", wildcard);
    params.putSingle(Config._TYPE, type);
    params.putAll(pagerToParams(pager));
    return getItems(find("wildcard", params), pager);
  }

  /**
   * Counts indexed objects.
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @return the number of results found
   */
  public Long getCount(String type) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle(Config._TYPE, type);
    Pager pager = new Pager();
    getItems(find("count", params), pager);
    return pager.getCount();
  }

  /**
   * Counts indexed objects matching a set of terms/values.
   * @param type the type of object to search for. See {@link com.erudika.para.core.ParaObject#getType()}
   * @param terms a list of terms (property values)
   * @return the number of results found
   */
  public Long getCount(String type, Map<String, ?> terms) {
    if (terms == null) {
      return 0L;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    ArrayList<String> list = new ArrayList<String>();
    for (Map.Entry<String, ? extends Object> term : terms.entrySet()) {
      String key = term.getKey();
      Object value = term.getValue();
      if (value != null) {
        list.add(key.concat(Config.SEPARATOR).concat(value.toString()));
      }
    }
    if (!terms.isEmpty()) {
      params.put("terms", list);
    }
    params.putSingle(Config._TYPE, type);
    params.putSingle("count", "true");
    Pager pager = new Pager();
    getItems(find("terms", params), pager);
    return pager.getCount();
  }

  private <P extends ParaObject> Map<String, Object> find(String queryType, MultivaluedMap<String, String> params) {
    Map<String, Object> map = new HashMap<String, Object>();
    if (params != null && !params.isEmpty()) {
      String qType = StringUtils.isBlank(queryType) ? "" : "/".concat(queryType);
      return getEntity(invokeGet("search".concat(qType), params), Map.class);
    } else {
      map.put("items", new ArrayList<P>());
      map.put("totalHits", 0);
    }
    return map;
  }

  /////////////////////////////////////////////
  //         LINKS
  /////////////////////////////////////////////

  /**
   * Count the total number of links between this object and another type of object.
   * @param type2 the other type of object
   * @param obj the object to execute this method on
   * @return the number of links for the given object
   */
  @SuppressWarnings("unchecked")
  public Long countLinks(ParaObject obj, String type2) {
    if (obj == null || obj.getId() == null || type2 == null) {
      return 0L;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("count", "true");
    Pager pager = new Pager();
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    getItems((Map<String, Object>) getEntity(invokeGet(url, params), Map.class), pager);
    return pager.getCount();
  }

  /**
   * Returns all objects linked to the given one. Only applicable to many-to-many relationships.
   * @param <P> type of linked objects
   * @param type2 type of linked objects to search for
   * @param obj the object to execute this method on
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of linked objects
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> List<P> getLinkedObjects(ParaObject obj, String type2, Pager... pager) {
    ArrayList<P> list = new ArrayList<P>();
    if (obj == null || obj.getId() == null || type2 == null) {
      return list;
    }
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    return getItems((Map<String, Object>) getEntity(invokeGet(url, null), Map.class), pager);
  }

  /**
   * Checks if this object is linked to another.
   * @param type2 the other type
   * @param id2 the other id
   * @param obj the object to execute this method on
   * @return true if the two are linked
   */
  public boolean isLinked(ParaObject obj, String type2, String id2) {
    if (obj == null || obj.getId() == null || type2 == null || id2 == null) {
      return false;
    }
    String url = Utils.formatMessage("{0}/links/{1}/{2}", obj.getObjectURI(), type2, id2);
    return getEntity(invokeGet(url, null), Boolean.class);
  }

  /**
   * Checks if a given object is linked to this one.
   * @param toObj the other object
   * @param obj the object to execute this method on
   * @return true if linked
   */
  public boolean isLinked(ParaObject obj, ParaObject toObj) {
    if (obj == null || obj.getId() == null || toObj == null || toObj.getId() == null) {
      return false;
    }
    return isLinked(obj, toObj.getType(), toObj.getId());
  }

  /**
   * Links an object to this one in a many-to-many relationship.
   * Only a link is created. Objects are left untouched.
   * The type of the second object is automatically determined on read.
   * @param id2 link to the object with this id
   * @param obj the object to execute this method on
   * @return the id of the {@link com.erudika.para.core.Linker} object that is created
   */
  public String link(ParaObject obj, String id2) {
    if (obj == null || obj.getId() == null || id2 == null) {
      return null;
    }
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), id2);
    return getEntity(invokePost(url, null), String.class);
  }

  /**
   * Unlinks an object from this one.
   * Only a link is deleted. Objects are left untouched.
   * @param type2 the other type
   * @param obj the object to execute this method on
   * @param id2 the other id
   */
  public void unlink(ParaObject obj, String type2, String id2) {
    if (obj == null || obj.getId() == null || type2 == null || id2 == null) {
      return;
    }
    String url = Utils.formatMessage("{0}/links/{1}/{2}", obj.getObjectURI(), type2, id2);
    invokeDelete(url, null);
  }

  /**
   * Unlinks all objects that are linked to this one.
   * @param obj the object to execute this method on
   * Deletes all {@link com.erudika.para.core.Linker} objects.
   * Only the links are deleted. Objects are left untouched.
   */
  public void unlinkAll(ParaObject obj) {
    if (obj == null || obj.getId() == null) {
      return;
    }
    String url = Utils.formatMessage("{0}/links", obj.getObjectURI());
    invokeDelete(url, null);
  }

  /**
   * Count the total number of child objects for this object.
   * @param type2 the type of the other object
   * @param obj the object to execute this method on
   * @return the number of links
   */
  @SuppressWarnings("unchecked")
  public Long countChildren(ParaObject obj, String type2) {
    if (obj == null || obj.getId() == null || type2 == null) {
      return 0L;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("count", "true");
    params.putSingle("childrenonly", "true");
    Pager pager = new Pager();
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    getItems((Map<String, Object>) getEntity(invokeGet(url, params), Map.class), pager);
    return pager.getCount();
  }

  /**
   * Returns all child objects linked to this object.
   * @param <P> the type of children
   * @param type2 the type of children to look for
   * @param obj the object to execute this method on
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of {@link ParaObject} in a one-to-many relationship with this object
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> List<P> getChildren(ParaObject obj, String type2, Pager... pager) {
    ArrayList<P> list = new ArrayList<P>();
    if (obj == null || obj.getId() == null || type2 == null) {
      return list;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("childrenonly", "true");
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    return getItems((Map<String, Object>) getEntity(invokeGet(url, params), Map.class), pager);
  }

  /**
   * Returns all child objects linked to this object.
   * @param <P> the type of children
   * @param type2 the type of children to look for
   * @param field the field name to use as filter
   * @param term the field value to use as filter
   * @param obj the object to execute this method on
   * @param pager a {@link com.erudika.para.utils.Pager}
   * @return a list of {@link ParaObject} in a one-to-many relationship with this object
   */
  @SuppressWarnings("unchecked")
  public <P extends ParaObject> List<P> getChildren(ParaObject obj, String type2, String field, String term,
      Pager... pager) {
    ArrayList<P> list = new ArrayList<P>();
    if (obj == null || obj.getId() == null || type2 == null) {
      return list;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("childrenonly", "true");
    params.putSingle("field", field);
    params.putSingle("term", term);
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    return getItems((Map<String, Object>) getEntity(invokeGet(url, params), Map.class), pager);
  }

  /**
   * Deletes all child objects permanently.
   * @param obj the object to execute this method on
   * @param type2 the children's type.
   */
  public void deleteChildren(ParaObject obj, String type2) {
    if (obj == null || obj.getId() == null || type2 == null) {
      return;
    }
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("childrenonly", "true");
    String url = Utils.formatMessage("{0}/links/{1}", obj.getObjectURI(), type2);
    invokeDelete(url, params);
  }

  /////////////////////////////////////////////
  //         UTILS
  /////////////////////////////////////////////

  /**
   * Generates a new unique id.
   * @return a new id
   */
  public String newId() {
    String res = getEntity(invokeGet("utils/newid", null), String.class);
    return res != null ? res : "";
  }

  /**
   * Returns the current timestamp.
   * @return a long number
   */
  public long getTimestamp() {
    Long res = getEntity(invokeGet("utils/timestamp", null), Long.class);
    return res != null ? res : 0L;
  }

  /**
   * Formats a date in a specific format.
   * @param format the date format
   * @param loc the locale instance
   * @return a formatted date
   */
  public String formatDate(String format, Locale loc) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("format", format);
    params.putSingle("locale", loc == null ? null : loc.toString());
    return getEntity(invokeGet("utils/formatdate", params), String.class);
  }

  /**
   * Converts spaces to dashes.
   * @param str a string with spaces
   * @param replaceWith a string to replace spaces with
   * @return a string with dashes
   */
  public String noSpaces(String str, String replaceWith) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("string", str);
    params.putSingle("replacement", replaceWith);
    return getEntity(invokeGet("utils/nospaces", params), String.class);
  }

  /**
   * Strips all symbols, punctuation, whitespace and control chars from a string.
   * @param str a dirty string
   * @return a clean string
   */
  public String stripAndTrim(String str) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("string", str);
    return getEntity(invokeGet("utils/nosymbols", params), String.class);
  }

  /**
   * Converts Markdown to HTML
   * @param markdownString Markdown
   * @return HTML
   */
  public String markdownToHtml(String markdownString) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("md", markdownString);
    return getEntity(invokeGet("utils/md2html", params), String.class);
  }

  /**
   * Returns the number of minutes, hours, months elapsed for a time delta (milliseconds).
   * @param delta the time delta between two events, in milliseconds
   * @return a string like "5m", "1h"
   */
  public String approximately(long delta) {
    MultivaluedMap<String, String> params = new MultivaluedHashMap<String, String>();
    params.putSingle("delta", Long.toString(delta));
    return getEntity(invokeGet("utils/timeago", params), String.class);
  }

  /////////////////////////////////////////////
  //         MISC
  /////////////////////////////////////////////

  /**
   * First-time setup - creates the root app and returns its credentials.
   * @return a map of credentials
   */
  Map<String, String> setup() {
    return getEntity(invokeGet("setup", null), Map.class);
  }

  /**
   * Generates a new set of access/secret keys.
   * Old keys are discarded and invalid after this.
   * @return a map of new credentials
   */
  public Map<String, String> newKeys() {
    return getEntity(invokePost("newkeys", null), Map.class);
  }

  /**
   * Returns all registered types for this App.
   * @return a map of plural-singular form of all the registered types.
   */
  public Map<String, String> types() {
    return getEntity(invokeGet("types", null), Map.class);
  }

  /**
   * Closes the client. Calling this method effectively invalidates all resource
   * targets produced by the client instance. Invoking any method
   * on such targets once the client is closed would result in an
   * IllegalStateException being thrown.
   */
  public void close() {
    if (client != null) {
      client.close();
    }
  }
}
TOP

Related Classes of com.erudika.para.rest.ParaClient

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.