Skip to content
This repository was archived by the owner on Sep 11, 2018. It is now read-only.

Commit 94df70f

Browse files
dbertouilledlebech
authored andcommitted
Add asset service and partial theme service (#66)
* Adding asset service functions with List, Get, Update, and Delete methods * Added minimal theme service required to use asset service with List method * For some reason these changes caused the customer tests to fail because of a floating point comparison issue on TotalSpent. Updated to only check up to 2 decimals.
1 parent 366c7fd commit 94df70f

File tree

7 files changed

+314
-1
lines changed

7 files changed

+314
-1
lines changed

asset.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package goshopify
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
const assetsBasePath = "admin/themes"
9+
10+
// AssetService is an interface for interfacing with the asset endpoints
11+
// of the Shopify API.
12+
// See: https://help.shopify.com/api/reference/asset
13+
type AssetService interface {
14+
List(int, interface{}) ([]Asset, error)
15+
Get(int, string) (*Asset, error)
16+
Update(int, Asset) (*Asset, error)
17+
Delete(int, string) error
18+
}
19+
20+
// AssetServiceOp handles communication with the asset related methods of
21+
// the Shopify API.
22+
type AssetServiceOp struct {
23+
client *Client
24+
}
25+
26+
// Asset represents a Shopify asset
27+
type Asset struct {
28+
Attachment string `json:"attachment"`
29+
ContentType string `json:"content_type"`
30+
Key string `json:"key"`
31+
PublicURL string `json:"public_url"`
32+
Size int `json:"size"`
33+
SourceKey string `json:"source_key"`
34+
Src string `json:"src"`
35+
ThemeID int `json:"theme_id"`
36+
Value string `json:"value"`
37+
CreatedAt *time.Time `json:"created_at"`
38+
UpdatedAt *time.Time `json:"updated_at"`
39+
}
40+
41+
// AssetResource is the result from the themes/x/assets.json?asset[key]= endpoint
42+
type AssetResource struct {
43+
Asset *Asset `json:"asset"`
44+
}
45+
46+
// AssetsResource is the result from the themes/x/assets.json endpoint
47+
type AssetsResource struct {
48+
Assets []Asset `json:"assets"`
49+
}
50+
51+
type assetGetOptions struct {
52+
Key string `url:"asset[key]"`
53+
ThemeID int `url:"theme_id"`
54+
}
55+
56+
// List the metadata for all assets in the given theme
57+
func (s *AssetServiceOp) List(themeID int, options interface{}) ([]Asset, error) {
58+
path := fmt.Sprintf("%s/%d/assets.json", assetsBasePath, themeID)
59+
resource := new(AssetsResource)
60+
err := s.client.Get(path, resource, options)
61+
return resource.Assets, err
62+
}
63+
64+
// Get an asset by key from the given theme
65+
func (s *AssetServiceOp) Get(themeID int, key string) (*Asset, error) {
66+
path := fmt.Sprintf("%s/%d/assets.json", assetsBasePath, themeID)
67+
options := assetGetOptions{
68+
Key: key,
69+
ThemeID: themeID,
70+
}
71+
resource := new(AssetResource)
72+
err := s.client.Get(path, resource, options)
73+
return resource.Asset, err
74+
}
75+
76+
// Update an asset
77+
func (s *AssetServiceOp) Update(themeID int, asset Asset) (*Asset, error) {
78+
path := fmt.Sprintf("%s/%d/assets.json", assetsBasePath, themeID)
79+
wrappedData := AssetResource{Asset: &asset}
80+
resource := new(AssetResource)
81+
err := s.client.Put(path, wrappedData, resource)
82+
return resource.Asset, err
83+
}
84+
85+
// Delete an asset
86+
func (s *AssetServiceOp) Delete(themeID int, key string) error {
87+
path := fmt.Sprintf("%s/%d/assets.json?asset[key]=%s", assetsBasePath, themeID, key)
88+
return s.client.Delete(path)
89+
}

asset_test.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package goshopify
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"gopkg.in/jarcoal/httpmock.v1"
8+
)
9+
10+
func assetTests(t *testing.T, asset Asset) {
11+
expectedKey := "templates/index.liquid"
12+
if asset.Key != expectedKey {
13+
t.Errorf("Asset.Key returned %+v, expected %+v", asset.Key, expectedKey)
14+
}
15+
}
16+
17+
func TestAssetList(t *testing.T) {
18+
setup()
19+
defer teardown()
20+
21+
httpmock.RegisterResponder(
22+
"GET",
23+
"https://fooshop.myshopify.com/admin/themes/1/assets.json",
24+
httpmock.NewStringResponder(
25+
200,
26+
`{"assets": [{"key":"assets\/1.liquid"},{"key":"assets\/2.liquid"}]}`,
27+
),
28+
)
29+
30+
assets, err := client.Asset.List(1, nil)
31+
if err != nil {
32+
t.Errorf("Asset.List returned error: %v", err)
33+
}
34+
35+
expected := []Asset{{Key: "assets/1.liquid"}, {Key: "assets/2.liquid"}}
36+
if !reflect.DeepEqual(assets, expected) {
37+
t.Errorf("Asset.List returned %+v, expected %+v", assets, expected)
38+
}
39+
}
40+
41+
func TestAssetGet(t *testing.T) {
42+
setup()
43+
defer teardown()
44+
45+
httpmock.RegisterResponder(
46+
"GET",
47+
"https://fooshop.myshopify.com/admin/themes/1/assets.json?asset%5Bkey%5D=foo%2Fbar.liquid&theme_id=1",
48+
httpmock.NewStringResponder(
49+
200,
50+
`{"asset": {"key":"foo\/bar.liquid"}}`,
51+
),
52+
)
53+
54+
asset, err := client.Asset.Get(1, "foo/bar.liquid")
55+
if err != nil {
56+
t.Errorf("Asset.Get returned error: %v", err)
57+
}
58+
59+
expected := &Asset{Key: "foo/bar.liquid"}
60+
if !reflect.DeepEqual(asset, expected) {
61+
t.Errorf("Asset.Get returned %+v, expected %+v", asset, expected)
62+
}
63+
}
64+
65+
func TestAssetUpdate(t *testing.T) {
66+
setup()
67+
defer teardown()
68+
69+
httpmock.RegisterResponder(
70+
"PUT",
71+
"https://fooshop.myshopify.com/admin/themes/1/assets.json",
72+
httpmock.NewBytesResponder(
73+
200,
74+
loadFixture("asset.json"),
75+
),
76+
)
77+
78+
asset := Asset{
79+
Key: "templates/index.liquid",
80+
Value: "content",
81+
}
82+
83+
returnedAsset, err := client.Asset.Update(1, asset)
84+
if err != nil {
85+
t.Errorf("Asset.Update returned error: %v", err)
86+
}
87+
if returnedAsset == nil {
88+
t.Errorf("Asset.Update returned nil")
89+
}
90+
}
91+
92+
func TestAssetDelete(t *testing.T) {
93+
setup()
94+
defer teardown()
95+
96+
httpmock.RegisterResponder(
97+
"DELETE",
98+
"https://fooshop.myshopify.com/admin/themes/1/assets.json?asset[key]=foo/bar.liquid",
99+
httpmock.NewStringResponder(200, "{}"),
100+
)
101+
102+
err := client.Asset.Delete(1, "foo/bar.liquid")
103+
if err != nil {
104+
t.Errorf("Asset.Delete returned error: %v", err)
105+
}
106+
}

customer_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func TestCustomerGet(t *testing.T) {
122122
if customer.State != expectation.State {
123123
t.Errorf("Customer.State returned %+v, expected %+v", customer.State, expectation.State)
124124
}
125-
if !expectation.TotalSpent.Equals(*customer.TotalSpent) {
125+
if !expectation.TotalSpent.Truncate(2).Equals(customer.TotalSpent.Truncate(2)) {
126126
t.Errorf("Customer.TotalSpent returned %+v, expected %+v", customer.TotalSpent, expectation.TotalSpent)
127127
}
128128
if customer.LastOrderId != expectation.LastOrderId {

fixtures/asset.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"asset": {
3+
"key": "templates\/index.liquid",
4+
"public_url": null,
5+
"created_at": "2010-07-12T15:31:50-04:00",
6+
"updated_at": "2017-01-05T15:38:16-05:00",
7+
"content_type": "text\/x-liquid",
8+
"size": 110,
9+
"theme_id": 1
10+
}
11+
}

goshopify.go

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ type Client struct {
5656
Variant VariantService
5757
Image ImageService
5858
Transaction TransactionService
59+
Theme ThemeService
60+
Asset AssetService
5961
}
6062

6163
// A general response error that follows a similar layout to Shopify's response
@@ -159,6 +161,8 @@ func NewClient(app App, shopName, token string) *Client {
159161
c.Variant = &VariantServiceOp{client: c}
160162
c.Image = &ImageServiceOp{client: c}
161163
c.Transaction = &TransactionServiceOp{client: c}
164+
c.Theme = &ThemeServiceOp{client: c}
165+
c.Asset = &AssetServiceOp{client: c}
162166

163167
return c
164168
}

theme.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package goshopify
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
const themesBasePath = "admin/themes"
9+
10+
// Options for theme list
11+
type ThemeListOptions struct {
12+
ListOptions
13+
Role string `url:"role,omitempty"`
14+
}
15+
16+
// ThemeService is an interface for interfacing with the themes endpoints
17+
// of the Shopify API.
18+
// See: https://help.shopify.com/api/reference/theme
19+
type ThemeService interface {
20+
List(interface{}) ([]Theme, error)
21+
}
22+
23+
// ThemeServiceOp handles communication with the theme related methods of
24+
// the Shopify API.
25+
type ThemeServiceOp struct {
26+
client *Client
27+
}
28+
29+
// Theme represents a Shopify theme
30+
type Theme struct {
31+
ID int `json:"id"`
32+
Name string `json:"string"`
33+
Previewable bool `json:"previewable"`
34+
Processing bool `json:"processing"`
35+
Role string `json:"role"`
36+
ThemeStoreID int `json:"theme_store_id"`
37+
CreatedAt *time.Time `json:"created_at"`
38+
UpdatedAt *time.Time `json:"updated_at"`
39+
}
40+
41+
// ThemesResource is the result from the themes.json endpoint
42+
type ThemesResource struct {
43+
Themes []Theme `json:"themes"`
44+
}
45+
46+
// List all themes
47+
func (s *ThemeServiceOp) List(options interface{}) ([]Theme, error) {
48+
path := fmt.Sprintf("%s.json", themesBasePath)
49+
resource := new(ThemesResource)
50+
err := s.client.Get(path, resource, options)
51+
return resource.Themes, err
52+
}

theme_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package goshopify
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"gopkg.in/jarcoal/httpmock.v1"
8+
)
9+
10+
func TestThemeList(t *testing.T) {
11+
setup()
12+
defer teardown()
13+
14+
httpmock.RegisterResponder(
15+
"GET",
16+
"https://fooshop.myshopify.com/admin/themes.json",
17+
httpmock.NewStringResponder(
18+
200,
19+
`{"themes": [{"id":1},{"id":2}]}`,
20+
),
21+
)
22+
23+
httpmock.RegisterResponder(
24+
"GET",
25+
"https://fooshop.myshopify.com/admin/themes.json?role=main",
26+
httpmock.NewStringResponder(
27+
200,
28+
`{"themes": [{"id":1}]}`,
29+
),
30+
)
31+
32+
themes, err := client.Theme.List(nil)
33+
if err != nil {
34+
t.Errorf("Theme.List returned error: %v", err)
35+
}
36+
37+
expected := []Theme{{ID: 1}, {ID: 2}}
38+
if !reflect.DeepEqual(themes, expected) {
39+
t.Errorf("Theme.List returned %+v, expected %+v", themes, expected)
40+
}
41+
42+
themes, err = client.Theme.List(ThemeListOptions{Role: "main"})
43+
if err != nil {
44+
t.Errorf("Theme.List returned error: %v", err)
45+
}
46+
47+
expected = []Theme{{ID: 1}}
48+
if !reflect.DeepEqual(themes, expected) {
49+
t.Errorf("Theme.List returned %+v, expected %+v", themes, expected)
50+
}
51+
}

0 commit comments

Comments
 (0)