Go
一个 Go 程序,仅使用标准库(net/http 和 encoding/json)调用 API。它列出模板中带标签的变量,用给定的值测量所生成的图纸,并将其导出为 PDF。
下载:main.go。
// main.go: list a template's inputs, measure it and export it as a PDF with the PiQ SaaS API.
// Go 1.18 or later; the standard library only.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
)
const template = "L=400,\"Length\"\nW=300,\"Width\"\nP1=[L,0]\nP2=[L,W]\nP3=[0,W]\nPOLYLINE @1,[P0,P1,P2,P3],TRUE,0"
var key, base = os.Getenv("PIQ_API_KEY"), strings.TrimSuffix(os.Getenv("PIQ_URL"), "/")
// Request is the JSON body of a request: a template (or its source), values and, to save, where to.
type Request struct {
Template string `json:"template,omitempty"`
Source *Object `json:"source,omitempty"`
Values map[string]any `json:"values,omitempty"`
Destination *Object `json:"destination,omitempty"`
Overwrite bool `json:"overwrite,omitempty"`
}
// Object is an object in one of your registered storages.
type Object struct {
Storage string `json:"storage"`
Key string `json:"key"`
}
func post(path string, body Request) *http.Response {
data, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", base+path, bytes.NewReader(data))
req.Header.Set("X-API-KEY", key)
req.Header.Set("Content-Type", "application/json")
response, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if response.StatusCode >= 300 {
fmt.Fprintln(os.Stderr, "Error", response.Status)
var problem struct {
Title, Detail string
Errors map[string][]string
}
text, _ := io.ReadAll(response.Body) // a 401 has no body
if len(text) > 0 && json.Unmarshal(text, &problem) != nil {
problem.Detail = string(text)
}
for _, line := range []string{problem.Title, problem.Detail} {
if line != "" {
fmt.Fprintln(os.Stderr, line)
}
}
for field, messages := range problem.Errors {
fmt.Fprintf(os.Stderr, " %s: %s\n", field, strings.Join(messages, " "))
}
os.Exit(1)
}
return response
}
func postFor(path string, body Request, result any) {
response := post(path, body)
defer response.Body.Close()
json.NewDecoder(response.Body).Decode(result)
}
func main() {
if key == "" {
fmt.Fprintln(os.Stderr, "Set PIQ_API_KEY to your API key.")
os.Exit(1)
}
if base == "" {
base = "https://saas.parametriq.co.uk"
}
storage := os.Getenv("PIQ_STORAGE") // optional: a registered storage's name
// the template itself, or (optional) where it is kept in your storage
request := Request{Template: template}
if storage != "" {
request = Request{Source: &Object{storage, "templates/box.piq"}}
}
// 1. The template's labelled variables
var inputs struct {
Inputs []struct {
Name, Label, Kind string
Value any
}
}
postFor("/inputs", request, &inputs)
for _, i := range inputs.Inputs {
fmt.Printf("%s: %s (%s, %v)\n", i.Name, i.Label, i.Kind, i.Value)
}
// 2. The drawing measured, with the values given
var m struct {
Width, Height float64
Layers []struct {
Layer string
Length float64
}
}
request.Values = map[string]any{"L": 500, "W": 250}
postFor("/measure", request, &m)
cut := 0.0
for _, layer := range m.Layers {
if layer.Layer == "Cut" {
cut = layer.Length
}
}
fmt.Printf("Size: %v x %v, cut length: %v\n", m.Width, m.Height, cut)
// 3. The drawing as a PDF
pdf := post("/export/pdf", request)
data, _ := io.ReadAll(pdf.Body)
if err := os.WriteFile("box.pdf", data, 0o644); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("Saved box.pdf (%s warnings)\n", pdf.Header.Get("X-PiQ-Warnings"))
// 4. Optional: the PDF saved to your storage instead of downloaded
if storage != "" {
request.Destination, request.Overwrite = &Object{storage, "drawings/box.pdf"}, true
var saved struct {
Storage, Key string
Size int64
}
postFor("/save/pdf", request, &saved)
fmt.Printf("Saved %s to %s (%d bytes)\n", saved.Key, saved.Storage, saved.Size)
}
}
工作原理
- 设置。 密钥来自
PIQ_API_KEY,地址来自PIQ_URL(未设置时为服务本身的地址),还可以选择从PIQ_STORAGE读取存储名称。没有密钥时,程序会立即停止。 Request是每个请求的 JSON 正文。其字段使用 API 的 camelCase 名称和omitempty标记,因此只发送已设置的字段:Template(模板的文本),或者在设置了PIQ_STORAGE时为Source(模板在您存储中的保存位置),然后是Values,保存时还有Destination和Overwrite。post将请求以 JSON 形式发送到某个操作,并在X-API-KEY标头中携带密钥。成功的响应会交回以供读取。任何其他响应会先打印其状态,然后打印其问题描述:其Title、Detail,以及Errors中的每一项,即字段及其问题。401没有正文,因此没有更多内容可打印;不是 JSON 的正文按原样打印。然后程序以1退出。postFor发送请求,并将 JSON 响应解码到只包含此处所用字段的结构体中(encoding/json无需标记即可将width匹配到Width)。- Inputs。
POST /inputs只需要模板。其每个Inputs都有名称、标签、类型和值;值的类型为any,因为它可能是数字、文本,或true、false。 - Measure。 将这些值添加到请求中,
POST /measure即可给出图纸的宽度和高度,以及按图层统计的线长;切割长度即Cut图层上的长度。 - Export。
POST /export/pdf返回 PDF 本身而不是 JSON:其正文被写入box.pdf。X-PiQ-Warnings标头表示有多少内容无法按原样导出。 - Save(可选)。 指定了存储时,会添加
Destination和Overwrite,POST /save/pdf会导出同一图纸并将其保存到您的存储中,替换已存在的文件。响应会说明保存位置及文件大小。
运行
将程序保存为单独文件夹中的 main.go,然后:
export PIQ_API_KEY=XXXX-XXXX-XXXX-XXXXX
go run main.go
它会打印模板中带标签的变量、图纸的尺寸和切割长度,并保存 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)
如果给出模板无法使用的值(例如 "L": "long"),它会打印问题并停止:
Error 400 Bad Request
One or more validation errors occurred.
Some of the values given can't be used.
L: L is a number.
如果密钥不正确,它只会打印 Error 401 Unauthorized:401 没有问题描述。
要试用存储部分,请注册一个存储(请参阅注册存储),将模板以 templates/box.piq
放入其中,并将 PIQ_STORAGE 设置为该存储的名称。这样,模板将从您的存储中读取,
PDF 也会以 drawings/box.pdf 保存到其中,替换已存在的任何文件。