aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--err.go19
-rw-r--r--lulu.go51
-rw-r--r--print_test.go8
-rw-r--r--query.go45
4 files changed, 106 insertions, 17 deletions
diff --git a/err.go b/err.go
index 58984dc..30c7f8f 100644
--- a/err.go
+++ b/err.go
@@ -18,6 +18,7 @@ func pkgErrf(err error, format string, a ...any) error {
err)
}
+// error encoding request
type errEncReq struct {
payload any
path string
@@ -28,17 +29,19 @@ func (e errEncReq) Error() string {
return fmt.Sprintf("error encoding request body %v for %s: %v", e.payload, e.path, e.error)
}
-type errResp struct {
+// server responded with the wrong status
+type errRespStatus struct {
*http.Response
}
-func (e errResp) Error() string {
+func (e errRespStatus) Error() string {
resp := e.Response
req := resp.Request
body, _ := io.ReadAll(resp.Body)
return fmt.Sprintf("%s %s: %s: %s", req.Method, req.URL, resp.Status, body)
}
+// error reading response
type errReadResp struct {
*http.Response
error
@@ -50,6 +53,7 @@ func (e errReadResp) Error() string {
req.Method, req.URL, e.error)
}
+// error decoding response
type errDecResp struct {
*http.Response
body []byte
@@ -61,3 +65,14 @@ func (e errDecResp) Error() string {
return fmt.Sprintf("%s %s: error decoding response body `%s`: %v",
req.Method, req.URL, string(e.body), e.error)
}
+
+// server returned a bad response
+type errResp struct {
+ *http.Response
+ error
+}
+
+func (e errResp) Error() string {
+ req := e.Response.Request
+ return fmt.Sprintf("%s %s: bad response: %v", req.Method, req.URL, e.error)
+}
diff --git a/lulu.go b/lulu.go
index a7a6cdd..197380d 100644
--- a/lulu.go
+++ b/lulu.go
@@ -24,7 +24,7 @@ const (
coverDimensionsPath = "/cover-dimensions"
validateCoverPath = "/validate-cover"
printJobCostPath = "/print-job-cost-calculations"
- printJobPath = " /print-jobs"
+ printJobsPath = "/print-jobs"
)
// ApiUrl is the location of the API server. It is set to the sandbox
@@ -180,28 +180,63 @@ func (c *Client) PrintJobCost(items []PrintJobCostLineItem, addr ShippingAddress
//
// https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_list
func (c *Client) PrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) {
- //q := parsePrintJobQueries(queries)
- //page := 1
+ var q printJobQueries
+ q.apply(queries...)
+ qvals := q.vals()
- // TODO
- return nil, fmt.Errorf("not implemented")
+ verify := func(v any) error {
+ resp := v.(*getPrintJobsResp)
+ if int(resp.Count) != len(resp.Results) {
+ return fmt.Errorf("count (%d) != len(results) (%d)", resp.Count, len(resp.Results))
+ }
+ return nil
+ }
+
+ var jobs []PrintJob
+ for page := 1; ; page++ {
+ qvals.Set("page", fmt.Sprint(page))
+ var resp getPrintJobsResp
+ if err := c.getQueryDecodeVerify(printJobsPath, qvals, &resp, verify); err != nil {
+ return jobs, pkgErr(err)
+ }
+ if len(resp.Results) > 0 {
+ jobs = append(jobs, resp.Results...)
+ } else {
+ return jobs, nil
+ }
+ }
}
// getDecode sends a GET request and unmarshals the response.
func (c *Client) getDecode(path string, v any) error {
+ verify := func(v any) error { return nil }
+ return c.getQueryDecodeVerify(path, nil, v, verify)
+}
+
+// getDecodeVerify sends a GET /path/?query request and unmarshals and
+// verifies the response.
+func (c *Client) getQueryDecodeVerify(path string, query url.Values, v any, verify func(any) error) error {
url, err := url.JoinPath(ApiUrl, path)
if err != nil {
return err
}
+ url += "?" + query.Encode()
+
resp, err := c.c.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- return errResp{resp}
+ return errRespStatus{resp}
}
- return decodeResponse(resp, v)
+ if err := decodeResponse(resp, v); err != nil {
+ return err
+ }
+ if err := verify(v); err != nil {
+ return errResp{resp, err}
+ }
+ return nil
}
// postDecode sends a POST request and unmarshals the response.
@@ -212,7 +247,7 @@ func (c *Client) postDecode(path string, payload any, wantStatus int, v any) err
}
defer resp.Body.Close()
if resp.StatusCode != wantStatus {
- return errResp{resp}
+ return errRespStatus{resp}
}
return decodeResponse(resp, v)
}
diff --git a/print_test.go b/print_test.go
index 9e51095..d14c529 100644
--- a/print_test.go
+++ b/print_test.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/shopspring/decimal"
+ "github.com/stretchr/testify/require"
)
//go:embed testdata/getprintjobsresp.json
@@ -113,3 +114,10 @@ func TestUnmarshalGetPrintJobsResp(t *testing.T) {
requireUnmarshalJsonEq(t, want, getPrintJobsRespJson)
}
+
+func TestPrintJobs(t *testing.T) {
+ t.Fail() // TODO: create a few print jobs and retrieve them
+ c := newClient(t)
+ _, err := c.PrintJobs()
+ require.NoError(t, err)
+}
diff --git a/query.go b/query.go
index cf56bdd..3611958 100644
--- a/query.go
+++ b/query.go
@@ -1,6 +1,12 @@
package lulu
-import "time"
+import (
+ "fmt"
+ "net/url"
+ "time"
+)
+
+type PrintJobQuery func(*printJobQueries)
type printJobQueries struct {
creatAfter, creatBefore, modAfter, modBefore *time.Time
@@ -9,15 +15,40 @@ type printJobQueries struct {
excludeLineItems bool
}
-func parsePrintJobQueries(qs []PrintJobQuery) printJobQueries {
- var q printJobQueries
- for i := range qs {
- qs[i](&q)
+func (q *printJobQueries) apply(f ...PrintJobQuery) {
+ for i := range f {
+ f[i](q)
}
- return q
}
-type PrintJobQuery func(*printJobQueries)
+func (q *printJobQueries) vals() url.Values {
+ v := url.Values{}
+ if q.creatAfter != nil {
+ v.Set("created_after", q.creatAfter.UTC().Format(time.RFC3339))
+ }
+ if q.creatBefore != nil {
+ v.Set("created_before", q.creatBefore.UTC().Format(time.RFC3339))
+ }
+ if q.modAfter != nil {
+ v.Set("modified_after", q.modAfter.UTC().Format(time.RFC3339))
+ }
+ if q.modBefore != nil {
+ v.Set("modified_before", q.modBefore.UTC().Format(time.RFC3339))
+ }
+ if q.status != nil {
+ v.Set("status", string(*q.status))
+ }
+ if q.id != nil {
+ v.Set("id", fmt.Sprint(*q.id))
+ }
+ if q.orderId != nil {
+ v.Set("order_id", fmt.Sprint(*q.orderId))
+ }
+ if q.excludeLineItems {
+ v.Set("exclude_line_items", "true")
+ }
+ return v
+}
// Include only print jobs created after t.
func FilterCreatedAfter(t time.Time) PrintJobQuery {