Skip to content

Commit

Permalink
Sync go-shopify with upstream #6
Browse files Browse the repository at this point in the history
  • Loading branch information
ttyfky authored Jun 29, 2023
2 parents d7b62d0 + 7b76d34 commit 5bd67da
Show file tree
Hide file tree
Showing 62 changed files with 2,623 additions and 322 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Go

on: [push, pull_request]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.18', '1.19', '1.20' ]

steps:
- uses: actions/checkout@v3
- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Build
run: go build -v ./...
- name: Test
run: go test -coverprofile=coverage.txt -v ./...
- name: Upload code coverage results
run: bash <(curl -s https://codecov.io/bash)
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@ The new home of Bold Commerce's Shopify Go library which is originally forked fr
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/MIT)
## Supported Go Versions

This library has been tested against the following versions of Go
* 1.10
* 1.11
* 1.12
* 1.13
* 1.14
* 1.15
* 1.16
This library is tested automatically against the latest version of Go (currently 1.20) and the two previous versions (1.19, 1.18) but should also work with older versions.

## Install

Expand Down Expand Up @@ -108,24 +101,27 @@ client := goshopify.NewClient(app, "shopname", "")
// Fetch the number of products.
numProducts, err := client.Product.Count(nil)
```

### Client Options
When creating a client there are configuration options you can pass to NewClient. Simply use the last variadic param and
pass in the built in options or create your own and manipulate the client. See [options.go](https://github.com/belong-inc/go-shopify/blob/master/options.go)
for more details.

#### WithVersion

Read more details on the [Shopify API Versioning](https://shopify.dev/concepts/about-apis/versioning)
to understand the format and release schedules. You can use `WithVersion` to specify a specific version
to understand the format and release schedules. You can use `WithVersion` to specify a specific version
of the API. If you do not use this option you will be defaulted to the oldest stable API.

```go
client := goshopify.NewClient(app, "shopname", "", goshopify.WithVersion("2019-04"))
```

#### WithRetry
Shopify [Rate Limits](https://shopify.dev/concepts/about-apis/rate-limits) their API and if this happens to you they
will send a back off (usually 2s) to tell you to retry your request. To support this functionality seamlessly within
the client a `WithRetry` option exists where you can pass an `int` of how many times you wish to retry per-request

Shopify [Rate Limits](https://shopify.dev/concepts/about-apis/rate-limits) their API and if this happens to you they
will send a back off (usually 2s) to tell you to retry your request. To support this functionality seamlessly within
the client a `WithRetry` option exists where you can pass an `int` of how many times you wish to retry per-request
before returning an error. `WithRetry` additionally supports retrying HTTP503 errors.

```go
Expand Down Expand Up @@ -202,6 +198,7 @@ In order to be sure that a webhook is sent from ShopifyApi you could easily veri
it with the `VerifyWebhookRequest` method.

For example:

```go
func ValidateWebhook(httpRequest *http.Request) (bool) {
shopifyApp := goshopify.App{ApiSecret: "ratz"}
Expand All @@ -210,38 +207,47 @@ func ValidateWebhook(httpRequest *http.Request) (bool) {
```

## Develop and test

`docker` and `docker-compose` must be installed

### Mac/Linux/Windows with make

Using the make file is the easiest way to get started with the tests and wraps the manual steps below with easy to use
make commands.

```shell
make && make test
```

#### Makefile goals
* `make` or `make container`: default goal is to make the `go-shopify:latest` build container
* `make test`: run go test in the container
* `make clean`: deletes the `go-shopify:latest` image and coverage output
* `make coverage`: generates the coverage.html and opens it

- `make` or `make container`: default goal is to make the `go-shopify:latest` build container
- `make test`: run go test in the container
- `make clean`: deletes the `go-shopify:latest` image and coverage output
- `make coverage`: generates the coverage.html and opens it

### Manually

To run the tests you will need the `go-shopify:latest` image built to run your tests, to do this run

```
docker-compose build test
```

To run tests you can use run

```shell
docker-compose run --rm tests
```

To create a coverage profile run the following to generate a coverage.html

```
docker-compose run --rm dev sh -c 'go test -coverprofile=coverage.out ./... && go tool cover -html coverage.out -o coverage.html'
```

When done testing and you want to cleanup simply run

```
docker image rm go-shopify:latest
```
Expand Down
91 changes: 91 additions & 0 deletions abandoned_checkout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package goshopify

import (
"fmt"
"time"

"github.com/shopspring/decimal"
)

const abandonedCheckoutsBasePath = "checkouts"

// AbandonedCheckoutService is an interface for interfacing with the abandonedCheckouts endpoints
// of the Shopify API.
// See: https://shopify.dev/docs/api/admin-rest/latest/resources/abandoned-checkouts
type AbandonedCheckoutService interface {
List(interface{}) ([]AbandonedCheckout, error)
}

// AbandonedCheckoutServiceOp handles communication with the checkout related methods of
// the Shopify API.
type AbandonedCheckoutServiceOp struct {
client *Client
}

// Represents the result from the checkouts.json endpoint
type AbandonedCheckoutsResource struct {
AbandonedCheckouts []AbandonedCheckout `json:"checkouts,omitempty"`
}

// AbandonedCheckout represents a Shopify abandoned checkout
type AbandonedCheckout struct {
ID int64 `json:"id,omitempty"`
Token string `json:"token,omitempty"`
CartToken string `json:"cart_token,omitempty"`
Email string `json:"email,omitempty"`
Gateway string `json:"gateway,omitempty"`
BuyerAcceptsMarketing bool `json:"buyer_accepts_marketing,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
LandingSite string `json:"landing_site,omitempty"`
Note string `json:"note,omitempty"`
NoteAttributes []NoteAttribute `json:"note_attributes,omitempty"`
ReferringSite string `json:"referring_site,omitempty"`
ShippingLines []ShippingLines `json:"shipping_lines,omitempty"`
TaxesIncluded bool `json:"taxes_included,omitempty"`
TotalWeight int `json:"total_weight,omitempty"`
Currency string `json:"currency,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
UserID int64 `json:"user_id,omitempty"`
SourceIdentifier string `json:"source_identifier,omitempty"`
SourceUrl string `json:"source_url,omitempty"`
DeviceID int64 `json:"device_id,omitempty"`
Phone string `json:"phone,omitempty"`
CustomerLocale string `json:"customer_locale,omitempty"`
Name string `json:"name,omitempty"`
Source string `json:"source,omitempty"`
AbandonedCheckoutUrl string `json:"abandoned_checkout_url,omitempty"`
DiscountCodes []DiscountCode `json:"discount_codes,omitempty"`
TaxLines []TaxLine `json:"tax_lines,omitempty"`
SourceName string `json:"source_name,omitempty"`
PresentmentCurrency string `json:"presentment_currency,omitempty"`
BuyerAcceptsSmsMarketing bool `json:"buyer_accepts_sms_marketing,omitempty"`
SmsMarketingPhone string `json:"sms_marketing_phone,omitempty"`
TotalDiscounts *decimal.Decimal `json:"total_discounts,omitempty"`
TotalLineItemsPrice *decimal.Decimal `json:"total_line_items_price,omitempty"`
TotalPrice *decimal.Decimal `json:"total_price,omitempty"`
SubtotalPrice *decimal.Decimal `json:"subtotal_price,omitempty"`
TotalDuties string `json:"total_duties,omitempty"`
BillingAddress *Address `json:"billing_address,omitempty"`
ShippingAddress *Address `json:"shipping_address,omitempty"`
Customer *Customer `json:"customer,omitempty"`
SmsMarketingConsent *SmsMarketingConsent `json:"sms_marketing_consent,omitempty"`
AdminGraphqlApiID string `json:"admin_graphql_api_id,omitempty"`
DefaultAddress *CustomerAddress `json:"default_address,omitempty"`
}

type SmsMarketingConsent struct {
State string `json:"state,omitempty"`
OptInLevel string `json:"opt_in_level,omitempty"`
ConsentUpdatedAt *time.Time `json:"consent_updated_at,omitempty"`
ConsentCollectedFrom string `json:"consent_collected_from,omitempty"`
}

// Get abandoned checkout list
func (s *AbandonedCheckoutServiceOp) List(options interface{}) ([]AbandonedCheckout, error) {
path := fmt.Sprintf("/%s.json", abandonedCheckoutsBasePath)
resource := new(AbandonedCheckoutsResource)
err := s.client.Get(path, resource, options)
return resource.AbandonedCheckouts, err
}
34 changes: 34 additions & 0 deletions abandoned_checkout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package goshopify

import (
"fmt"
"reflect"
"testing"

"github.com/jarcoal/httpmock"
)

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

httpmock.RegisterResponder(
"GET",
fmt.Sprintf("https://fooshop.myshopify.com/%s/checkouts.json", client.pathPrefix),
httpmock.NewStringResponder(
200,
`{"checkouts": [{"id":1},{"id":2}]}`,
),
)

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

expected := []AbandonedCheckout{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(abandonedCheckouts, expected) {
t.Errorf("AbandonedCheckout.List returned %+v, expected %+v", abandonedCheckouts, expected)
}

}
2 changes: 1 addition & 1 deletion access_scopes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type AccessScopesResource struct {
// AccessScopesServiceOp handles communication with the Access Scopes
// related methods of the Shopify API
type AccessScopesServiceOp struct {
client *Client
client *Client
}

// List gets access scopes based on used oauth token
Expand Down
2 changes: 1 addition & 1 deletion access_scopes_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package goshopify

import (
"testing"
"fmt"
"reflect"
"testing"

"github.com/jarcoal/httpmock"
)
Expand Down
22 changes: 11 additions & 11 deletions asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ type AssetServiceOp struct {

// Asset represents a Shopify asset
type Asset struct {
Attachment string `json:"attachment"`
ContentType string `json:"content_type"`
Key string `json:"key"`
PublicURL string `json:"public_url"`
Size int `json:"size"`
SourceKey string `json:"source_key"`
Src string `json:"src"`
ThemeID int64 `json:"theme_id"`
Value string `json:"value"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
Attachment string `json:"attachment,omitempty"`
ContentType string `json:"content_type,omitempty"`
Key string `json:"key,omitempty"`
PublicURL string `json:"public_url,omitempty"`
Size int `json:"size,omitempty"`
SourceKey string `json:"source_key,omitempty"`
Src string `json:"src,omitempty"`
ThemeID int64 `json:"theme_id,omitempty"`
Value string `json:"value,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}

// AssetResource is the result from the themes/x/assets.json?asset[key]= endpoint
Expand Down
1 change: 1 addition & 0 deletions blog.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Blog struct {
TemplateSuffix string `json:"template_suffix"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
AdminGraphqlAPIID string `json:"admin_graphql_api_id,omitempty"`
}

// BlogsResource is the result from the blogs.json endpoint
Expand Down
Loading

0 comments on commit 5bd67da

Please sign in to comment.