All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prismic.Form Maven / Gradle / Ivy

Go to download

The developer kit to access Prismic.io repositories using the Java language.

The newest version!
package io.prismic;

import com.fasterxml.jackson.databind.JsonNode;
import io.prismic.core.HttpClient;

import java.util.*;

/**
 * A general usage RESTful form, manipulated by higher-level forms like {@link Form.SearchForm}.
 * If your need is simply to query content, you shouldn't need to look into this class, and use
 * the methods in {@link Form.SearchForm} instead.
 */
public class Form {

  public static class Field {

    final private String type;
    final private Boolean multiple;
    final private String defaultValue;

    public Field(String type, Boolean multiple, String defaultValue) {
      this.type = type;
      this.multiple = multiple;
      this.defaultValue = defaultValue;
    }

    public String getType() {
      return type;
    }

    public String getDefaultValue() {
      return defaultValue;
    }

    public Boolean isMultiple() {
      return multiple;
    }

    // --

    static Field parse(JsonNode json) {
      String type = json.path("type").asText();
      String defaultValue = (json.has("default") ? json.path("default").asText() : null);
      Boolean multiple = (json.has("multiple") && json.path("multiple").asBoolean());
      return new Field(type, multiple, defaultValue);
    }

  }

  // --

  final private String name;
  final private String method;
  final private String rel;
  final private String enctype;
  final private String action;
  final private Map fields;

  public Form(String name, String method, String rel, String enctype, String action, Map fields) {
    this.name = name;
    this.method = method.toUpperCase();
    this.rel = rel;
    this.enctype = enctype;
    this.action = action;
    this.fields = Collections.unmodifiableMap(fields);
  }

  public String getName() {
    return name;
  }

  public String getMethod() {
    return method;
  }

  public String getRel() {
    return rel;
  }

  public String getEnctype() {
    return enctype;
  }

  public String getAction() {
    return action;
  }

  public Map getFields() {
    return fields;
  }

  public String toString() {
    return method + " " + action;
  }

  // --

  static Form parse(JsonNode json) {
    String name = json.path("name").asText();
    String method = json.path("method").asText();
    String rel = json.path("rel").asText();
    String enctype = json.path("enctype").asText();
    String action = json.path("action").asText();

    Map fields = new HashMap<>();
    Iterator fieldsJson = json.with("fields").fieldNames();
    while(fieldsJson.hasNext()) {
      String field = fieldsJson.next();
      fields.put(field, Field.parse(json.with("fields").path(field)));
    }

    return new Form(name, method, rel, enctype, action, fields);
  }

  // --

  /**
   * The object you will use to perform queries. At the moment, only queries of the type "SearchForm" exist in prismic.io's APIs.
   * There is one named "everything", that allow to query through the while repository, and there is also one per collection
   * created by prismic.io administrators in the writing-room.
   *
   * From an {@link Api} object, you get a SearchForm form like this: api.getForm("everything");
   *
   * Then, from a SearchForm form, you query like this: search.query("[[:d = at(document.type, "Product")]]").ref(ref).submit();
   */
  public static class SearchForm {

    final private Api api;
    final private Form form;
    final private Map> data;

    public SearchForm(Api api, Form form) {
      this.api = api;
      this.form = form;
      this.data = new HashMap<>();
      for(Map.Entry field: form.getFields().entrySet())
        if (field.getValue().getDefaultValue() != null) {
          List value = new ArrayList<>(1);
          value.add(field.getValue().getDefaultValue());
          this.data.put(field.getKey(), value);
        }
    }

    /**
     * Allows to set one of the form's fields, such as "q" for the query field, or the "ordering" field, or the "pageSize" field.
     * The field must exist in the RESTful description that is in the /api document. To be on the safe side, you should use the
     * specialized methods, and use searchForm.orderings(o) rather than searchForm.set("orderings", o)
     * if they exist.
     *
     * @param field the name of the field to set
     * @param value the value with which to set it
     * @return the current form, in order to chain those calls
     */
    public SearchForm set(String field, String value) {
      Field fieldDesc = form.getFields().get(field);
      if (fieldDesc == null) {
        throw new RuntimeException("Unknown field " + field);
      }
      if (value == null) {
        return this;
      }
      if (fieldDesc.isMultiple()) {
        List existingValue = data.get(field);
        if(existingValue == null) {
          existingValue = new ArrayList<>();
        }
        existingValue.add(value);
        data.put(field, existingValue);
      } else {
        List newValue = new ArrayList<>();
        newValue.add(value);
        data.put(field, newValue);
      }
      return this;
    }

    /**
     * A simple helper to set numeric value; see set(String,String).
     * @param field the name of the field to set
     * @param value target value
     * @return the current form, in order to chain those calls
     */
    public SearchForm set(String field, Integer value) {
      Field fieldDesc = form.getFields().get(field);
      if(fieldDesc == null) {
        throw new RuntimeException("Unknown field " + field);
      }
      if(!"Integer".equals(fieldDesc.getType())) {
        throw new RuntimeException("Cannot set an Integer value to field " + field + " of type " + fieldDesc.getType());
      }
      return set(field, value.toString());
    }

    /**
     * Allows to set the language you want to get for your query.
     *
     * @param lang the language code you wish
     * @return the current form, in order to chain those calls
     */
    public SearchForm lang(String lang) {
      return set("lang", lang);
    }

    /**
     * Allows to set the ref on which you wish to be performing the query.
     *
     * This is mandatory to submit a query; if you call api.getForm("everything").submit();, the kit will complain!
     *
     * Please do not forge the ref dynamically in this call, like this: ref(api.getMaster()).
     * Prefer to set a ref variable once for your whole webpage, and use that variable in this method: ref(ref).
     * That way, you can change this variable's assignment once, and trivially set your whole webpage into the future or the past.
     *
     * @param ref the ref object representing the ref on which you wish to query
     * @return the current form, in order to chain those calls
     */
    public SearchForm ref(Ref ref) {
      return ref(ref.getRef());
    }

    /**
     * Allows to set the ref on which you wish to be performing the query.
     *
     * This is mandatory to submit a query; if you call api.getForm("everything").submit();, the kit will complain!
     *
     * Please do not forge the ref dynamically in this call, like this: ref(api.getMaster().getRef()).
     * Prefer to set a ref variable once for your whole webpage, and use that variable in this method: ref(ref).
     * That way, you can change this variable's assignment once, and trivially set your whole webpage into the future or the past.
     *
     * @param ref the ID of the ref on which you wish to query
     * @return the current form, in order to chain those calls
     */
    public SearchForm ref(String ref) {
      return set("ref", ref);
    }

    /**
     * Allows to set the size of the pagination of the query's response.
     *
     * The default value is 20; a call with a different page size will look like:
     * api.getForm("everything").pageSize("15").ref(ref).submit();.
     *
     * @param pageSize the size of the pagination you wish
     * @return the current form, in order to chain those calls
     */
    public SearchForm pageSize(String pageSize) {
      return set("pageSize", pageSize);
    }

    /**
     * Allows to set the size of the pagination of the query's response.
     *
     * The default value is 20; a call with a different page size will look like:
     * api.getForm("everything").pageSize(15).ref(ref).submit();.
     *
     * @param pageSize the size of the pagination you wish
     * @return the current form, in order to chain those calls
     */
    public SearchForm pageSize(int pageSize) {
      return set("pageSize", pageSize);
    }

    /**
     * Allows to set which page you want to get for your query.
     *
     * The default value is 1; a call for a different page will look like:
     * api.getForm("everything").page("2").ref(ref).submit();
     * (do remember that the default size of a page is 20, you can change it with pageSize)
     *
     * @param page the page number
     * @return the current form, in order to chain those calls
     */
    public SearchForm page(String page) {
      return set("page", page);
    }

    /**
     * Restrict the document fragments to the set of fields specified.
     *
     * @param fields the fields to return
     * @return the current form, in order to chain those calls
     */
    public SearchForm fetch(String... fields) {
      switch (fields.length) {
        case 0: // Noop
          return this;
        default:
          return set("fetch", Utils.mkString(fields, ","));
      }
    }

    /**
     * Include the specified fragment in the details of DocumentLink
     *
     * @param fields the fields to return
     * @return the current form, in order to chain those calls
     */
    public SearchForm fetchLinks(String... fields) {
      switch (fields.length) {
        case 0: // Noop
          return this;
        default:
          return set("fetchLinks", Utils.mkString(fields, ","));
      }
    }

    /**
     * Allows to set which page you want to get for your query.
     *
     * The default value is 1; a call for a different page will look like:
     * api.getForm("everything").page(2).ref(ref).submit();
     * (do remember that the default size of a page is 20, you can change it with pageSize)
     *
     * @param page the page number
     * @return the current form, in order to chain those calls
     */
    public SearchForm page(int page) {
      return set("page", page);
    }

    /**
     * Allows to set which ordering you want for your query.
     *
     * A call will look like:
     * api.getForm("products").orderings("my.product.price desc", "my.product.date").ref(ref).submit();
     * Read prismic.io's API documentation to learn more about how to write orderings.
     *
     * @param orderings one or more ordering, as the name of a field optionally followed by "desc"
     * @return the current form, in order to chain those calls
     */
    public SearchForm orderings(String... orderings) {
      switch (orderings.length) {
        case 0: // Noop
          return this;
        case 1: // Strip for backward compatibility
          return set("orderings", "[" + strip(orderings[0]) + "]");
        default:
          return set("orderings", "[" + Utils.mkString(orderings, ",") + "]");
      }
    }

    // Temporary hack for Backward compatibility
    private String strip(String q) {
      if(q == null) return "";
      String tq = q.trim();
      if(tq.indexOf("[") == 0 && tq.lastIndexOf("]") == tq.length() - 1) {
        return tq.substring(1, tq.length() - 1);
      }
      return tq;
    }

    /**
     * Allows to set the query field of the current form. For instance:
     * search.query("[[:d = at(document.type, "Product")]]");
     * Look up prismic.io's documentation online to discover the possible query predicates.
     *
     * Beware: a query is a list of predicates, therefore, it always starts with "[[" and ends with "]]".
     *
     * @param q the query to pass
     * @return the current form, in order to chain those calls
     */
    public SearchForm query(String q) {
      Field fieldDesc = form.getFields().get("q");
      if(fieldDesc != null && fieldDesc.isMultiple()) {
        return set("q", q);
      } else {
        List value = new ArrayList<>();
        value.add(("[ " + (form.getFields().containsKey("q") ? strip(form.getFields().get("q").getDefaultValue()) : "") + " " + strip(q) + " ]"));
        this.data.put("q", value);
        return this;
      }
    }

    /**
     * Allows to set the query field of the current form, using Predicate objects. Example:
     * search.query(Predicates.at("document.type", "Product"));
     * See io.prismic.Predicates for more helper methods.
     *
     * @param predicates any number of predicate, is more than one is provided documents that satisfy all predicates will be returned ("AND" query)
     * @return the current form, in order to chain those calls
     */
     public SearchForm query(Predicate... predicates) {
      StringBuilder result = new StringBuilder();
      if (predicates != null) {
        for (Predicate p : predicates) {
          result.append(p.q());
        }
      }
      return this.query("[" + result.toString() + "]");
    }

    /**
     * The method to call to perform and retrieve your query.
     *
     * Please make sure you're set a ref on this SearchForm form before querying, or the kit will complain!
     *
     * @return the list of documents, that can be directly used as such.
     */
    public Response submit() {
      if("GET".equals(form.getMethod()) && "application/x-www-form-urlencoded".equals(form.getEnctype())) {
        StringBuilder url = new StringBuilder(form.getAction());
        String sep = form.getAction().contains("?") ? "&" : "?";
        for(Map.Entry> d: data.entrySet()) {
          for(String v: d.getValue()) {
            url.append(sep)
               .append(d.getKey())
               .append("=")
               .append(HttpClient.encodeURIComponent(v));
            sep = "&";
          }
        }
        JsonNode json = HttpClient.fetch(url.toString(), api.getLogger(), api.getCache(), api.getProxy());
        return Response.parse(json);
      } else {
        throw new Api.Error(Api.Error.Code.UNEXPECTED, "Form type not supported");
      }
    }

    public String toString() {
      StringBuilder dataStr = new StringBuilder();
      for(Map.Entry> d: data.entrySet()) {
        for(String v: d.getValue()) {
          dataStr.append(d.getKey()).append("=").append(v).append(" ");
        }
      }
      return form.toString() + " {" + dataStr.toString().trim() + "}";
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy