Java
A Java program that uses the API with java.net.http, part of Java 11 and later; it needs Java 17 for the rest. It lists a template's labelled variables, measures the drawing it makes with the values given, and exports it as a PDF. Java has no JSON parser of its own, so the program ends with a short one, enough for these answers, and builds its requests' JSON as text; in a real program use a library such as Jackson or Gson.
Download it: Box.java.
// Box.java: list a template's inputs, measure it and export it as a PDF with the PiQ SaaS API.
// Java 17 or later; java.net.http, no libraries needed. Run with: java Box.java
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.*;
public class Box {
static final String TEMPLATE =
"L=400,\"Length\"\nW=300,\"Width\"\nP1=[L,0]\nP2=[L,W]\nP3=[0,W]\nPOLYLINE @1,[P0,P1,P2,P3],TRUE,0";
static final String KEY = System.getenv("PIQ_API_KEY");
static final String BASE = Objects.requireNonNullElse(System.getenv("PIQ_URL"), "https://saas.parametriq.co.uk")
.replaceAll("/$", "");
static final HttpClient HTTP = HttpClient.newHttpClient();
public static void main(String[] args) throws Exception {
if (KEY == null || KEY.isEmpty()) { System.err.println("Set PIQ_API_KEY to your API key."); System.exit(1); }
String storage = System.getenv("PIQ_STORAGE"); // optional: a registered storage's name
// the template itself, or (optional) where it is kept in your storage
String template = storage == null ? "\"template\":" + quote(TEMPLATE)
: "\"source\":{\"storage\":" + quote(storage) + ",\"key\":\"templates/box.piq\"}";
String values = "\"values\":{\"L\":500,\"W\":250}";
// 1. The template's labelled variables
var inputs = (Map<?, ?>) Json.parse(post("/inputs", "{" + template + "}").body());
for (var item : (List<?>) inputs.get("inputs")) {
var i = (Map<?, ?>) item;
System.out.printf("%s: %s (%s, %s)%n", i.get("name"), i.get("label"), i.get("kind"), i.get("value"));
}
// 2. The drawing measured, with the values given
var m = (Map<?, ?>) Json.parse(post("/measure", "{" + template + "," + values + "}").body());
Object cut = 0;
for (var layer : (List<?>) m.get("layers"))
if ("Cut".equals(((Map<?, ?>) layer).get("layer"))) cut = ((Map<?, ?>) layer).get("length");
System.out.printf("Size: %s x %s, cut length: %s%n", m.get("width"), m.get("height"), cut);
// 3. The drawing as a PDF
var pdf = HTTP.send(request("/export/pdf", "{" + template + "," + values + "}"),
HttpResponse.BodyHandlers.ofByteArray());
if (pdf.statusCode() >= 300) fail(pdf.statusCode(), new String(pdf.body(), "UTF-8"));
Files.write(Path.of("box.pdf"), pdf.body());
System.out.printf("Saved box.pdf (%s warnings)%n", pdf.headers().firstValue("X-PiQ-Warnings").orElse("?"));
// 4. Optional: the PDF saved to your storage instead of downloaded
if (storage != null) {
String destination = "\"destination\":{\"storage\":" + quote(storage)
+ ",\"key\":\"drawings/box.pdf\"},\"overwrite\":true";
var saved = (Map<?, ?>) Json.parse(
post("/save/pdf", "{" + template + "," + values + "," + destination + "}").body());
System.out.printf("Saved %s to %s (%s bytes)%n", saved.get("key"), saved.get("storage"), saved.get("size"));
}
}
static HttpRequest request(String path, String json) {
return HttpRequest.newBuilder(URI.create(BASE + path)).header("X-API-KEY", KEY)
.header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(json)).build();
}
static HttpResponse<String> post(String path, String json) throws Exception {
var response = HTTP.send(request(path, json), HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 300) fail(response.statusCode(), response.body());
return response;
}
static void fail(int status, String body) {
System.err.println("Error " + status);
if (!body.isEmpty()) { // a 401 has no body
try {
var problem = (Map<?, ?>) Json.parse(body);
for (var part : List.of("title", "detail"))
if (problem.get(part) != null) System.err.println(problem.get(part));
if (problem.get("errors") instanceof Map<?, ?> errors)
errors.forEach((field, messages) -> System.err.println(" " + field + ": "
+ String.join(" ", ((List<?>) messages).stream().map(String::valueOf).toList())));
} catch (RuntimeException e) { System.err.println(body); }
}
System.exit(1);
}
static String quote(String s) {
var b = new StringBuilder("\"");
for (char c : s.toCharArray())
if (c == '"' || c == '\\') b.append('\\').append(c);
else if (c < 0x20) b.append(String.format("\\u%04x", (int) c));
else b.append(c);
return b.append('"').toString();
}
/** Just enough JSON for this example: in a real program use a library such as Jackson or Gson. */
static class Json {
final String s; int i;
Json(String s) { this.s = s; }
static Object parse(String s) { return new Json(s).value(); }
void skip() { while (i < s.length() && Character.isWhitespace(s.charAt(i))) i++; }
Object value() {
skip();
char c = s.charAt(i);
if (c == '{') {
var map = new LinkedHashMap<String, Object>(); i++; skip();
if (s.charAt(i) == '}') { i++; return map; }
do { skip(); String k = (String) value(); skip(); i++; map.put(k, value()); skip(); }
while (s.charAt(i++) == ',');
return map;
}
if (c == '[') {
var list = new ArrayList<Object>(); i++; skip();
if (s.charAt(i) == ']') { i++; return list; }
do list.add(value()); while (skipThen() == ',');
return list;
}
if (c == '"') {
var b = new StringBuilder(); i++;
for (char d; (d = s.charAt(i++)) != '"'; )
if (d != '\\') b.append(d);
else switch (d = s.charAt(i++)) {
case 'n' -> b.append('\n'); case 't' -> b.append('\t'); case 'r' -> b.append('\r');
case 'b' -> b.append('\b'); case 'f' -> b.append('\f');
case 'u' -> { b.append((char) Integer.parseInt(s.substring(i, i + 4), 16)); i += 4; }
default -> b.append(d);
}
return b.toString();
}
int start = i;
while (i < s.length() && ",}] \r\n\t".indexOf(s.charAt(i)) < 0) i++;
String word = s.substring(start, i);
return switch (word) { case "true" -> true; case "false" -> false; case "null" -> null;
default -> word.matches("-?\\d+") ? (Object) Long.parseLong(word) : (Object) Double.parseDouble(word); };
}
char skipThen() { skip(); return s.charAt(i++); }
}
}
How it works
- Settings. The key comes from
PIQ_API_KEY, the address fromPIQ_URL(the service itself if not set) and, optionally, a storage's name fromPIQ_STORAGE. Without a key the program stops at once. - The template.
templateis the start of every request's JSON: the template's text astemplateor, whenPIQ_STORAGEis set, where it is kept in your storage assource.quotemakes a JSON string of text, escaping its quotes and new lines.valuesgivesLandWby name. requestbuilds aPOSTof JSON to an operation, with the key in theX-API-KEYheader.postsends it and hands back the answer as text, or, if it isn't a success, goes tofail.failprints the status, then the problem description: itstitle, itsdetail, and each entry oferrors, the field and what is wrong with it. A401has no body, so the body is only parsed when there is one; a body that isn't JSON is printed as it is. The program then exits with1.- Inputs.
POST /inputsneeds only the template. Its answer is parsed into maps and lists; each of itsinputshas aname,label,kindandvalue. - Measure.
POST /measurewith the values gives the drawing'swidthandheight, and its lines' lengths by layer inlayers; the cut length is the length on theCutlayer. - Export.
POST /export/pdfanswers with the PDF itself, not JSON, so it is read as bytes, checked, and written tobox.pdf. TheX-PiQ-Warningsheader says how many things couldn't be exported as they are. - Save (optional). With a storage,
POST /save/pdfexports the same drawing and saves it to your storage atdestination,"overwrite":truereplacing a file already there. It answers with where it saved it and its size. Jsonreads JSON intoMap,List,String,Long,Double,Booleanandnull. It trusts what it is given, which is fine for the service's answers but no more.
Running it
Save the program as Box.java, then run it as it is, without compiling it first:
export PIQ_API_KEY=XXXX-XXXX-XXXX-XXXXX
java Box.java
It prints the template's labelled variables, the drawing's size and cut length, and saves box.pdf:
L: Length (Number, 400)
W: Width (Number, 300)
D: Depth (Number, 300)
Size: 500 x 250, cut length: 1500
Saved box.pdf (0 warnings)
With a value the template can't use ("L": "long", say), it prints the problem and stops:
Error 400
One or more validation errors occurred.
Some of the values given can't be used.
L: L is a number.
With a key that isn't right it prints only Error 401: a 401 has no
problem description.
To try the storage part, register a storage (see Register a storage), put a template in
it as templates/box.piq, and set PIQ_STORAGE to the storage's name. The template is then read from your storage,
and the PDF is also saved to it as drawings/box.pdf, replacing any file already there.
See also: API, Inputs, Measure, Export a drawing, Save to your storage, Register a storage, Clients.