aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2026-05-13 10:44:57 -0400
committerSam Anthony <sam@samanthony.xyz>2026-05-13 10:45:41 -0400
commit56523fc6a304c09f252449342b10c52f7836c4aa (patch)
tree07cd85a92ba3c1393805a938fbc27b8ad18162b5
parent520f392ca5207f5364bbed501615613e3485b84b (diff)
downloadlulu-56523fc6a304c09f252449342b10c52f7836c4aa.zip
create print job
-rw-r--r--Makefile2
-rw-r--r--cost.go1
-rw-r--r--email.go15
-rw-r--r--lulu.go66
-rw-r--r--lulu_test.go16
-rw-r--r--print.go68
-rw-r--r--print_test.go85
-rw-r--r--ship.go29
-rw-r--r--ship_test.go94
-rw-r--r--status.go (renamed from order.go)13
-rw-r--r--status_gen.go (renamed from order_gen.go)39
11 files changed, 335 insertions, 93 deletions
diff --git a/Makefile b/Makefile
index 079f77d..7914f12 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-GEN = $(addsuffix _gen.go, cover interior order pkgid ship)
+GEN = $(addsuffix _gen.go, cover interior pkgid ship status)
TEST = $(wildcard *_test.go)
SRC = $(filter-out ${GEN} ${TEST}, $(wildcard *.go))
diff --git a/cost.go b/cost.go
index 3d3c3dd..ac7ad7d 100644
--- a/cost.go
+++ b/cost.go
@@ -60,6 +60,7 @@ type printJobCostReq struct {
ShipOpt ShippingLevel `json:"shipping_option"`
}
+// TODO: just use ShippingAddress?
type printJobCostReqShipAddr struct {
City string `json:"city"`
Country string `json:"country_code"`
diff --git a/email.go b/email.go
index bdf4dce..61a80ab 100644
--- a/email.go
+++ b/email.go
@@ -5,8 +5,10 @@ import (
"net/mail"
)
-type EmailAddress struct {
- *mail.Address
+type EmailAddress mail.Address
+
+func (a EmailAddress) MarshalText() ([]byte, error) {
+ return []byte(a.Address), nil
}
func (a *EmailAddress) UnmarshalText(text []byte) error {
@@ -14,13 +16,16 @@ func (a *EmailAddress) UnmarshalText(text []byte) error {
if err != nil {
return err
}
- a.Address = addr
+ *a = EmailAddress(*addr)
return nil
}
func ParseEmailAddress(address string) (EmailAddress, error) {
addr, err := mail.ParseAddress(address)
- return EmailAddress{addr}, err
+ if err != nil {
+ return EmailAddress{}, err
+ }
+ return EmailAddress(*addr), nil
}
func MustParseEmailAddress(s string) EmailAddress {
@@ -28,5 +33,5 @@ func MustParseEmailAddress(s string) EmailAddress {
if err != nil {
panic(fmt.Sprintf("lulu: %v", err))
}
- return EmailAddress{addr}
+ return EmailAddress(*addr)
}
diff --git a/lulu.go b/lulu.go
index 197380d..c2d668d 100644
--- a/lulu.go
+++ b/lulu.go
@@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"os"
+ "time"
"golang.org/x/oauth2/clientcredentials"
)
@@ -175,11 +176,11 @@ func (c *Client) PrintJobCost(items []PrintJobCostLineItem, addr ShippingAddress
return resp.cost(), resp.AddressValidation, nil
}
-// PrintJobs retrieves a list of all print jobs that have been created,
-// filtered by a set of optional query parameters.
+// GetPrintJobs retrieves a list of all print jobs that have been
+// created, filtered by a set of optional query parameters.
//
// https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_list
-func (c *Client) PrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) {
+func (c *Client) GetPrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) {
var q printJobQueries
q.apply(queries...)
qvals := q.vals()
@@ -207,6 +208,62 @@ func (c *Client) PrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) {
}
}
+// Print creates a new print job.
+//
+// contact: Email address that should be contacted if questions
+// regarding the Print-Job arise. Lulu recommends to use the
+// email of a person who is responsible for placing the Print-Job
+// like a developer or business owner.
+//
+// externalId: Arbitrary string to identify and connect a print
+// job to your systems. Set it to an order number, a purchase
+// order or whatever else works for your particular use case.
+//
+// productionDelay: Delay before a newly created Print-Job is
+// sent to production. Minimum is 60 minutes, maximum is 2880
+// minutes (=48 hours). As most cancellation requests occur right
+// after an order has been placed, it makes sense to wait for
+// some time before sending an order to production. Once
+// production has started, orders cannot be canceled anymore.
+//
+// addr: The postal address of the customer that the order will
+// be sent to.
+//
+// shipOpt: Shipping method.
+//
+// items: List of books to print.
+//
+// https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_create
+func (c *Client) Print(contact EmailAddress, externalId string, productionDelay time.Duration, addr ShippingAddress, shipOpt ShippingLevel, items []Printable) (PrintJob, error) {
+ if err := verifyProductionDelay(productionDelay); err != nil {
+ return PrintJob{}, pkgErr(err)
+ }
+
+ req := printReq{
+ Contact: contact,
+ ExternalId: externalId,
+ LineItems: items,
+ ProductionDelayMins: uint(productionDelay.Round(time.Minute).Minutes()),
+ ShipAddr: addr,
+ ShipOpt: shipOpt,
+ }
+
+ var job PrintJob
+ err := c.postDecode(printJobsPath, req, http.StatusCreated, &job)
+ if err != nil {
+ return job, pkgErr(err)
+ }
+ return job, nil
+}
+
+func verifyProductionDelay(delay time.Duration) error {
+ if delay < MinProductionDelay || delay > MaxProductionDelay {
+ return fmt.Errorf("production delay %s out of range: must be [%s, %s]",
+ delay, MinProductionDelay, MaxProductionDelay)
+ }
+ return 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 }
@@ -261,6 +318,9 @@ func (c *Client) post(path string, payload any) (*http.Response, error) {
if err != nil {
return nil, err
}
+
+ debugf("POST %s request: `%s`\n", url, body)
+
return c.c.Post(url, "application/json", bytes.NewBuffer(body))
}
diff --git a/lulu_test.go b/lulu_test.go
index 613cc4b..ceb0990 100644
--- a/lulu_test.go
+++ b/lulu_test.go
@@ -70,6 +70,22 @@ func requireUnmarshalJsonEq[T any](t *testing.T, expected T, j string) {
require.Equal(t, expected, actual)
}
+// assert that t2 is after t1
+func requireAfter(t *testing.T, t2, t1 time.Time) {
+ t.Helper()
+ require.Truef(t, t2.After(t1), "%s not after %s", t2, t1)
+}
+
+// assert that dt2 is not before tt1
+func requireNotBeforeDate(t *testing.T, dt2, dt1 Date) {
+ t.Helper()
+ y2, m2, d2 := time.Time(dt2).UTC().Date()
+ y1, m1, d1 := time.Time(dt1).UTC().Date()
+ require.Truef(t, y2 >= y1 && m2 >= m1 && d2 >= d1, "%s is before %s",
+ time.Date(y2, m2, d2, 0, 0, 0, 0, time.UTC).Format(time.DateOnly),
+ time.Date(y1, m1, d1, 0, 0, 0, 0, time.UTC).Format(time.DateOnly))
+}
+
// poll periodically calls f() until it returns true or the deadline is exceeded.
func poll(t *testing.T, f func() bool) {
t.Helper()
diff --git a/print.go b/print.go
index 4302e6e..8532fff 100644
--- a/print.go
+++ b/print.go
@@ -11,6 +11,47 @@ const (
MaxProductionDelay = 48 * time.Hour
)
+type printReq struct {
+ Contact EmailAddress `json:"contact_email"`
+ ExternalId string `json:"external_id"`
+ LineItems []Printable `json:"line_items"`
+ ProductionDelayMins uint `json:"production_delay"`
+ ShipAddr ShippingAddress `json:"shipping_address"`
+ ShipOpt ShippingLevel `json:"shipping_level"`
+}
+
+// PrintableId uniquely identifies a book to be printed: a "printable". A
+// printable consists of a cover and an interior file as well as a
+// PodPkgId which specifies the manufacturing options.
+type PrintableId string
+
+// Printable is a book to be printed.
+type Printable struct {
+ // Arbitrary string to identify and connect a print job to your
+ // systems. Set it to an order number, a purchase order or
+ // whatever else works for your particular use case.
+ ExternalId string `json:"external_id"`
+
+ // Interior and cover files have to be specified with a URL from
+ // which Lulu can download the files. Using encoded basic
+ // authentication in the URL is ok. All files processed by Lulu
+ // will be validated and normalized before sending them to
+ // production. If problems with the file occur, the PrintJob will
+ // be rejected or cancelled and an error message will be
+ // displayed.
+ CoverUrl string `json:"cover"`
+ InteriorUrl string `json:"interior"`
+
+ // Manufacturing options.
+ Mfg PkgId `json:"pod_package_id"`
+
+ // The number of copies to print.
+ Quantity uint `json:"quantity"`
+
+ // The title of the book. Must not exceed 255 bytes.
+ Title string `json:"title"`
+}
+
type getPrintJobsResp struct {
Count uint `json:"count"`
Next string `json:"next"`
@@ -60,6 +101,8 @@ type PrintJob struct {
// ISO 3166-1 alpha-2 country code of the tax country determined for this job
TaxCountry string `json:"tax_country"`
+
+ Status PrintJobStatus `json:"status"`
}
func (pj *PrintJob) UnmarshalJSON(data []byte) error {
@@ -77,13 +120,7 @@ func (pj *PrintJob) UnmarshalJSON(data []byte) error {
return nil
}
-type EstimatedShippingDates struct {
- ArrivalMax Date `json:"arrival_max"`
- ArrivalMin Date `json:"arrival_min"`
- DispatchMax Date `json:"dispatch_max"`
- DispatchMin Date `json:"dispatch_min"`
-}
-
+// LineItem represents a book that should be printed: a "printable" for short.
type LineItem struct {
// Arbitrary string to identify and connect a print job to your
// systems. Set it to an order number, a purchase order or
@@ -98,7 +135,7 @@ type LineItem struct {
Mfg PkgId `json:"pod_package_id"`
// Id of the printable. It can be used instead of PrintableNormalization.
- PrintableId string `json:"printable_id"`
+ PrintableId PrintableId `json:"printable_id"`
// Normalization process of the cover and interior source files.
PrintableNormalization PrintableNormalization `json:"printable_normalization"`
@@ -121,6 +158,13 @@ type LineItem struct {
Carrier string `json:"carrier_name"`
}
+type EstimatedShippingDates struct {
+ ArrivalMax Date `json:"arrival_max"`
+ ArrivalMin Date `json:"arrival_min"`
+ DispatchMax Date `json:"dispatch_max"`
+ DispatchMin Date `json:"dispatch_min"`
+}
+
// PrintableNormalization represents the normalization processes of the
// interior and cover source files.
type PrintableNormalization struct {
@@ -146,7 +190,7 @@ type NormalizedFile struct {
type LineItemStatus struct {
Messages LineItemStatusMessages `json:"messages"`
- Status OrderStatus `json:"name"`
+ Status ItemStatus `json:"name"`
}
type LineItemStatusMessages struct {
@@ -191,3 +235,9 @@ func (msgs *LineItemStatusMessages) UnmarshalJSON(data []byte) error {
return nil
}
+
+type PrintJobStatus struct {
+ Changed time.Time `json:"changed"` // time of the last status change
+ Msg string `json:"message"`
+ Status OrderStatus `json:"name"`
+}
diff --git a/print_test.go b/print_test.go
index d14c529..ec4e455 100644
--- a/print_test.go
+++ b/print_test.go
@@ -75,49 +75,70 @@ func TestUnmarshalGetPrintJobsResp(t *testing.T) {
Messages: LineItemStatusMessages{
Info: "Line-item is currently being validated",
},
- Status: OrderCreated,
+ Status: ItemCreated,
},
Title: "My Book",
}},
- ProductionDelay: 120 * time.Minute,
- ProductionDue: time.Time{},
- AddressValidation: ShippingAddressValidation{
- Address: ShippingAddress{
- City: "Lübeck",
- Country: "DE",
- IsBusiness: false,
- Name: "Hans Dampf",
- Phone: "844-212-0689",
- PostCode: "23552",
- State: "",
- Street1: "Holstenstr. 40",
- Street2: "",
- },
- Suggested: ShippingAddress{
- Country: "DE",
- State: "",
- PostCode: "23552",
- City: "Lübeck",
- Street1: "Holstenstraße 40",
- Street2: "",
- },
- Warnings: []ShippingAddressWarning{{
- Type: "validation_warning",
- Path: "external",
- Code: "REPLACED",
- Msg: "street1: Holstenstr. 40 -> Holstenstraße 40",
- }},
+ ProductionDelay: 120 * time.Minute,
+ ProductionDue: time.Time{},
+ AddressValidation: shipAddrValidationSample,
+ ShipOpt: Mail,
+ Status: PrintJobStatus{
+ Changed: mustParseTime(time.RFC3339, "2017-08-07T08:47:26.480493Z"),
+ Msg: "Print-job is currently being validated",
+ Status: OrderCreated,
},
- ShipOpt: Mail,
}},
}
requireUnmarshalJsonEq(t, want, getPrintJobsRespJson)
}
-func TestPrintJobs(t *testing.T) {
+func TestGetPrintJobs(t *testing.T) {
t.Fail() // TODO: create a few print jobs and retrieve them
c := newClient(t)
- _, err := c.PrintJobs()
+ _, err := c.GetPrintJobs()
+ require.NoError(t, err)
+}
+
+func TestPrint(t *testing.T) {
+ contact := MustParseEmailAddress("test@test.com")
+ jobEid := "demo-time"
+ productionDelay := 120 * time.Minute
+ addr := shipAddrSample
+ shipOpt := Mail
+ item := Printable{
+ ExternalId: "item-reference-1",
+ CoverUrl: "https://www.dropbox.com/s/7bv6mg2tj0h3l0r/lulu_trade_perfect_template.pdf?dl=1&raw=1",
+ InteriorUrl: "https://www.dropbox.com/s/r20orb8umqjzav9/lulu_trade_interior_template-32.pdf?dl=1&raw=1",
+ Mfg: PkgId{UsTrade, Mono, Standard, Perfect, P60UncoatedWhite, Matte, NoLinen, NoFoil},
+ Quantity: 30,
+ Title: "My Book",
+ }
+
+ c := newClient(t)
+ startTime := time.Now()
+ job, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, []Printable{item})
require.NoError(t, err)
+ require.Equal(t, contact, job.Contact)
+ requireAfter(t, job.Created, startTime)
+ requireAfter(t, job.Modified, startTime)
+ require.Equal(t, jobEid, job.ExternalId)
+ require.NotZero(t, job.Id)
+ require.Len(t, job.LineItems, 1)
+ jobItem := job.LineItems[0]
+ require.Equal(t, item.ExternalId, jobItem.ExternalId)
+ require.NotZero(t, jobItem.Id)
+ require.Equal(t, item.CoverUrl, jobItem.PrintableNormalization.Cover.SrcUrl)
+ require.Equal(t, item.InteriorUrl, jobItem.PrintableNormalization.Interior.SrcUrl)
+ require.Equal(t, item.Mfg, jobItem.Mfg)
+ require.Equal(t, item.Quantity, jobItem.Quantity)
+ require.NotEmpty(t, jobItem.Status.Messages.Info)
+ require.NotEmpty(t, jobItem.Status.Status)
+ require.Equal(t, productionDelay, job.ProductionDelay)
+ requireMatchShipAddrValidationSample(t, job.AddressValidation)
+ require.Equal(t, shipOpt, job.ShipOpt)
+ requireAfter(t, job.Status.Changed, startTime)
+ require.NotEmpty(t, job.Status.Msg)
+ require.NotEmpty(t, job.Status.Status)
}
diff --git a/ship.go b/ship.go
index 0cfd733..030c80d 100644
--- a/ship.go
+++ b/ship.go
@@ -29,42 +29,43 @@ const (
)
type ShippingAddress struct {
- Country string // ISO 3166-2 country code
+ // ISO 3166-2 country code
+ Country string `json:"country_code"`
- // 2 or 3 letter state codes (officially called ISO-3166-2
+ // 2 or 3 letter state code (officially called ISO-3166-2
// subdivision codes). They are required for some countries (e.g.
// US, MX, CA, AU).
- State string
+ State string `json:"state"`
- City string
- Street1 string // First address line
- Street2 string // Second address line
- PostCode string // Required for most countries
+ City string `json:"city"`
+ Street1 string `json:"street1"` // First address line
+ Street2 string `json:"street2"` // Second address line
+ PostCode string `json:"postcode"` // Required for most countries
// Only relevant for US addresses. Some US carriers don't deliver
// to business-addresses on Saturday.
- IsBusiness bool
+ IsBusiness bool `json:"is_business"`
// Full name of the person, including first and last name.
- Name string
- Title Title
+ Name string `json:"name"`
+ Title Title `json:"title"`
// Name of an organization. Required if no person name is given.
- Organization string
+ Organization string `json:"organization"`
// Shipping carriers require an email address for notifications
// or handling delivery issues. If no email is given, the default
// email in the user profile will be used.
- Email EmailAddress
+ Email EmailAddress `json:"email"`
// Shipping carriers require a phone number for handling delivery
// issues. If no phone number is given, the default in the API
// user profile will be used.
- Phone PhoneNumber
+ Phone PhoneNumber `json:"phone_number"`
// The recipient's tax identification number. Required for
// shipping addresses to Brazil, Chile, and Mexico.
- TaxId string
+ TaxId string `json:"recipient_tax_id"`
}
func (a *ShippingAddress) UnmarshalJSON(data []byte) error {
diff --git a/ship_test.go b/ship_test.go
index 7643cb3..bd5432d 100644
--- a/ship_test.go
+++ b/ship_test.go
@@ -3,37 +3,77 @@ package lulu
import (
_ "embed"
"testing"
+
+ "github.com/stretchr/testify/require"
)
//go:embed testdata/shipaddrresp.json
var shipAddrRespJson string
+var shipAddrSample = ShippingAddress{
+ City: "Lübeck",
+ Country: "DE",
+ IsBusiness: false,
+ Name: "Hans Dampf",
+ Phone: MustParsePhoneNumber("844-212-0689"),
+ PostCode: "23552",
+ State: "",
+ Street1: "Holstenstr. 40",
+ Street2: "",
+}
+var shipWarningsSample = []ShippingAddressWarning{{
+ Type: "validation_warning",
+ Path: "external",
+ Code: "REPLACED",
+ Msg: "street1: Holstenstr. 40 -> Holstenstraße 40",
+}}
+var suggestedShipAddrSample = ShippingAddress{
+ Country: "DE",
+ State: "",
+ PostCode: "23552",
+ City: "Lübeck",
+ Street1: "Holstenstraße 40",
+}
+var shipAddrValidationSample = ShippingAddressValidation{
+ Address: shipAddrSample,
+ Warnings: shipWarningsSample,
+ Suggested: suggestedShipAddrSample,
+}
+
func TestUnmarshalShippingAddressValidation(t *testing.T) {
- want := ShippingAddressValidation{
- Address: ShippingAddress{
- City: "Lübeck",
- Country: "DE",
- IsBusiness: false,
- Name: "Hans Dampf",
- Phone: MustParsePhoneNumber("844-212-0689"),
- PostCode: "23552",
- State: "",
- Street1: "Holstenstr. 40",
- Street2: "",
- },
- Warnings: []ShippingAddressWarning{{
- Type: "validation_warning",
- Path: "external",
- Code: "REPLACED",
- Msg: "street1: Holstenstr. 40 -> Holstenstraße 40",
- }},
- Suggested: ShippingAddress{
- Country: "DE",
- State: "",
- PostCode: "23552",
- City: "Lübeck",
- Street1: "Holstenstraße 40",
- },
- }
- requireUnmarshalJsonEq(t, want, shipAddrRespJson)
+ requireUnmarshalJsonEq(t, shipAddrValidationSample, shipAddrRespJson)
+}
+
+func requireMatchShipAddrValidationSample(t *testing.T, av ShippingAddressValidation) {
+ t.Helper()
+ requireMatchShipAddrSample(t, av.Address)
+ requireMatchShipWarningsSample(t, av.Warnings)
+ requireMatchSuggestedShipAddrSample(t, av.Suggested)
+}
+
+func requireMatchShipAddrSample(t *testing.T, addr ShippingAddress) {
+ t.Helper()
+ require.Equal(t, shipAddrSample.City, addr.City)
+ require.Equal(t, shipAddrSample.Country, addr.Country)
+ require.Equal(t, shipAddrSample.IsBusiness, addr.IsBusiness)
+ require.Equal(t, shipAddrSample.Name, addr.Name)
+ require.Equal(t, shipAddrSample.Phone, addr.Phone)
+ require.Equal(t, shipAddrSample.PostCode, addr.PostCode)
+ require.Equal(t, shipAddrSample.State, addr.State)
+ require.Equal(t, shipAddrSample.Street1, addr.Street1)
+ require.Equal(t, shipAddrSample.Street2, addr.Street2)
+}
+
+func requireMatchShipWarningsSample(t *testing.T, warns []ShippingAddressWarning) {
+ t.Helper()
+ require.Equal(t, shipWarningsSample, warns)
+}
+
+func requireMatchSuggestedShipAddrSample(t *testing.T, sug ShippingAddress) {
+ t.Helper()
+ require.Equal(t, suggestedShipAddrSample.Country, sug.Country)
+ require.Equal(t, suggestedShipAddrSample.State, sug.State)
+ require.Equal(t, suggestedShipAddrSample.PostCode, sug.PostCode)
+ require.Equal(t, suggestedShipAddrSample.City, sug.City)
+ require.Equal(t, suggestedShipAddrSample.Street1, sug.Street1)
}
diff --git a/order.go b/status.go
index ec53071..d2aeb30 100644
--- a/order.go
+++ b/status.go
@@ -1,6 +1,6 @@
package lulu
-//go:generate go run github.com/yawnak/string-enumer -t OrderStatus --text -o ./order_gen.go .
+//go:generate go run github.com/yawnak/string-enumer -t OrderStatus -t ItemStatus --text -o ./status_gen.go .
type OrderStatus string
@@ -16,3 +16,14 @@ const (
OrderRejected OrderStatus = "REJECTED" // When there is a problem with the input data or the file, Lulu will reject a Print-Job with a detailed error message. Please contact our experts if you need help in resolving this issue.
OrderCanceled OrderStatus = "CANCELED" // You can cancel a Print-Job as long as it is “unpaid” using an API request to the status endpoint. In rare cases, Lulu might also cancel a Print-Job if a problem has surfaced in production and the order cannot be fulfilled.
)
+
+type ItemStatus string
+
+const (
+ ItemCreated ItemStatus = "CREATED"
+ ItemAccepted ItemStatus = "ACCEPTED"
+ ItemRejected ItemStatus = "REJECTED"
+ ItemInProduction ItemStatus = "IN_PRODUCTION"
+ ItemError ItemStatus = "ERROR"
+ ItemShipped ItemStatus = "SHIPPED"
+)
diff --git a/order_gen.go b/status_gen.go
index 5b17f05..2b2bb84 100644
--- a/order_gen.go
+++ b/status_gen.go
@@ -1,10 +1,47 @@
-// Code generated by "string-enumer -t OrderStatus --text -o ./order_gen.go ."; DO NOT EDIT.
+// Code generated by "string-enumer -t OrderStatus -t ItemStatus --text -o ./status_gen.go ."; DO NOT EDIT.
package lulu
import (
"fmt"
)
+// validItemStatusValues contains a map of all valid ItemStatus values for easy lookup
+var validItemStatusValues = map[ItemStatus]struct{}{
+ ItemCreated: {},
+ ItemAccepted: {},
+ ItemRejected: {},
+ ItemInProduction: {},
+ ItemError: {},
+ ItemShipped: {},
+}
+
+// Valid validates if a value is a valid ItemStatus
+func (v ItemStatus) Valid() bool {
+ _, ok := validItemStatusValues[v]
+ return ok
+}
+
+// ItemStatusValues returns a list of all (valid) ItemStatus values
+func ItemStatusValues() []ItemStatus {
+ return []ItemStatus{
+ ItemCreated,
+ ItemAccepted,
+ ItemRejected,
+ ItemInProduction,
+ ItemError,
+ ItemShipped,
+ }
+}
+
+// UnmarshalText takes a text, verifies that it is a correct ItemStatus and unmarshals it
+func (v *ItemStatus) UnmarshalText(text []byte) error {
+ if valid := ItemStatus(text).Valid(); !valid {
+ return fmt.Errorf("not valid value for ItemStatus: %s", text)
+ }
+ *v = ItemStatus(text)
+ return nil
+}
+
// validOrderStatusValues contains a map of all valid OrderStatus values for easy lookup
var validOrderStatusValues = map[OrderStatus]struct{}{
OrderCreated: {},