diff --git a/customer_address.go b/customer_address.go index acce75a5..734a8f2d 100644 --- a/customer_address.go +++ b/customer_address.go @@ -1,5 +1,26 @@ package goshopify +import "fmt" + +const customerAddressResourceName = "customer-addresses" + +// CustomerAddressService is an interface for interfacing with the customer address endpoints +// of the Shopify API. +// See: https://help.shopify.com/en/api/reference/customers/customer_address +type CustomerAddressService interface { + List(int, interface{}) ([]CustomerAddress, error) + Get(int, int, interface{}) (*CustomerAddress, error) + Create(int, CustomerAddress) (*CustomerAddress, error) + Update(int, CustomerAddress) (*CustomerAddress, error) + Delete(int, int) error +} + +// CustomerAddressServiceOp handles communication with the customer address related methods of +// the Shopify API. +type CustomerAddressServiceOp struct { + client *Client +} + // CustomerAddress represents a Shopify customer address type CustomerAddress struct { ID int `json:"id,omitempty"` @@ -20,3 +41,52 @@ type CustomerAddress struct { CountryName string `json:"country_name"` Default bool `json:"default"` } + +// CustomerAddressResoruce represents the result from the addresses/X.json endpoint +type CustomerAddressResource struct { + Address *CustomerAddress `json:"customer_address"` +} + +// CustomerAddressResoruce represents the result from the customers/X/addresses.json endpoint +type CustomerAddressesResource struct { + Addresses []CustomerAddress `json:"addresses"` +} + +// List addresses +func (s *CustomerAddressServiceOp) List(customerID int, options interface{}) ([]CustomerAddress, error) { + path := fmt.Sprintf("%s/%d/addresses.json", customersBasePath, customerID) + resource := new(CustomerAddressesResource) + err := s.client.Get(path, resource, options) + return resource.Addresses, err +} + +// Get address +func (s *CustomerAddressServiceOp) Get(customerID, addressID int, options interface{}) (*CustomerAddress, error) { + path := fmt.Sprintf("%s/%d/addresses/%d.json", customersBasePath, customerID, addressID) + resource := new(CustomerAddressResource) + err := s.client.Get(path, resource, options) + return resource.Address, err +} + +// Create a new address for given customer +func (s *CustomerAddressServiceOp) Create(customerID int, address CustomerAddress) (*CustomerAddress, error) { + path := fmt.Sprintf("%s/%d/addresses.json", customersBasePath, customerID) + wrappedData := CustomerAddressResource{Address: &address} + resource := new(CustomerAddressResource) + err := s.client.Post(path, wrappedData, resource) + return resource.Address, err +} + +// Create a new address for given customer +func (s *CustomerAddressServiceOp) Update(customerID int, address CustomerAddress) (*CustomerAddress, error) { + path := fmt.Sprintf("%s/%d/addresses/%d.json", customersBasePath, customerID, address.ID) + wrappedData := CustomerAddressResource{Address: &address} + resource := new(CustomerAddressResource) + err := s.client.Put(path, wrappedData, resource) + return resource.Address, err +} + +// Delete an existing address +func (s *CustomerAddressServiceOp) Delete(customerID, addressID int) error { + return s.client.Delete(fmt.Sprintf("%s/%d/addresses/%d.json", customersBasePath, customerID, addressID)) +} diff --git a/customer_address_test.go b/customer_address_test.go new file mode 100644 index 00000000..b81ec766 --- /dev/null +++ b/customer_address_test.go @@ -0,0 +1,165 @@ +package goshopify + +import ( + "testing" + + httpmock "gopkg.in/jarcoal/httpmock.v1" +) + +func verifyAddress(t *testing.T, address CustomerAddress) { + expectedID := 1 + if address.ID != expectedID { + t.Errorf("CustomerAddress.ID returned %+v, expected %+v", address.ID, expectedID) + } + + expectedCustomerID := 1 + if address.CustomerID != expectedCustomerID { + t.Errorf("CustomerAddress.CustomerID returned %+v, expected %+v", address.CustomerID, expectedCustomerID) + } + + expectedFirstName := "Test" + if address.FirstName != expectedFirstName { + t.Errorf("CustomerAddress.FirstName returned %+v, expected %+v", address.FirstName, expectedFirstName) + } + + expectedLastName := "Citizen" + if address.LastName != expectedLastName { + t.Errorf("CustomerAddress.LastName returned %+v, expected %+v", address.LastName, expectedLastName) + } + + expectedCompany := "TestCo" + if address.Company != expectedCompany { + t.Errorf("CustomerAddress.Company returned %+v, expected %+v", address.Company, expectedCompany) + } + + expectedAddress1 := "1 Smith St" + if address.Address1 != expectedAddress1 { + t.Errorf("CustomerAddress.Address1 returned %+v, expected %+v", address.Address1, expectedAddress1) + } + + expectedAddress2 := "" + if address.Address2 != expectedAddress2 { + t.Errorf("CustomerAddress.Address2 returned %+v, expected %+v", address.Address2, expectedAddress2) + } + + expectedCity := "BRISBANE" + if address.City != expectedCity { + t.Errorf("CustomerAddress.City returned %+v, expected %+v", address.City, expectedCity) + } + + expectedProvince := "Queensland" + if address.Province != expectedProvince { + t.Errorf("CustomerAddress.Province returned %+v, expected %+v", address.Province, expectedProvince) + } + + expectedCountry := "Australia" + if address.Country != expectedCountry { + t.Errorf("CustomerAddress.Country returned %+v, expected %+v", address.Country, expectedCountry) + } + + expectedZip := "4000" + if address.Zip != expectedZip { + t.Errorf("CustomerAddress.Zip returned %+v, expected %+v", address.Zip, expectedZip) + } + + expectedPhone := "1111 111 111" + if address.Phone != expectedPhone { + t.Errorf("CustomerAddress.Phone returned %+v, expected %+v", address.Phone, expectedPhone) + } + + expectedName := "Test Citizen" + if address.Name != expectedName { + t.Errorf("CustomerAddress.Name returned %+v, expected %+v", address.Name, expectedName) + } + + expectedProvinceCode := "QLD" + if address.ProvinceCode != expectedProvinceCode { + t.Errorf("CustomerAddress.ProvinceCode returned %+v, expected %+v", address.ProvinceCode, expectedProvinceCode) + } + + expectedCountryCode := "AU" + if address.CountryCode != expectedCountryCode { + t.Errorf("CustomerAddress.CountryCode returned %+v, expected %+v", address.CountryCode, expectedCountryCode) + } + + expectedCountryName := "Australia" + if address.CountryName != expectedCountryName { + t.Errorf("CustomerAddress.CountryName returned %+v, expected %+v", address.CountryName, expectedCountryName) + } + + expectedDefault := true + if address.Default != expectedDefault { + t.Errorf("CustomerAddress.Default returned %+v, expected %+v", address.Default, expectedDefault) + } +} + +func TestList(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("GET", "https://fooshop.myshopify.com/admin/customers/1/addresses.json", httpmock.NewBytesResponder(200, loadFixture("customer_addresses.json"))) + + addresses, err := client.CustomerAddress.List(1, nil) + if err != nil { + t.Errorf("CustomerAddress.List returned error: %v", err) + } + + if len(addresses) != 2 { + t.Errorf("CustomerAddress.List got %v addresses, expected 2", len(addresses)) + } + verifyAddress(t, addresses[0]) +} + +func TestGet(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("GET", "https://fooshop.myshopify.com/admin/customers/1/addresses/1.json", httpmock.NewBytesResponder(200, loadFixture("customer_address.json"))) + + address, err := client.CustomerAddress.Get(1, 1, nil) + if err != nil { + t.Errorf("CustomerAddress.Get returned error: %v", err) + } + + verifyAddress(t, *address) +} + +func TestCreate(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("POST", "https://fooshop.myshopify.com/admin/customers/1/addresses.json", httpmock.NewBytesResponder(200, loadFixture("customer_address.json"))) + + address, err := client.CustomerAddress.Create(1, CustomerAddress{}) + if err != nil { + t.Errorf("CustomerAddress.Create returned error: %v", err) + } + + verifyAddress(t, *address) +} + +func TestUpdate(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("PUT", "https://fooshop.myshopify.com/admin/customers/1/addresses/1.json", httpmock.NewBytesResponder(200, loadFixture("customer_address.json"))) + + address, err := client.CustomerAddress.Update(1, CustomerAddress{ID: 1}) + if err != nil { + t.Errorf("CustomerAddress.Update returned error: %v", err) + } + + verifyAddress(t, *address) +} + +func TestDelete(t *testing.T) { + setup() + defer teardown() + + httpmock.RegisterResponder("DELETE", "https://fooshop.myshopify.com/admin/customers/1/addresses/1.json", httpmock.NewStringResponder(200, "{}")) + + err := client.CustomerAddress.Delete(1, 1) + if err != nil { + t.Errorf("CustomerAddress.Update returned error: %v", err) + } +} diff --git a/fixtures/customer_address.json b/fixtures/customer_address.json new file mode 100644 index 00000000..775dafd5 --- /dev/null +++ b/fixtures/customer_address.json @@ -0,0 +1,21 @@ +{ + "customer_address": { + "address1": "1 Smith St", + "address2": null, + "city": "BRISBANE", + "company": "TestCo", + "country": "Australia", + "country_code": "AU", + "country_name": "Australia", + "customer_id": 1, + "default": true, + "first_name": "Test", + "id": 1, + "last_name": "Citizen", + "name": "Test Citizen", + "phone": "1111 111 111", + "province": "Queensland", + "province_code": "QLD", + "zip": "4000" + } +} \ No newline at end of file diff --git a/fixtures/customer_addresses.json b/fixtures/customer_addresses.json new file mode 100644 index 00000000..ddcf423a --- /dev/null +++ b/fixtures/customer_addresses.json @@ -0,0 +1,42 @@ +{ + "addresses": [ + { + "address1": "1 Smith St", + "address2": null, + "city": "BRISBANE", + "company": "TestCo", + "country": "Australia", + "country_code": "AU", + "country_name": "Australia", + "customer_id": 1, + "default": true, + "first_name": "Test", + "id": 1, + "last_name": "Citizen", + "name": "Test Citizen", + "phone": "1111 111 111", + "province": "Queensland", + "province_code": "QLD", + "zip": "4000" + }, + { + "address1": "2 Smith St", + "address2": null, + "city": "BRISBANE", + "company": null, + "country": "Australia", + "country_code": "AU", + "country_name": "Australia", + "customer_id": 1, + "default": true, + "first_name": "Test", + "id": 2, + "last_name": "Citizen", + "name": "Test Citizen", + "phone": "2222 222 222", + "province": "Queensland", + "province_code": "QLD", + "zip": "4000" + } + ] +} \ No newline at end of file diff --git a/goshopify.go b/goshopify.go index fb7c4077..ee38a465 100644 --- a/goshopify.go +++ b/goshopify.go @@ -52,6 +52,7 @@ type Client struct { CustomCollection CustomCollectionService SmartCollection SmartCollectionService Customer CustomerService + CustomerAddress CustomerAddressService Order OrderService Shop ShopService Webhook WebhookService @@ -187,6 +188,7 @@ func NewClient(app App, shopName, token string) *Client { c.CustomCollection = &CustomCollectionServiceOp{client: c} c.SmartCollection = &SmartCollectionServiceOp{client: c} c.Customer = &CustomerServiceOp{client: c} + c.CustomerAddress = &CustomerAddressServiceOp{client: c} c.Order = &OrderServiceOp{client: c} c.Shop = &ShopServiceOp{client: c} c.Webhook = &WebhookServiceOp{client: c}