aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/lulu/main.go6
-rw-r--r--cost_test.go2
-rw-r--r--cover_test.go8
-rw-r--r--interior_test.go10
-rw-r--r--job_test.go4
-rw-r--r--lulu.go93
-rw-r--r--lulu_test.go5
-rw-r--r--print_test.go6
8 files changed, 68 insertions, 66 deletions
diff --git a/cmd/lulu/main.go b/cmd/lulu/main.go
index 0609279..45a5bef 100644
--- a/cmd/lulu/main.go
+++ b/cmd/lulu/main.go
@@ -24,7 +24,11 @@ func main() {
creds, err := readCreds(cli.Sandbox)
ctx.FatalIfErrorf(err)
- clnt, err := lulu.NewClient(context.Background(), creds)
+ newClient := lulu.NewClient
+ if cli.Sandbox {
+ newClient = lulu.NewSandboxClient
+ }
+ clnt, err := newClient(context.Background(), creds)
ctx.FatalIfErrorf(err)
err = ctx.Run(cli.Globals, clnt)
diff --git a/cost_test.go b/cost_test.go
index a02c125..fe5da83 100644
--- a/cost_test.go
+++ b/cost_test.go
@@ -133,7 +133,7 @@ func TestUnmarshalCostResp(t *testing.T) {
}
func TestCost(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
items := costReqSample.Items
addr := ShippingAddress{
City: "Lübeck",
diff --git a/cover_test.go b/cover_test.go
index fe40b69..4659f38 100644
--- a/cover_test.go
+++ b/cover_test.go
@@ -41,7 +41,7 @@ func TestUnmarshalCoverDimensions(t *testing.T) {
}
func TestCoverDimensions(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
mfg := PkgId{
UsTrade,
Mono,
@@ -101,7 +101,7 @@ func TestUnmarshalCoverValidation(t *testing.T) {
}
func TestStartCoverValidation(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
mfg := PkgId{
UsTrade,
Mono,
@@ -118,7 +118,7 @@ func TestStartCoverValidation(t *testing.T) {
}
func TestGetCoverValidation(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
mfg := PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Gloss, NoLinen, NoFoil}
id, err := c.StartCoverValidation(coverUrl, mfg, 210)
require.NoError(t, err)
@@ -135,7 +135,7 @@ func TestGetCoverValidation(t *testing.T) {
}
func TestValidateCover(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
mfg := PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Gloss, NoLinen, NoFoil}
diff --git a/interior_test.go b/interior_test.go
index f32d16e..eb87924 100644
--- a/interior_test.go
+++ b/interior_test.go
@@ -52,7 +52,7 @@ func TestUnmarshalInteriorValidation(t *testing.T) {
}
func TestStartInteriorValidation(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
mfg := PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Gloss, NoLinen, NoFoil}
id, err := c.StartInteriorValidation(interiorUrl, mfg)
require.NoError(t, err)
@@ -60,14 +60,14 @@ func TestStartInteriorValidation(t *testing.T) {
}
func TestStartInteriorValidationBasic(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
id, err := c.StartInteriorValidationBasic(interiorUrl)
require.NoError(t, err)
require.NotZero(t, id)
}
func TestGetInteriorValidation(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
mfg := PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Gloss, NoLinen, NoFoil}
id, err := c.StartInteriorValidation(interiorUrl, mfg)
require.NoError(t, err)
@@ -84,7 +84,7 @@ func TestGetInteriorValidation(t *testing.T) {
}
func TestValidateInterior(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
mfg := PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Gloss, NoLinen, NoFoil}
@@ -94,7 +94,7 @@ func TestValidateInterior(t *testing.T) {
}
func TestValidateInteriorBasic(t *testing.T) {
- c := newClient(t)
+ c := tNewClient(t)
ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
val, err := c.ValidateInteriorBasic(ctx, interiorUrl)
diff --git a/job_test.go b/job_test.go
index 27b371d..cf4fc2c 100644
--- a/job_test.go
+++ b/job_test.go
@@ -29,7 +29,7 @@ func TestJob(t *testing.T) {
shipOpt := Mail
items := []Printable{printableSample}
- c := newClient(t)
+ c := tNewClient(t)
job1, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, items)
require.NoError(t, err)
require.NotZero(t, job1.Id)
@@ -47,7 +47,7 @@ func TestJob(t *testing.T) {
func TestJobs(t *testing.T) {
// Create some jobs
- c := newClient(t)
+ c := tNewClient(t)
jobParams := []struct {
contact string
extid string
diff --git a/lulu.go b/lulu.go
index c3838dc..f79ebe0 100644
--- a/lulu.go
+++ b/lulu.go
@@ -15,11 +15,11 @@ import (
)
const (
- SandboxUrl = "https://api.sandbox.lulu.com/"
- ProductionUrl = "https://api.lulu.com/"
+ sandbox = "https://api.sandbox.lulu.com/"
+ production = "https://api.lulu.com/"
- ProductionApiKeyPage = "https://developers.lulu.com/user-profile/api-keys"
- SandboxApiKeyPage = "https://developers.sandbox.lulu.com/user-profile/api-keys"
+ productionKeyPage = "https://developers.lulu.com/user-profile/api-keys"
+ sandboxKeyPage = "https://developers.sandbox.lulu.com/user-profile/api-keys"
PollPeriod = time.Second
@@ -31,31 +31,11 @@ const (
printJobsPath = "/print-jobs"
)
-// ApiUrl is the location of the API server. It is set to the sandbox
-// environment by default; change it to the production environment when
-// you are ready to deploy.
-var ApiUrl = SandboxUrl
-
-// Sandbox sets ApiUrl to SandboxApiUrl so that subsequent requests will
-// be sent to the sandbox API server.
-func Sandbox() { ApiUrl = SandboxUrl }
-
-// Production sets ApiUrl to ProductionApiUrl so that subsequent requests
-// will be sent to the production API server.
-func Production() { ApiUrl = ProductionUrl }
-
-// ApiKeyPage returns the URL of the page where you can generate a
-// client-key and client-secret to use for authentication.
-func ApiKeyPage() string {
- if ApiUrl == SandboxUrl {
- return SandboxApiKeyPage
- }
- return ProductionApiKeyPage
-}
-
type Client struct {
- ctx context.Context
- c *http.Client
+ ctx context.Context
+ c *http.Client
+ apiUrl string
+ keyPageUrl string
}
// Credentials contains the client-key and client-secret used to
@@ -64,10 +44,23 @@ type Credentials struct {
Key, Secret string
}
-// NewClient returns a client that will use the given credentials to
-// connect to the API server.
+// NewClient creates a client that will use creds to connect to the
+// production API server.
+//
+// ctx is used by the OAuth2 client. If you don't care about this, use
+// [context.Background]. See [clientcredentials.Config.Client].
func NewClient(ctx context.Context, creds Credentials) (*Client, error) {
- tokenUrl, err := url.JoinPath(ApiUrl, tokenPath)
+ return newClient(ctx, creds, production, productionKeyPage)
+}
+
+// NewClient creates a client that will connect to the sandbox API
+// server. See [NewClient].
+func NewSandboxClient(ctx context.Context, creds Credentials) (*Client, error) {
+ return newClient(ctx, creds, sandbox, sandboxKeyPage)
+}
+
+func newClient(ctx context.Context, creds Credentials, apiUrl, keyPageUrl string) (*Client, error) {
+ tokenUrl, err := url.JoinPath(apiUrl, tokenPath)
if err != nil {
return nil, pkgErrf(err, "error creating client")
}
@@ -77,7 +70,7 @@ func NewClient(ctx context.Context, creds Credentials) (*Client, error) {
ClientSecret: creds.Secret,
TokenURL: tokenUrl,
}
- return &Client{ctx, cfg.Client(ctx)}, nil
+ return &Client{ctx, cfg.Client(ctx), apiUrl, keyPageUrl}, nil
}
// Context returns the client's context.
@@ -85,8 +78,8 @@ func (c *Client) Context() context.Context { return c.ctx }
// ValidateInterior starts a server-side validation job for the given
// interior file and polls its status until it finishes or the context
-// expires. See also: StartInteriorValidation() and
-// GetInteriorValidation().
+// expires. See [Client.StartInteriorValidation], [Client.GetInteriorValidation],
+// and [PollPeriod].
func (c *Client) ValidateInterior(ctx context.Context, srcUrl string, mfg PkgId) (InteriorValidation, error) {
id, err := c.StartInteriorValidation(srcUrl, mfg)
if err != nil {
@@ -95,7 +88,7 @@ func (c *Client) ValidateInterior(ctx context.Context, srcUrl string, mfg PkgId)
return c.pollInteriorValidation(ctx, id, InteriorStatusNormalized)
}
-// ValidateInteriorBasic is like ValidateInterior but without the
+// ValidateInteriorBasic is like [Client.ValidateInterior] but without the
// optional pod_package_id.
func (c *Client) ValidateInteriorBasic(ctx context.Context, srcUrl string) (InteriorValidation, error) {
id, err := c.StartInteriorValidationBasic(srcUrl)
@@ -116,7 +109,7 @@ func (c *Client) pollInteriorValidation(ctx context.Context, id uint, wantStatus
// StartInteriorValidation starts a server-side validation job for the
// interior file located at srcUrl using manufacturing settings given by
-// mfg. It returns the ID of the job. Use GetInteriorValidation() to poll
+// mfg. It returns the ID of the job. Use [Client.GetInteriorValidation] to poll
// the status of the job.
//
// https://api.lulu.com/docs/#tag/Files-validation/operation/Validate-Interior_create
@@ -128,7 +121,7 @@ func (c *Client) StartInteriorValidation(srcUrl string, mfg PkgId) (uint, error)
return id, nil
}
-// StartInteriorValidationBasic is like StartInteriorValidation but
+// StartInteriorValidationBasic is like [Client.StartInteriorValidation] but
// without the optional pod_package_id.
//
// https://api.lulu.com/docs/#tag/Files-validation/operation/Validate-Interior_create
@@ -177,9 +170,10 @@ func (c *Client) CoverDimensions(mfg PkgId, npages uint, unit Unit) (CoverDimens
return dims, nil
}
-// ValidateCover starts a server-side validation job for the given cover
-// file and polls its status until it finishes or the context expires.
-// See also: StartCoverValidation() and GetCoverValidation().
+// ValidateCover starts a server-side validation job for the given
+// cover file and polls its status until it finishes or the context
+// expires. See [Client.StartCoverValidation],
+// [Client.GetCoverValidation], and [PollPeriod].
func (c *Client) ValidateCover(ctx context.Context, srcUrl string, mfg PkgId, npages uint) (CoverValidation, error) {
id, err := c.StartCoverValidation(srcUrl, mfg, npages)
if err != nil {
@@ -191,10 +185,11 @@ func (c *Client) ValidateCover(ctx context.Context, srcUrl string, mfg PkgId, np
})
}
-// StartCoverValidation starts a server-side validation job for the cover
-// file located at srcUrl, returning the job ID. mfg is the manufacturing
-// settings of the book, and npages is the number of interior pages. Use
-// GetCoverValidation() to poll the status of the job.
+// StartCoverValidation starts a server-side validation job for the
+// cover file located at srcUrl, returning the job ID. mfg is the
+// manufacturing settings of the book, and npages is the number of
+// interior pages. Use [Client.GetCoverValidation] to poll the status
+// of the job.
//
// https://api.lulu.com/docs/#tag/Files-validation/operation/Validate-Cover_create
func (c *Client) StartCoverValidation(srcUrl string, mfg PkgId, npages uint) (uint, error) {
@@ -384,7 +379,7 @@ func (c *Client) getDecode(path string, v any) error {
// getQueryDecode sends a GET path?query request and unmarshals the response into v.
func (c *Client) getQueryDecode(path string, query url.Values, v any) error {
- url, err := url.JoinPath(ApiUrl, path)
+ url, err := c.url(path)
if err != nil {
return err
}
@@ -419,7 +414,7 @@ func (c *Client) post(path string, payload any) (*http.Response, error) {
if err != nil {
return nil, errEncReq{payload, path, err}
}
- url, err := url.JoinPath(ApiUrl, path)
+ url, err := c.url(path)
if err != nil {
return nil, err
}
@@ -435,7 +430,7 @@ func (c *Client) putDecode(path string, payload any, v any) error {
if err != nil {
return errEncReq{payload, path, err}
}
- url, err := url.JoinPath(ApiUrl, path)
+ url, err := c.url(path)
if err != nil {
return err
}
@@ -458,6 +453,10 @@ func (c *Client) putDecode(path string, payload any, v any) error {
return decodeResponse(resp, v)
}
+func (c *Client) url(path string) (string, error) {
+ return url.JoinPath(c.apiUrl, path)
+}
+
func decodeResponse(resp *http.Response, v any) error {
body, err := io.ReadAll(resp.Body)
if err != nil {
diff --git a/lulu_test.go b/lulu_test.go
index 063d5aa..6791680 100644
--- a/lulu_test.go
+++ b/lulu_test.go
@@ -29,17 +29,16 @@ var (
func TestMain(m *testing.M) {
flag.BoolVar(&Debug, "d", false, "Print debug info to stderr.")
flag.Parse()
- Sandbox()
m.Run()
}
-func newClient(t *testing.T) *Client {
+func tNewClient(t *testing.T) *Client {
t.Helper()
creds := Credentials{
Key: strings.TrimSpace(clientKey),
Secret: strings.TrimSpace(clientSecret),
}
- c, err := NewClient(t.Context(), creds)
+ c, err := NewSandboxClient(t.Context(), creds)
require.NoError(t, err)
return c
}
diff --git a/print_test.go b/print_test.go
index 23d54c8..9115c1a 100644
--- a/print_test.go
+++ b/print_test.go
@@ -171,7 +171,7 @@ func TestPrint(t *testing.T) {
shipOpt := Mail
item := printableSample
- c := newClient(t)
+ c := tNewClient(t)
startTime := time.Now()
job, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, []Printable{item})
require.NoError(t, err)
@@ -205,7 +205,7 @@ func TestReprint(t *testing.T) {
printItem := printableSample
// Create print job
- c := newClient(t)
+ c := tNewClient(t)
job1, err := c.Print(contact, "print", productionDelay, addr, shipOpt, []Printable{printItem})
require.NoError(t, err)
id1 := job1.Id
@@ -242,7 +242,7 @@ func TestReprint(t *testing.T) {
func TestCancel(t *testing.T) {
// Print
- c := newClient(t)
+ c := tNewClient(t)
contact := MustParseEmailAddress("test@test.com")
jobEid := "cancel-test"
productionDelay := 120 * time.Minute