Skip to content

Commit ef772ce

Browse files
lukecycadainiauskasoliver006
authored
InventoryLevel (bold-commerce#166)
* Added Inventory Level --------- Co-authored-by: Dainius Matusevicius <[email protected]> Co-authored-by: Oliver <[email protected]>
1 parent ac4e748 commit ef772ce

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

fixtures/inventory_level.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"inventory_level": {
3+
"inventory_item_id": 808950810,
4+
"location_id": 905684977,
5+
"available": 6,
6+
"updated_at": "2020-04-06T10:51:56-04:00",
7+
"admin_graphql_api_id": "gid://shopify/InventoryLevel/905684977?inventory_item_id=808950810"
8+
}
9+
}

fixtures/inventory_levels.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"inventory_levels": [
3+
{
4+
"inventory_item_id": 808950810,
5+
"location_id": 487838322,
6+
"available": 9,
7+
"updated_at": "2020-04-06T10:51:36-04:00",
8+
"admin_graphql_api_id": "gid://shopify/InventoryLevel/690933842?inventory_item_id=808950810"
9+
},
10+
{
11+
"inventory_item_id": 39072856,
12+
"location_id": 487838322,
13+
"available": 27,
14+
"updated_at": "2020-04-06T10:51:36-04:00",
15+
"admin_graphql_api_id": "gid://shopify/InventoryLevel/690933842?inventory_item_id=39072856"
16+
},
17+
{
18+
"inventory_item_id": 808950810,
19+
"location_id": 905684977,
20+
"available": 1,
21+
"updated_at": "2020-04-06T10:51:36-04:00",
22+
"admin_graphql_api_id": "gid://shopify/InventoryLevel/905684977?inventory_item_id=808950810"
23+
},
24+
{
25+
"inventory_item_id": 39072856,
26+
"location_id": 905684977,
27+
"available": 3,
28+
"updated_at": "2020-04-06T10:51:36-04:00",
29+
"admin_graphql_api_id": "gid://shopify/InventoryLevel/905684977?inventory_item_id=39072856"
30+
}
31+
]
32+
}

goshopify.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ type Client struct {
115115
InventoryItem InventoryItemService
116116
ShippingZone ShippingZoneService
117117
ProductListing ProductListingService
118+
InventoryLevel InventoryLevelService
118119
AccessScopes AccessScopesService
119120
FulfillmentService FulfillmentServiceService
120121
CarrierService CarrierServiceService
@@ -292,6 +293,7 @@ func NewClient(app App, shopName, token string, opts ...Option) *Client {
292293
c.InventoryItem = &InventoryItemServiceOp{client: c}
293294
c.ShippingZone = &ShippingZoneServiceOp{client: c}
294295
c.ProductListing = &ProductListingServiceOp{client: c}
296+
c.InventoryLevel = &InventoryLevelServiceOp{client: c}
295297
c.AccessScopes = &AccessScopesServiceOp{client: c}
296298
c.FulfillmentService = &FulfillmentServiceServiceOp{client: c}
297299
c.CarrierService = &CarrierServiceOp{client: c}

inventory_level.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package goshopify
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
const inventoryLevelsBasePath = "inventory_levels"
9+
10+
// InventoryLevelService is an interface for interacting with the
11+
// inventory items endpoints of the Shopify API
12+
// See https://help.shopify.com/en/api/reference/inventory/inventorylevel
13+
type InventoryLevelService interface {
14+
List(interface{}) ([]InventoryLevel, error)
15+
Adjust(interface{}) (*InventoryLevel, error)
16+
Delete(int64, int64) error
17+
Connect(InventoryLevel) (*InventoryLevel, error)
18+
Set(InventoryLevel) (*InventoryLevel, error)
19+
}
20+
21+
// InventoryLevelServiceOp is the default implementation of the InventoryLevelService interface
22+
type InventoryLevelServiceOp struct {
23+
client *Client
24+
}
25+
26+
// InventoryLevel represents a Shopify inventory level
27+
type InventoryLevel struct {
28+
InventoryItemId int64 `json:"inventory_item_id,omitempty"`
29+
LocationId int64 `json:"location_id,omitempty"`
30+
Available int `json:"available"`
31+
CreatedAt *time.Time `json:"created_at,omitempty"`
32+
UpdatedAt *time.Time `json:"updated_at,omitempty"`
33+
AdminGraphqlApiId string `json:"admin_graphql_api_id,omitempty"`
34+
}
35+
36+
// InventoryLevelResource is used for handling single level requests and responses
37+
type InventoryLevelResource struct {
38+
InventoryLevel *InventoryLevel `json:"inventory_level"`
39+
}
40+
41+
// InventoryLevelsResource is used for handling multiple item responsees
42+
type InventoryLevelsResource struct {
43+
InventoryLevels []InventoryLevel `json:"inventory_levels"`
44+
}
45+
46+
// InventoryLevelListOptions is used for get list
47+
type InventoryLevelListOptions struct {
48+
InventoryItemIds []int64 `url:"inventory_item_ids,omitempty,comma"`
49+
LocationIds []int64 `url:"location_ids,omitempty,comma"`
50+
Limit int `url:"limit,omitempty"`
51+
UpdatedAtMin time.Time `url:"updated_at_min,omitempty"`
52+
}
53+
54+
// InventoryLevelAdjustOptions is used for Adjust inventory levels
55+
type InventoryLevelAdjustOptions struct {
56+
InventoryItemId int64 `json:"inventory_item_id"`
57+
LocationId int64 `json:"location_id"`
58+
Adjust int `json:"available_adjustment"`
59+
}
60+
61+
// List inventory levels
62+
func (s *InventoryLevelServiceOp) List(options interface{}) ([]InventoryLevel, error) {
63+
path := fmt.Sprintf("%s.json", inventoryLevelsBasePath)
64+
resource := new(InventoryLevelsResource)
65+
err := s.client.Get(path, resource, options)
66+
return resource.InventoryLevels, err
67+
}
68+
69+
// Delete an inventory level
70+
func (s *InventoryLevelServiceOp) Delete(itemId, locationId int64) error {
71+
path := fmt.Sprintf("%s.json?inventory_item_id=%v&location_id=%v",
72+
inventoryLevelsBasePath, itemId, locationId)
73+
return s.client.Delete(path)
74+
}
75+
76+
// Connect an inventory level
77+
func (s *InventoryLevelServiceOp) Connect(level InventoryLevel) (*InventoryLevel, error) {
78+
return s.post(fmt.Sprintf("%s/connect.json", inventoryLevelsBasePath), level)
79+
}
80+
81+
// Set an inventory level
82+
func (s *InventoryLevelServiceOp) Set(level InventoryLevel) (*InventoryLevel, error) {
83+
return s.post(fmt.Sprintf("%s/set.json", inventoryLevelsBasePath), level)
84+
}
85+
86+
// Adjust the inventory level of an inventory item at a single location
87+
func (s *InventoryLevelServiceOp) Adjust(options interface{}) (*InventoryLevel, error) {
88+
return s.post(fmt.Sprintf("%s/adjust.json", inventoryLevelsBasePath), options)
89+
}
90+
91+
func (s *InventoryLevelServiceOp) post(path string, options interface{}) (*InventoryLevel, error) {
92+
resource := new(InventoryLevelResource)
93+
err := s.client.Post(path, options, resource)
94+
return resource.InventoryLevel, err
95+
}

inventory_level_test.go

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package goshopify
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/jarcoal/httpmock"
8+
)
9+
10+
func inventoryLevelTests(t *testing.T, item *InventoryLevel) {
11+
if item == nil {
12+
t.Errorf("InventoryItem is nil")
13+
return
14+
}
15+
16+
expectedInt := int64(808950810)
17+
if item.InventoryItemId != expectedInt {
18+
t.Errorf("InventoryLevel.InventoryItemId returned %+v, expected %+v",
19+
item.InventoryItemId, expectedInt)
20+
}
21+
22+
expectedInt = int64(905684977)
23+
if item.LocationId != expectedInt {
24+
t.Errorf("InventoryLevel.LocationId is %+v, expected %+v",
25+
item.LocationId, expectedInt)
26+
}
27+
28+
expectedAvailable := 6
29+
if item.Available != expectedAvailable {
30+
t.Errorf("InventoryLevel.Available is %+v, expected %+v",
31+
item.Available, expectedInt)
32+
}
33+
}
34+
35+
func inventoryLevelsTests(t *testing.T, levels []InventoryLevel) {
36+
expectedLen := 4
37+
if len(levels) != expectedLen {
38+
t.Errorf("InventoryLevels list lenth is %+v, expected %+v", len(levels), expectedLen)
39+
}
40+
}
41+
42+
func TestInventoryLevelsList(t *testing.T) {
43+
setup()
44+
defer teardown()
45+
46+
httpmock.RegisterResponder("GET",
47+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels.json", client.pathPrefix),
48+
httpmock.NewBytesResponder(200, loadFixture("inventory_levels.json")))
49+
50+
levels, err := client.InventoryLevel.List(nil)
51+
if err != nil {
52+
t.Errorf("InventoryLevels.List returned error: %v", err)
53+
}
54+
55+
inventoryLevelsTests(t, levels)
56+
}
57+
58+
func TestInventoryLevelListWithItemId(t *testing.T) {
59+
setup()
60+
defer teardown()
61+
62+
params := map[string]string{
63+
"inventory_item_ids": "1,2",
64+
}
65+
httpmock.RegisterResponderWithQuery(
66+
"GET",
67+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels.json", client.pathPrefix),
68+
params,
69+
httpmock.NewBytesResponder(200, loadFixture("inventory_levels.json")),
70+
)
71+
72+
options := InventoryLevelListOptions{
73+
InventoryItemIds: []int64{1, 2},
74+
}
75+
76+
levels, err := client.InventoryLevel.List(options)
77+
if err != nil {
78+
t.Errorf("InventoryLevels.List returned error: %v", err)
79+
}
80+
81+
inventoryLevelsTests(t, levels)
82+
}
83+
84+
func TestInventoryLevelListWithLocationId(t *testing.T) {
85+
setup()
86+
defer teardown()
87+
88+
params := map[string]string{
89+
"location_ids": "1,2",
90+
}
91+
httpmock.RegisterResponderWithQuery(
92+
"GET",
93+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels.json", client.pathPrefix),
94+
params,
95+
httpmock.NewBytesResponder(200, loadFixture("inventory_levels.json")),
96+
)
97+
98+
options := InventoryLevelListOptions{
99+
LocationIds: []int64{1, 2},
100+
}
101+
102+
levels, err := client.InventoryLevel.List(options)
103+
if err != nil {
104+
t.Errorf("InventoryLevels.List returned error: %v", err)
105+
}
106+
107+
inventoryLevelsTests(t, levels)
108+
}
109+
110+
func TestInventoryLevelAdjust(t *testing.T) {
111+
setup()
112+
defer teardown()
113+
114+
httpmock.RegisterResponder("POST",
115+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels/adjust.json", client.pathPrefix),
116+
httpmock.NewBytesResponder(200, loadFixture("inventory_level.json")))
117+
118+
option := InventoryLevelAdjustOptions{
119+
InventoryItemId: 808950810,
120+
LocationId: 905684977,
121+
Adjust: 6,
122+
}
123+
124+
adjItem, err := client.InventoryLevel.Adjust(option)
125+
if err != nil {
126+
t.Errorf("InventoryLevel.Adjust returned error: %v", err)
127+
}
128+
129+
inventoryLevelTests(t, adjItem)
130+
}
131+
132+
func TestInventoryLevelDelete(t *testing.T) {
133+
setup()
134+
defer teardown()
135+
136+
httpmock.RegisterResponder("DELETE",
137+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels.json", client.pathPrefix),
138+
httpmock.NewStringResponder(200, "{}"))
139+
140+
err := client.InventoryLevel.Delete(1, 1)
141+
if err != nil {
142+
t.Errorf("InventoryLevel.Delete returned error: %v", err)
143+
}
144+
}
145+
146+
func TestInventoryLevelConnect(t *testing.T) {
147+
setup()
148+
defer teardown()
149+
150+
httpmock.RegisterResponder(
151+
"POST",
152+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels/connect.json", client.pathPrefix),
153+
httpmock.NewBytesResponder(200, loadFixture("inventory_level.json")),
154+
)
155+
156+
options := InventoryLevel{
157+
InventoryItemId: 1,
158+
LocationId: 1,
159+
}
160+
161+
level, err := client.InventoryLevel.Connect(options)
162+
if err != nil {
163+
t.Errorf("InventoryLevels.Connect returned error: %v", err)
164+
}
165+
166+
inventoryLevelTests(t, level)
167+
}
168+
169+
func TestInventoryLevelSet(t *testing.T) {
170+
setup()
171+
defer teardown()
172+
173+
httpmock.RegisterResponder(
174+
"POST",
175+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels/set.json", client.pathPrefix),
176+
httpmock.NewBytesResponder(200, loadFixture("inventory_level.json")),
177+
)
178+
179+
options := InventoryLevel{
180+
InventoryItemId: 1,
181+
LocationId: 1,
182+
}
183+
184+
level, err := client.InventoryLevel.Set(options)
185+
if err != nil {
186+
t.Errorf("InventoryLevels.Set returned error: %v", err)
187+
}
188+
189+
inventoryLevelTests(t, level)
190+
}
191+
192+
func TestInventoryLevelSetZero(t *testing.T) {
193+
setup()
194+
defer teardown()
195+
196+
httpmock.RegisterResponder(
197+
"POST",
198+
fmt.Sprintf("https://fooshop.myshopify.com/%s/inventory_levels/set.json", client.pathPrefix),
199+
httpmock.NewBytesResponder(200, loadFixture("inventory_level.json")),
200+
)
201+
202+
options := InventoryLevel{
203+
InventoryItemId: 1,
204+
LocationId: 1,
205+
Available: 0,
206+
}
207+
208+
level, err := client.InventoryLevel.Set(options)
209+
if err != nil {
210+
t.Errorf("InventoryLevels.Set returned error: %v", err)
211+
}
212+
213+
inventoryLevelTests(t, level)
214+
}

0 commit comments

Comments
 (0)