shopify

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2021 License: MIT Imports: 15 Imported by: 0

README

shopify

Shopify GraphQL and Restful Admin API client

Docs of this package

Shopify GraphQL Admin API Docs

Shopify REST Admin API Docs

Create Apps

Usage

New client from static token
import "github.com/caiguanhao/shopify"

src := shopify.Oauth2StaticTokenSource(
	&shopify.Oauth2Token{AccessToken: os.Getenv("SHOPIFY_TOKEN")},
)
httpClient := shopify.Oauth2NewClient(context.Background(), src)
client := shopify.NewClient(os.Getenv("SHOPIFY_SHOP"), httpClient)
Put results into custom structs
var customers []struct {
	Id    string `json:"id"`
	Email string `json:"email"`
}
client.New(`query { customers(first: 10) { edges { node { id email } } } }`).
	MustDo(&customers, "customers.edges.*.node")
Pagination
var cursor *string
var hasNextPage bool = true
for hasNextPage {
	client.New(`query ($n: Int, $after: String) {
customers(first: $n, after: $after) {
pageInfo { hasNextPage }
edges { cursor node { id email } } } }`, "n", 2, "after", cursor).
		MustDo(
			&customers, "customers.edges.*.node",
			&cursor, "customers.edges.*.cursor",
			&hasNextPage, "customers.pageInfo.hasNextPage",
		)
}
Restful API
// get themes
var themes []struct {
	Id   int
	Role string
}
client.NewRest("GET", "themes").MustDo(&themes, "themes.*")

// get files of a specific theme
var files []string
client.NewRest("GET", fmt.Sprintf("themes/%d/assets", 100000000000), shopify.KV{
	"fields": "key",
}).MustDo(&files, "assets.*.key")

// update theme file
var updatedAt string
client.NewRest("PUT", fmt.Sprintf("themes/%d/assets", 100000000000), nil, shopify.KV{
	"asset": shopify.KV{
		"key":   "layout/theme.liquid",
		"value": "<html>...</html>",
	},
}).MustDo(&updatedAt, "asset.updated_at")
Multiple queries in one request

You can use NewMulti():

var url, code string
gql, args, targets := shopify.NewMulti("query").
	Add("currentAppInstallation").Return(`{ launchUrl }`).
	Out(&url, ".launchUrl").Self().
	Add("shop").Return("{ currencyCode }").
	Out(&code, ".currencyCode").Self().
	Do()
client.New(gql, args...).MustDo(targets...)
fmt.Println(url, code)

Or write your own GQL:

var launchUrl, currencyCode string
client.New("{ cai: currentAppInstallation { launchUrl }, shop: shop { currencyCode } }").
	MustDo(&launchUrl, "cai.launchUrl", &currencyCode, "shop.currencyCode")
fmt.Println(launchUrl, currencyCode)
Oauth2 Example

Create a new App and put http://127.0.0.1/hello to "Allowed redirection URL(s)".

Copy the API key as ClientID, API secret key as ClientSecret.

conf := &shopify.Oauth2Config{
	ClientID:     "f75xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
	ClientSecret: "shpss_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
	Scopes: []string{
		"read_customers",
		// for list of access scopes, visit:
		// https://shopify.dev/api/usage/access-scopes
	},
	RedirectURL: "http://127.0.0.1/hello",
	Endpoint: shopify.Oauth2Endpoint{
		AuthURL:  "https://<YOUR-SHOP-NAME>.myshopify.com/admin/oauth/authorize",
		TokenURL: "https://<YOUR-SHOP-NAME>.myshopify.com/admin/oauth/access_token",
	},
}

// redirect user to consent page to ask for permission
fmt.Println(conf.AuthCodeURL("state"))

// once user installed the app, it will redirect to url like this:
// http://127.0.0.1/hello?code=codexxxxxxxxxxxxxxxxxxxxxxxxxxxx&hmac=...
// verify the request then get the token with the code in the url
token, err := conf.Exchange(ctx, "codexxxxxxxxxxxxxxxxxxxxxxxxxxxx")
if err != nil {
	log.Fatal(err)
}

// save the token as json for later use
json.Marshal(token)

// load the token
ctx := context.Background()
token := new(shopify.Oauth2Token)
json.Unmarshal(/* token content */, token)
src := conf.TokenSource(ctx, token)
httpClient := shopify.Oauth2NewClient(ctx, src)
client := shopify.NewClient(/* your shop name */, httpClient)

Test

export SHOPIFY_TOKEN=shpat_00000000000000000000000000000000 SHOPIFY_SHOP=demo
export DEBUG=1 # if you want to show more info
go test -v ./...

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Oauth2StaticTokenSource = oauth2.StaticTokenSource
	Oauth2NewClient         = oauth2.NewClient
)
View Source
var (
	ErrUnauthorized = errors.New("401 Unauthorized: incorrect authentication credential")
)

Functions

func RemoveZeroValues added in v1.2.0

func RemoveZeroValues(slice interface{}) (out []interface{})

Remove any zero value in a slice.

func Slice added in v1.2.0

func Slice(slice interface{}) (out []interface{})

Turn any slice into slice of interface.

Types

type Client

type Client struct {
	Debug bool   // print request and response body if true
	Shop  string // shop name
	// contains filtered or unexported fields
}

func NewClient

func NewClient(shop string, httpClient *http.Client) *Client

Create a new client with shop name and http client.

func (*Client) New

func (client *Client) New(gql string, variables ...interface{}) *Request

Prepare a new GraphQL query or mutation. Variables must be provided in key-value pair order.

func (*Client) NewRest added in v1.1.0

func (client *Client) NewRest(method, route string, params ...interface{}) *RestRequest

Prepare a new restful request given method and route name. First params is request query and second params is request body.

func (*Client) UploadJSONL

func (client *Client) UploadJSONL(jsonl string) (string, error)

UploadJSONL wraps UploadJSONLWithContext using context.Background.

func (*Client) UploadJSONLWithContext

func (client *Client) UploadJSONLWithContext(ctx context.Context, jsonl string) (key string, err error)

Upload JSONL string and return the stagedUploadPath used in bulkOperationRunMutation.

type Error

type Error struct {
	Message   string `json:"message"`
	Locations []struct {
		Line   int
		Column int
	} `json:"locations"`
	Path []interface{} `json:"path"`
}

Error response

type Errors

type Errors []Error

func (Errors) Error

func (errs Errors) Error() string

type KV added in v1.1.0

type KV = map[string]interface{}

type Multi added in v1.3.0

type Multi struct {
	// contains filtered or unexported fields
}

func NewMulti added in v1.2.0

func NewMulti(operationType string) *Multi

NewMulti creates a chain to define operations and output destinations, allowing to generate one single GraphQL query to execute multiple GraphQL queries or mutations at the same time.

func (*Multi) Add added in v1.3.0

func (m *Multi) Add(operation string, operationArgTypes ...string) *MultiItem

Add defines a new operation with its argument types. The operation argument types must be in the format of "name: type".

func (*Multi) Do added in v1.3.0

func (m *Multi) Do() (gql string, args, targets []interface{})

Do generates gql, args and targets that can be used in Client.New() and Request.Do().

func (Multi) Len added in v1.3.1

func (m Multi) Len() int

Len returns number of items in the chain.

func (Multi) String added in v1.3.0

func (m Multi) String() string

type MultiItem added in v1.3.0

type MultiItem struct {
	// contains filtered or unexported fields
}

func (*MultiItem) In added in v1.3.0

func (mi *MultiItem) In(args ...interface{}) *MultiItem

In puts input values in the order of the operation argument types to the chain.

func (*MultiItem) Out added in v1.3.0

func (mi *MultiItem) Out(targets ...interface{}) *MultiItem

Out puts destinations to slices followed by a JSON path to the chain.

func (*MultiItem) Return added in v1.3.0

func (mi *MultiItem) Return(body string) *MultiItem

Return defines output fields of a operation. This is the body of the GraphQL query. If the body starts with corresponding type, then fragment will be used.

func (*MultiItem) Self added in v1.3.0

func (mi *MultiItem) Self() *Multi

Self returns the chain.

func (MultiItem) String added in v1.3.0

func (mi MultiItem) String() string

type Oauth2Config

type Oauth2Config = oauth2.Config

type Oauth2Endpoint

type Oauth2Endpoint = oauth2.Endpoint

type Oauth2Token

type Oauth2Token = oauth2.Token

type Oauth2Transport

type Oauth2Transport = oauth2.Transport

type Request

type Request struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables,omitempty"`
	// contains filtered or unexported fields
}

func (*Request) Do

func (req *Request) Do(dest ...interface{}) error

Execute the GraphQL query or mutation and unmarshal JSON response into the optional dest. Specify JSON path after each dest to efficiently get required info from deep nested structs.

func (*Request) MustDo

func (req *Request) MustDo(dest ...interface{})

MustDo is like Do but panics if operation fails.

func (*Request) WithContext

func (req *Request) WithContext(ctx context.Context) *Request

Set context.

type RestRequest added in v1.1.0

type RestRequest struct {
	// contains filtered or unexported fields
}

func (*RestRequest) Do added in v1.1.0

func (req *RestRequest) Do(dest ...interface{}) error

Execute the restful request and unmarshal JSON response into the optional dest. Specify JSON path after each dest to efficiently get required info from deep nested structs.

func (*RestRequest) MustDo added in v1.1.0

func (req *RestRequest) MustDo(dest ...interface{})

MustDo is like Do but panics if operation fails.

func (*RestRequest) WithContext added in v1.1.0

func (req *RestRequest) WithContext(ctx context.Context) *RestRequest

Set context.

type UserError

type UserError struct {
	Field   []string `json:"field"`
	Message string   `json:"message"`
}

Error of mutation input

type UserErrors

type UserErrors []UserError

func (UserErrors) Error

func (errs UserErrors) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL