diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2026-07-29 16:45:00 -0230 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2026-07-29 16:45:00 -0230 |
| commit | e7bba51bb8bb41c462873c613bc6d4cf402a58d1 (patch) | |
| tree | a94f91656e52aaf5c8edb5aed02679bae8efab11 /lulu.go | |
| parent | 95acc996fdcc6221741a3072633c5753e88d0f07 (diff) | |
| download | lulu-e7bba51bb8bb41c462873c613bc6d4cf402a58d1.zip | |
sandbox and production client constructors
Previously this was a package-level configuration with Sandbox() and
Production() controlling the global ApiUrl variable. Now it's controlled
per-client.
Diffstat (limited to 'lulu.go')
| -rw-r--r-- | lulu.go | 93 |
1 files changed, 46 insertions, 47 deletions
@@ -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 { |