Skip to content

Commit

Permalink
Add inventory item API (#43)
Browse files Browse the repository at this point in the history
* Add an inventory item service to the client that supports list, get, and update
  • Loading branch information
dbertouille authored Jan 17, 2019
1 parent 91a7ed6 commit 1b05aff
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 0 deletions.
11 changes: 11 additions & 0 deletions fixtures/inventory_item.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
31 changes: 31 additions & 0 deletions fixtures/inventory_items.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
2 changes: 2 additions & 0 deletions goshopify.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
70 changes: 70 additions & 0 deletions inventory_item.go
Original file line number Diff line number Diff line change
@@ -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
}
116 changes: 116 additions & 0 deletions inventory_item_test.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 1b05aff

Please sign in to comment.