Skip to content

Commit

Permalink
Add discount codes service (#42)
Browse files Browse the repository at this point in the history
* Implemented Discount Code API service

* Added tests for Discount Code API service

* Fixed typo

* Fix HTTP returning codes on discount test cases

* Fix JSON formatting

* Add new line on discount_code.json
  • Loading branch information
MarinX authored and andrewhoff committed Jan 11, 2019
1 parent 4b0481a commit 91a7ed6
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
84 changes: 84 additions & 0 deletions discount_code.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package goshopify

import (
"fmt"
"time"
)

const discountCodeBasePath = "/admin/price_rules/%d/discount_codes"

// DiscountCodeService is an interface for interfacing with the discount endpoints
// of the Shopify API.
// See: https://help.shopify.com/en/api/reference/discounts/PriceRuleDiscountCode
type DiscountCodeService interface {
Create(int, PriceRuleDiscountCode) (*PriceRuleDiscountCode, error)
Update(int, PriceRuleDiscountCode) (*PriceRuleDiscountCode, error)
List(int) ([]PriceRuleDiscountCode, error)
Get(int, int) (*PriceRuleDiscountCode, error)
Delete(int, int) error
}

// DiscountCodeServiceOp handles communication with the discount code
// related methods of the Shopify API.
type DiscountCodeServiceOp struct {
client *Client
}

// PriceRuleDiscountCode represents a Shopify Discount Code
type PriceRuleDiscountCode struct {
ID int `json:"id,omitempty"`
PriceRuleID int `json:"price_rule_id,omitempty"`
Code string `json:"code,omitempty"`
UsageCount int `json:"usage_count,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}

// DiscountCodesResource is the result from the discount_codes.json endpoint
type DiscountCodesResource struct {
DiscountCodes []PriceRuleDiscountCode `json:"discount_codes"`
}

// DiscountCodeResource represents the result from the discount_codes/X.json endpoint
type DiscountCodeResource struct {
PriceRuleDiscountCode *PriceRuleDiscountCode `json:"discount_code"`
}

// Create a discount code
func (s *DiscountCodeServiceOp) Create(priceRuleID int, dc PriceRuleDiscountCode) (*PriceRuleDiscountCode, error) {
path := fmt.Sprintf(discountCodeBasePath+".json", priceRuleID)
wrappedData := DiscountCodeResource{PriceRuleDiscountCode: &dc}
resource := new(DiscountCodeResource)
err := s.client.Post(path, wrappedData, resource)
return resource.PriceRuleDiscountCode, err
}

// Update an existing discount code
func (s *DiscountCodeServiceOp) Update(priceRuleID int, dc PriceRuleDiscountCode) (*PriceRuleDiscountCode, error) {
path := fmt.Sprintf(discountCodeBasePath+"/%d.json", priceRuleID, dc.ID)
wrappedData := DiscountCodeResource{PriceRuleDiscountCode: &dc}
resource := new(DiscountCodeResource)
err := s.client.Put(path, wrappedData, resource)
return resource.PriceRuleDiscountCode, err
}

// List of discount codes
func (s *DiscountCodeServiceOp) List(priceRuleID int) ([]PriceRuleDiscountCode, error) {
path := fmt.Sprintf(discountCodeBasePath+".json", priceRuleID)
resource := new(DiscountCodesResource)
err := s.client.Get(path, resource, nil)
return resource.DiscountCodes, err
}

// Get a single discount code
func (s *DiscountCodeServiceOp) Get(priceRuleID int, discountCodeID int) (*PriceRuleDiscountCode, error) {
path := fmt.Sprintf(discountCodeBasePath+"/%d.json", priceRuleID, discountCodeID)
resource := new(DiscountCodeResource)
err := s.client.Get(path, resource, nil)
return resource.PriceRuleDiscountCode, err
}

// Delete a discount code
func (s *DiscountCodeServiceOp) Delete(priceRuleID int, discountCodeID int) error {
return s.client.Delete(fmt.Sprintf(discountCodeBasePath+"/%d.json", priceRuleID, discountCodeID))
}
129 changes: 129 additions & 0 deletions discount_code_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package goshopify

import (
"testing"

httpmock "gopkg.in/jarcoal/httpmock.v1"
)

func TestDiscountCodeList(t *testing.T) {
setup()
defer teardown()

httpmock.RegisterResponder(
"GET",
"https://fooshop.myshopify.com/admin/price_rules/507328175/discount_codes.json",
httpmock.NewStringResponder(
200,
`{"discount_codes":[{"id":507328175,"price_rule_id":507328175,"code":"SUMMERSALE10OFF","usage_count":0,"created_at":"2018-07-05T12:41:00-04:00","updated_at":"2018-07-05T12:41:00-04:00"}]}`,
),
)

codes, err := client.DiscountCode.List(507328175)
if err != nil {
t.Errorf("DiscountCode.List returned error: %v", err)
}

expected := []PriceRuleDiscountCode{{ID: 507328175}}
if expected[0].ID != codes[0].ID {
t.Errorf("DiscountCode.List returned %+v, expected %+v", codes, expected)
}

}

func TestDiscountCodeGet(t *testing.T) {
setup()
defer teardown()

httpmock.RegisterResponder(
"GET",
"https://fooshop.myshopify.com/admin/price_rules/507328175/discount_codes/507328175.json",
httpmock.NewStringResponder(
200,
`{"discount_code":{"id":507328175,"price_rule_id":507328175,"code":"SUMMERSALE10OFF","usage_count":0,"created_at":"2018-07-05T12:41:00-04:00","updated_at":"2018-07-05T12:41:00-04:00"}}`,
),
)

dc, err := client.DiscountCode.Get(507328175, 507328175)
if err != nil {
t.Errorf("DiscountCode.Get returned error: %v", err)
}

expected := &PriceRuleDiscountCode{ID: 507328175}

if dc.ID != expected.ID {
t.Errorf("DiscountCode.Get returned %+v, expected %+v", dc, expected)
}

}

func TestDiscountCodeCreate(t *testing.T) {
setup()
defer teardown()

httpmock.RegisterResponder(
"POST",
"https://fooshop.myshopify.com/admin/price_rules/507328175/discount_codes.json",
httpmock.NewBytesResponder(
201,
loadFixture("discount_code.json"),
),
)

dc := PriceRuleDiscountCode{
Code: "SUMMERSALE10OFF",
}

returnedDC, err := client.DiscountCode.Create(507328175, dc)
if err != nil {
t.Errorf("DiscountCode.Create returned error: %v", err)
}

expectedInt := 1054381139
if returnedDC.ID != expectedInt {
t.Errorf("DiscountCode.ID returned %+v, expected %+v", returnedDC.ID, expectedInt)
}

}

func TestDiscountCodeUpdate(t *testing.T) {
setup()
defer teardown()

httpmock.RegisterResponder(
"PUT",
"https://fooshop.myshopify.com/admin/price_rules/507328175/discount_codes/1054381139.json",
httpmock.NewBytesResponder(
200,
loadFixture("discount_code.json"),
),
)

dc := PriceRuleDiscountCode{
ID: 1054381139,
Code: "SUMMERSALE10OFF",
}

returnedDC, err := client.DiscountCode.Update(507328175, dc)
if err != nil {
t.Errorf("DiscountCode.Update returned error: %v", err)
}

expectedInt := 1054381139
if returnedDC.ID != expectedInt {
t.Errorf("DiscountCode.ID returned %+v, expected %+v", returnedDC.ID, expectedInt)
}
}

func TestDiscountCodeDelete(t *testing.T) {
setup()
defer teardown()

httpmock.RegisterResponder("DELETE", "https://fooshop.myshopify.com/admin/price_rules/507328175/discount_codes/507328175.json",
httpmock.NewStringResponder(204, "{}"))

err := client.DiscountCode.Delete(507328175, 507328175)
if err != nil {
t.Errorf("DiscountCode.Delete returned error: %v", err)
}
}
10 changes: 10 additions & 0 deletions fixtures/discount_code.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"discount_code": {
"id": 1054381139,
"price_rule_id": 507328175,
"code": "SUMMERSALE10OFF",
"usage_count": 0,
"created_at": "2018-07-05T13:04:26-04:00",
"updated_at": "2018-07-05T13:04:26-04:00"
}
}
2 changes: 2 additions & 0 deletions goshopify.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type Client struct {
StorefrontAccessToken StorefrontAccessTokenService
Collect CollectService
Location LocationService
DiscountCode DiscountCodeService
}

// A general response error that follows a similar layout to Shopify's response
Expand Down Expand Up @@ -211,6 +212,7 @@ func NewClient(app App, shopName, token string) *Client {
c.UsageCharge = &UsageChargeServiceOp{client: c}
c.Collect = &CollectServiceOp{client: c}
c.Location = &LocationServiceOp{client: c}
c.DiscountCode = &DiscountCodeServiceOp{client: c}

return c
}
Expand Down

0 comments on commit 91a7ed6

Please sign in to comment.