From 1b05aff4e958501c6dfece1dda83b326d2d97e7c Mon Sep 17 00:00:00 2001 From: dbertouille Date: Thu, 17 Jan 2019 09:44:16 -0600 Subject: [PATCH] Add inventory item API (#43) * Add an inventory item service to the client that supports list, get, and update --- fixtures/inventory_item.json | 11 ++++ fixtures/inventory_items.json | 31 +++++++++ goshopify.go | 2 + inventory_item.go | 70 ++++++++++++++++++++ inventory_item_test.go | 116 ++++++++++++++++++++++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 fixtures/inventory_item.json create mode 100644 fixtures/inventory_items.json create mode 100644 inventory_item.go create mode 100644 inventory_item_test.go diff --git a/fixtures/inventory_item.json b/fixtures/inventory_item.json new file mode 100644 index 00000000..2b92a6d8 --- /dev/null +++ b/fixtures/inventory_item.json @@ -0,0 +1,11 @@ +{ + "inventory_item": { + "id": 808950810, + "sku": "new sku", + "created_at": "2018-10-29T06:05:58-04:00", + "updated_at": "2018-10-29T06:05:58-04:00", + "cost": "25.00", + "tracked": true, + "admin_graphql_api_id": "gid://shopify/InventoryItem/808950810" + } + } \ No newline at end of file diff --git a/fixtures/inventory_items.json b/fixtures/inventory_items.json new file mode 100644 index 00000000..8e9ed51b --- /dev/null +++ b/fixtures/inventory_items.json @@ -0,0 +1,31 @@ +{ + "inventory_items": [ + { + "id": 39072856, + "sku": "IPOD2008GREEN", + "created_at": "2018-10-29T06:05:58-04:00", + "updated_at": "2018-10-29T06:05:58-04:00", + "cost": "25.00", + "tracked": true, + "admin_graphql_api_id": "gid://shopify/InventoryItem/39072856" + }, + { + "id": 457924702, + "sku": "IPOD2008BLACK", + "created_at": "2018-10-29T06:05:58-04:00", + "updated_at": "2018-10-29T06:05:58-04:00", + "cost": "25.00", + "tracked": true, + "admin_graphql_api_id": "gid://shopify/InventoryItem/457924702" + }, + { + "id": 808950810, + "sku": "IPOD2008PINK", + "created_at": "2018-10-29T06:05:58-04:00", + "updated_at": "2018-10-29T06:05:58-04:00", + "cost": "25.00", + "tracked": true, + "admin_graphql_api_id": "gid://shopify/InventoryItem/808950810" + } + ] + } \ No newline at end of file diff --git a/goshopify.go b/goshopify.go index 90cb47b7..b2a1d37b 100644 --- a/goshopify.go +++ b/goshopify.go @@ -73,6 +73,7 @@ type Client struct { Collect CollectService Location LocationService DiscountCode DiscountCodeService + InventoryItem InventoryItemService } // A general response error that follows a similar layout to Shopify's response @@ -213,6 +214,7 @@ func NewClient(app App, shopName, token string) *Client { c.Collect = &CollectServiceOp{client: c} c.Location = &LocationServiceOp{client: c} c.DiscountCode = &DiscountCodeServiceOp{client: c} + c.InventoryItem = &InventoryItemServiceOp{client: c} return c } diff --git a/inventory_item.go b/inventory_item.go new file mode 100644 index 00000000..767dde99 --- /dev/null +++ b/inventory_item.go @@ -0,0 +1,70 @@ +package goshopify + +import ( + "fmt" + "time" + + "github.com/shopspring/decimal" +) + +const inventoryItemsBasePath = "admin/inventory_items" + +// InventoryItemService is an interface for interacting with the +// inventory items endpoints of the Shopify API +// See https://help.shopify.com/en/api/reference/inventory/inventoryitem +type InventoryItemService interface { + List(interface{}) ([]InventoryItem, error) + Get(int, interface{}) (*InventoryItem, error) + Update(InventoryItem) (*InventoryItem, error) +} + +// InventoryItemServiceOp is the default implementation of the InventoryItemService interface +type InventoryItemServiceOp struct { + client *Client +} + +// InventoryItem represents a Shopify inventory item +type InventoryItem struct { + ID int `json:"id,omitempty"` + SKU string `json:"sku,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + Cost *decimal.Decimal `json:"cost,omitempty"` + Tracked *bool `json:"tracked,omitempty"` + AdminGraphqlAPIID string `json:"admin_graphql_api_id,omitempty"` +} + +// InventoryItemResource is used for handling single item requests and responses +type InventoryItemResource struct { + InventoryItem *InventoryItem `json:"inventory_item"` +} + +// InventoryItemsResource is used for handling multiple item responsees +type InventoryItemsResource struct { + InventoryItems []InventoryItem `json:"inventory_items"` +} + +// List inventory items +func (s *InventoryItemServiceOp) List(options interface{}) ([]InventoryItem, error) { + path := fmt.Sprintf("%s.json", inventoryItemsBasePath) + resource := new(InventoryItemsResource) + err := s.client.Get(path, resource, options) + return resource.InventoryItems, err +} + +// Get a inventory item +func (s *InventoryItemServiceOp) Get(id int, options interface{}) (*InventoryItem, error) { + path := fmt.Sprintf("%s/%d.json", inventoryItemsBasePath, id) + resource := new(InventoryItemResource) + err := s.client.Get(path, resource, options) + return resource.InventoryItem, err +} + +// Update a inventory item +func (s *InventoryItemServiceOp) Update(item InventoryItem) (*InventoryItem, error) { + path := fmt.Sprintf("%s/%d.json", inventoryItemsBasePath, item.ID) + wrappedData := InventoryItemResource{InventoryItem: &item} + resource := new(InventoryItemResource) + err := s.client.Put(path, wrappedData, resource) + return resource.InventoryItem, err +} diff --git a/inventory_item_test.go b/inventory_item_test.go new file mode 100644 index 00000000..ce764bad --- /dev/null +++ b/inventory_item_test.go @@ -0,0 +1,116 @@ +package goshopify + +import ( + "testing" + + httpmock "gopkg.in/jarcoal/httpmock.v1" +) + +func inventoryItemTests(t *testing.T, item *InventoryItem) { + if item == nil { + t.Errorf("InventoryItem is nil") + return + } + + expectedInt := 808950810 + if item.ID != expectedInt { + t.Errorf("InventoryItem.ID returned %+v, expected %+v", item.ID, expectedInt) + } + + expectedSKU := "new sku" + if item.SKU != expectedSKU { + t.Errorf("InventoryItem.SKU sku is %+v, expected %+v", item.SKU, expectedSKU) + } + + if item.Cost == nil { + t.Errorf("InventoryItem.Cost is nil") + return + } + + expectedCost := 25.00 + costFloat, _ := item.Cost.Float64() + if costFloat != expectedCost { + t.Errorf("InventoryItem.Cost (float) is %+v, expected %+v", costFloat, expectedCost) + } +} + +func inventoryItemsTests(t *testing.T, items []InventoryItem) { + expectedLen := 3 + if len(items) != expectedLen { + t.Errorf("InventoryItems list lenth is %+v, expected %+v", len(items), expectedLen) + } +} + +func TestInventoryItemsList(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("GET", "https://fooshop.myshopify.com/admin/inventory_items.json", + httpmock.NewBytesResponder(200, loadFixture("inventory_items.json"))) + + items, err := client.InventoryItem.List(nil) + if err != nil { + t.Errorf("InventoryItems.List returned error: %v", err) + } + + inventoryItemsTests(t, items) +} + +func TestInventoryItemsListWithIDs(t *testing.T) { + setup() + defer teardown() + + params := map[string]string{ + "ids": "1,2", + } + httpmock.RegisterResponderWithQuery( + "GET", + "https://fooshop.myshopify.com/admin/inventory_items.json", + params, + httpmock.NewBytesResponder(200, loadFixture("inventory_items.json")), + ) + + options := ListOptions{ + IDs: []int{1, 2}, + } + + items, err := client.InventoryItem.List(options) + if err != nil { + t.Errorf("InventoryItems.List returned error: %v", err) + } + + inventoryItemsTests(t, items) +} + +func TestInventoryItemGet(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("GET", "https://fooshop.myshopify.com/admin/inventory_items/1.json", + httpmock.NewBytesResponder(200, loadFixture("inventory_item.json"))) + + item, err := client.InventoryItem.Get(1, nil) + if err != nil { + t.Errorf("InventoryItem.Get returned error: %v", err) + } + + inventoryItemTests(t, item) +} +func TestInventoryItemUpdate(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("PUT", "https://fooshop.myshopify.com/admin/inventory_items/1.json", + httpmock.NewBytesResponder(200, loadFixture("inventory_item.json"))) + + item := InventoryItem{ + ID: 1, + } + + updatedItem, err := client.InventoryItem.Update(item) + if err != nil { + t.Errorf("InentoryItem.Update returned error: %v", err) + } + + inventoryItemTests(t, updatedItem) +}