diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | cover.go | 55 | ||||
| -rw-r--r-- | cover_test.go | 57 | ||||
| -rw-r--r-- | interior.go | 45 | ||||
| -rw-r--r-- | interior_test.go | 122 | ||||
| -rw-r--r-- | lulu.go | 87 | ||||
| -rw-r--r-- | lulu_test.go | 165 |
7 files changed, 280 insertions, 253 deletions
@@ -1,4 +1,4 @@ -GEN = pkgid_gen.go +GEN = $(addsuffix _gen.go, cover interior pkgid) TEST = $(wildcard *_test.go) SRC = $(filter-out ${GEN} ${TEST}, $(wildcard *.go)) diff --git a/cover.go b/cover.go new file mode 100644 index 0000000..30be2d6 --- /dev/null +++ b/cover.go @@ -0,0 +1,55 @@ +package lulu + +import ( + "encoding/json" + "fmt" + "strconv" +) + +//go:generate go run github.com/yawnak/string-enumer -t Unit --text -o ./cover_gen.go . + +// Unit is a unit of length measurement. +type Unit string + +const ( + Points Unit = "pt" + Millimeters Unit = "mm" + Inches Unit = "inch" +) + +// coverDimensionsReq is the json body of a /cover-dimensions/ request. +type coverDimensionsReq struct { + PkgId PkgId `json:"pod_package_id"` + NPages uint `json:"interior_page_count"` + Unit Unit `json:"unit"` +} + +type CoverDimensions struct { + Width, Height float64 + Unit Unit +} + +func (cd *CoverDimensions) UnmarshalJSON(data []byte) error { + s := string(data) + var alias struct { + Width, Height string + Unit Unit + } + if err := json.Unmarshal(data, &alias); err != nil { + return fmt.Errorf("malformed %T: %q: %w", cd, s, err) + } + + w, err := strconv.ParseFloat(alias.Width, 64) + if err != nil { + return fmt.Errorf("malformed %T.Width: %q: %w", cd, s, err) + } + h, err := strconv.ParseFloat(alias.Height, 64) + if err != nil { + return fmt.Errorf("malformed %T.Height: %q: %w", cd, s, err) + } + + cd.Width = w + cd.Height = h + cd.Unit = alias.Unit + return nil +} diff --git a/cover_test.go b/cover_test.go new file mode 100644 index 0000000..a0042c1 --- /dev/null +++ b/cover_test.go @@ -0,0 +1,57 @@ +package lulu + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMarshalCoverDimensionsReq(t *testing.T) { + t.Parallel() + requireMarshalJsonEq(t, `{ + "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX", + "interior_page_count": 210, + "unit": "pt"}`, + coverDimensionsReq{ + PkgId{ + UsTrade, + Mono, + Standard, + Perfect, + P60UncoatedWhite, + Matte, + NoLinen, + NoFoil}, + 210, + Points}) +} + +func TestUnmarshalCoverDimensions(t *testing.T) { + t.Parallel() + requireUnmarshalJsonEq(t, + CoverDimensions{123.4, 567.8, Points}, + `{"width": "123.400", "height": "567.800", "unit": "pt"}`) + requireUnmarshalJsonEq(t, + CoverDimensions{123.4, 567.8, Millimeters}, + `{"width": "123.400", "height": "567.800", "unit": "mm"}`) + requireUnmarshalJsonEq(t, + CoverDimensions{123.4, 567.8, Inches}, + `{"width": "123.400", "height": "567.800", "unit": "inch"}`) +} + +func TestCoverDimensions(t *testing.T) { + c := newClient(t) + mfg := PkgId{ + UsTrade, + Mono, + Standard, + Perfect, + P60UncoatedWhite, + Matte, + NoLinen, + NoFoil, + } + dims, err := c.CoverDimensions(mfg, 210, Points) + require.NoError(t, err) + require.Equal(t, CoverDimensions{920, 666, Points}, dims) +} diff --git a/interior.go b/interior.go new file mode 100644 index 0000000..19417e5 --- /dev/null +++ b/interior.go @@ -0,0 +1,45 @@ +package lulu + +//go:generate go run github.com/yawnak/string-enumer -t InteriorValidationStatus --text -o ./interior_gen.go . + +// InteriorValidationStatus is the status of a validation job for an +// interior file that is running on the server. +type InteriorValidationStatus string + +const ( + InteriorStatusNull InteriorValidationStatus = "NULL" // file validation is not started yet + InteriorStatusValidating InteriorValidationStatus = "VALIDATING" // file validation is still running + InteriorStatusValidated InteriorValidationStatus = "VALIDATED" // file validation finished without any errors + InteriorStatusNormalizing InteriorValidationStatus = "NORMALIZING" // file normalization (next step of validation, available only if pod_package_id is was passed in the payload) is still running + InteriorStatusNormalized InteriorValidationStatus = "NORMALIZED" // file normalization finished without any errors + InteriorStatusError InteriorValidationStatus = "ERROR" // file is invalid, list of errors is included in the response +) + +func (s InteriorValidationStatus) IsFinal() bool { + switch s { + case InteriorStatusValidated, InteriorStatusNormalized, InteriorStatusError: + return true + } + return false +} + +// validateInteriorReq is the json body of a /validate-interior/ request. +type validateInteriorReq struct { + SrcUrl string `json:"source_url"` + PkgId PkgId `json:"pod_package_id"` +} + +// validateInteriorReq is the json body of a /validate-interior/ request without the optional pod_package_id. +type validateInteriorBasicReq struct { + SrcUrl string `json:"source_url"` +} + +// InteriorValidationRecord contains the validation status of an interior file. +type InteriorValidationRecord struct { + Id uint + SrcUrl string `json:"source_url"` + NPages uint `json:"page_count"` + Errors string + Status InteriorValidationStatus + ValidPkgIds []PkgId `json:"valid_pod_package_ids"` +} diff --git a/interior_test.go b/interior_test.go new file mode 100644 index 0000000..ba3d948 --- /dev/null +++ b/interior_test.go @@ -0,0 +1,122 @@ +package lulu + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestMarshalValidateInteriorReq(t *testing.T) { + t.Parallel() + want := `{ + "source_url": "https://example.com/interior.pdf", + "pod_package_id": "0850X1100.BW.STD.LW.060UW444.MNG" +}` + req := validateInteriorReq{ + "https://example.com/interior.pdf", + PkgId{ + UsLetter, + Mono, + Standard, + LinenWrap, + P60UncoatedWhite, + Matte, + NavyLinen, + GoldFoil}, + } + requireMarshalJsonEq(t, want, req) +} + +func TestMarshalValidateInteriorBasicReq(t *testing.T) { + t.Parallel() + requireMarshalJsonEq(t, + `{"source_url": "https://example.com/interior.pdf"}`, + validateInteriorBasicReq{"https://example.com/interior.pdf"}) +} + +func TestUnmarshalInteriorValidationRecord(t *testing.T) { + t.Parallel() + data := `{ + "id": 1, + "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1", + "page_count": 210, + "errors": null, + "status": "VALIDATING", + "valid_pod_package_ids": null +}` + want := InteriorValidationRecord{ + 1, + "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1", + 210, + "", + InteriorStatusValidating, + nil, + } + requireUnmarshalJsonEq(t, want, data) +} + +func TestValidateInterior(t *testing.T) { + c := newClient(t) + mfg := PkgId{ + UsTrade, + Mono, + Standard, + Perfect, + P60UncoatedWhite, + Gloss, + NoLinen, + NoFoil, + } + id, err := c.ValidateInterior(interiorUrl, mfg) + require.NoError(t, err) + require.NotZero(t, id) + // It seems the server doesn't populate most of the response + // fields, but we just need the ID anyway. +} + +func TestValidateInteriorBasic(t *testing.T) { + c := newClient(t) + id, err := c.ValidateInteriorBasic(interiorUrl) + require.NoError(t, err) + require.NotZero(t, id) + // It seems the server doesn't populate most of the response + // fields, but we just need the ID anyway. +} + +func TestGetInteriorValidation(t *testing.T) { + c := newClient(t) + + // Start validation job + id, err := c.ValidateInteriorBasic(interiorUrl) + require.NoError(t, err) + + // Poll until done + timeout := 15 * time.Second + period := time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + timer := time.NewTimer(period) + for { + select { + case <-timer.C: + rec, err := c.GetInteriorValidation(id) + require.NoError(t, err) + if rec.Status.IsFinal() { + require.Equal(t, InteriorStatusValidated, rec.Status) + + require.Equal(t, id, rec.Id) + require.Equal(t, interiorUrl, rec.SrcUrl) + require.Equal(t, uint(210), rec.NPages) + require.Empty(t, rec.Errors) + require.NotEmpty(t, rec.ValidPkgIds) + return + } + timer.Reset(period) + case <-ctx.Done(): + t.Errorf("status still not finalized after %v", timeout) + return + } + } +} @@ -9,7 +9,6 @@ import ( "io" "net/http" "net/url" - "strconv" "golang.org/x/oauth2/clientcredentials" ) @@ -29,92 +28,6 @@ const ( // you are ready to deploy. var ApiUrl = SandboxUrl -// Unit is a unit of length measurement. -type Unit string - -const ( - Points Unit = "pt" - Millimeters Unit = "mm" - Inches Unit = "inch" -) - -type ValidationStatus string - -const ( - StatusNull ValidationStatus = "NULL" // file validation is not started yet - StatusValidating ValidationStatus = "VALIDATING" // file validation is still running - StatusValidated ValidationStatus = "VALIDATED" // file validation finished without any errors - StatusNormalizing ValidationStatus = "NORMALIZING" // file normalization (next step of validation, available only if pod_package_id is was passed in the payload) is still running - StatusNormalized ValidationStatus = "NORMALIZED" // file normalization finished without any errors - StatusError ValidationStatus = "ERROR" // file is invalid, list of errors is included in the response -) - -func (s ValidationStatus) IsFinal() bool { - switch s { - case StatusValidated, StatusNormalized, StatusError: - return true - } - return false -} - -// validateInteriorReq is the json body of a /validate-interior/ request. -type validateInteriorReq struct { - SrcUrl string `json:"source_url"` - PkgId PkgId `json:"pod_package_id"` -} - -// validateInteriorReq is the json body of a /validate-interior/ request without the optional pod_package_id. -type validateInteriorBasicReq struct { - SrcUrl string `json:"source_url"` -} - -// InteriorValidationRecord contains the validation status of an interior file. -type InteriorValidationRecord struct { - Id uint - SrcUrl string `json:"source_url"` - NPages uint `json:"page_count"` - Errors string - Status ValidationStatus - ValidPkgIds []PkgId `json:"valid_pod_package_ids"` -} - -// coverDimensionsReq is the json body of a /cover-dimensions/ request. -type coverDimensionsReq struct { - PkgId PkgId `json:"pod_package_id"` - NPages uint `json:"interior_page_count"` - Unit Unit `json:"unit"` -} - -type CoverDimensions struct { - Width, Height float64 - Unit Unit -} - -func (cd *CoverDimensions) UnmarshalJSON(data []byte) error { - s := string(data) - var alias struct { - Width, Height string - Unit Unit - } - if err := json.Unmarshal(data, &alias); err != nil { - return fmt.Errorf("malformed %T: %q: %w", cd, s, err) - } - - w, err := strconv.ParseFloat(alias.Width, 64) - if err != nil { - return fmt.Errorf("malformed %T.Width: %q: %w", cd, s, err) - } - h, err := strconv.ParseFloat(alias.Height, 64) - if err != nil { - return fmt.Errorf("malformed %T.Height: %q: %w", cd, s, err) - } - - cd.Width = w - cd.Height = h - cd.Unit = alias.Unit - return nil -} - type Client struct { c *http.Client } diff --git a/lulu_test.go b/lulu_test.go index 513d590..05d37ad 100644 --- a/lulu_test.go +++ b/lulu_test.go @@ -1,13 +1,11 @@ package lulu import ( - "context" "encoding/json" "fmt" "os" "strings" "testing" - "time" "github.com/stretchr/testify/require" ) @@ -44,169 +42,6 @@ func TestMain(m *testing.M) { m.Run() } -func TestMarshalValidateInteriorReq(t *testing.T) { - t.Parallel() - want := `{ - "source_url": "https://example.com/interior.pdf", - "pod_package_id": "0850X1100.BW.STD.LW.060UW444.MNG" -}` - req := validateInteriorReq{ - "https://example.com/interior.pdf", - PkgId{ - UsLetter, - Mono, - Standard, - LinenWrap, - P60UncoatedWhite, - Matte, - NavyLinen, - GoldFoil}, - } - requireMarshalJsonEq(t, want, req) -} - -func TestMarshalValidateInteriorBasicReq(t *testing.T) { - t.Parallel() - requireMarshalJsonEq(t, - `{"source_url": "https://example.com/interior.pdf"}`, - validateInteriorBasicReq{"https://example.com/interior.pdf"}) -} - -func TestUnmarshalInteriorValidationRecord(t *testing.T) { - t.Parallel() - data := `{ - "id": 1, - "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1", - "page_count": 210, - "errors": null, - "status": "VALIDATING", - "valid_pod_package_ids": null -}` - want := InteriorValidationRecord{ - 1, - "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1", - 210, - "", - StatusValidating, - nil, - } - requireUnmarshalJsonEq(t, want, data) -} - -func TestValidateInterior(t *testing.T) { - c := newClient(t) - mfg := PkgId{ - UsTrade, - Mono, - Standard, - Perfect, - P60UncoatedWhite, - Gloss, - NoLinen, - NoFoil, - } - id, err := c.ValidateInterior(interiorUrl, mfg) - require.NoError(t, err) - require.NotZero(t, id) - // It seems the server doesn't populate most of the response - // fields, but we just need the ID anyway. -} - -func TestValidateInteriorBasic(t *testing.T) { - c := newClient(t) - id, err := c.ValidateInteriorBasic(interiorUrl) - require.NoError(t, err) - require.NotZero(t, id) - // It seems the server doesn't populate most of the response - // fields, but we just need the ID anyway. -} - -func TestGetInteriorValidation(t *testing.T) { - c := newClient(t) - - // Start validation job - id, err := c.ValidateInteriorBasic(interiorUrl) - require.NoError(t, err) - - // Poll until done - timeout := 15 * time.Second - period := time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - timer := time.NewTimer(period) - for { - select { - case <-timer.C: - rec, err := c.GetInteriorValidation(id) - require.NoError(t, err) - if rec.Status.IsFinal() { - require.Equal(t, StatusValidated, rec.Status) - - require.Equal(t, id, rec.Id) - require.Equal(t, interiorUrl, rec.SrcUrl) - require.Equal(t, uint(210), rec.NPages) - require.Empty(t, rec.Errors) - require.NotEmpty(t, rec.ValidPkgIds) - return - } - timer.Reset(period) - case <-ctx.Done(): - t.Errorf("status still not finalized after %v", timeout) - return - } - } -} - -func TestUnmarshalCoverDimensions(t *testing.T) { - t.Parallel() - requireUnmarshalJsonEq(t, - CoverDimensions{123.4, 567.8, Points}, - `{"width": "123.400", "height": "567.800", "unit": "pt"}`) - requireUnmarshalJsonEq(t, - CoverDimensions{123.4, 567.8, Millimeters}, - `{"width": "123.400", "height": "567.800", "unit": "mm"}`) - requireUnmarshalJsonEq(t, - CoverDimensions{123.4, 567.8, Inches}, - `{"width": "123.400", "height": "567.800", "unit": "inch"}`) -} - -func TestMarshalCoverDimensionsReq(t *testing.T) { - t.Parallel() - requireMarshalJsonEq(t, `{ - "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX", - "interior_page_count": 210, - "unit": "pt"}`, - coverDimensionsReq{ - PkgId{ - UsTrade, - Mono, - Standard, - Perfect, - P60UncoatedWhite, - Matte, - NoLinen, - NoFoil}, - 210, - Points}) -} - -func TestCoverDimensions(t *testing.T) { - c := newClient(t) - mfg := PkgId{ - UsTrade, - Mono, - Standard, - Perfect, - P60UncoatedWhite, - Matte, - NoLinen, - NoFoil, - } - dims, err := c.CoverDimensions(mfg, 210, Points) - require.NoError(t, err) - require.Equal(t, CoverDimensions{920, 666, Points}, dims) -} - func newClient(t *testing.T) *Client { t.Helper() c, err := NewClient(t.Context(), clientKey, clientSecret) |