Git Product home page Git Product logo

go-shopify's People

Contributors

amwolff avatar andrewhoff avatar aryy avatar at-silva avatar baorv avatar boldbrandonm avatar c9845 avatar ckeyes88 avatar daisukefunase avatar dbertouille avatar dlebech avatar fidel-ruiz avatar gir avatar gordcurrie avatar jared-fraser avatar jerairrest avatar luthermonson avatar milos-matijasevic avatar mitchhamm avatar oliver006 avatar orian avatar ranabram avatar rickywiens avatar roelofjan-elsinga avatar spprichard avatar sradevski avatar sshaw avatar sudomake avatar umsu2 avatar weirdian2k3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-shopify's Issues

Feature Request: Order Cancellation + Order Close

Admin REST APIs that would be beneficial to implement:

  1. Order Cancellation
  2. Order Close

Would be nice to see following methods as a result:

client := goshopify.NewClient(app, tenant, token)

cancelInput := goshopify.OrderCancelInput{ 
  OrderID: 1, // shopify id of order
  // rest of fields are optional fields documented in request parameters section of docs
}
if err := client.Order.Cancel(cancelInput); err != nil {
  // handle error
}

closeInput := goshopify.OrderCloseInput{
  OrderID: 1, // shopify id of order
  // rest of fields are optional fields documented in request parameters section of docs
}
if err := client.Order.Close(closeInput); err != nil {
  // handle error
}

Thoughts?

Unable to use Metafields on 2021-07

While working with the Metafields API cleitn exposed by this package, I noticed that we're currently unable to work with Metafields on Shopify version 2021-07 since there was a breaking change to the model.

This is a blocker for some work of ours, but the change to this library can be non-breaking and transparently support both the old and new approaches.

RecurringApplicationCharge cannot update the usage cap to a decimal amount

func (r *RecurringApplicationChargeServiceOp) Update(chargeID, newCappedAmount int) (
	*RecurringApplicationCharge, error) {

	path := fmt.Sprintf("%s/%d/customize.json?recurring_application_charge[capped_amount]=%d",
		recurringApplicationChargesBasePath, chargeID, newCappedAmount)
	resource := &RecurringApplicationChargeResource{}
	err := r.client.Put(path, nil, resource)
	return resource.Charge, err
}

the newCappedAmount should be the type decimal instead of an int.
however the change would be break the original interface.

How to utilize RateLimitError?

I am making calls to Product.List but after sometime I think I am hitting the api limits set by Shopify.
How do I know that it is a RateLimitError from the err of Product,List call? And how do I access the RetryAfter attribute?

SinceID as Zero and omitempty

Problem
SinceId is an int64 but the API documentation tells you to pass since_id=0 to work with paginated results. I found a problem in the smartcollections end point where it sorts by alpha title on default so in order to force an id sort you have to to use since_id=0 properly and have to initialize that param to force the id sort. With omitempty on SinceID the marshaler will consider this empty and not send the param even if you explicitly set it to int64(0) since obviously omitempty considers zero an empty int64 type. Also, if you don't set the param the struct will still initialize with zero and you dont want to pass since_id=0 ALL the time so you can't just remove omitempty.

Workaround
Initialize SinceID as int64(1) as it is basically the same thing for all intents and purposes but is not technically not what the api documents as the proper method and could trip up a developer.

Fix?
Possibly just document as a limitation on the struct field

Cursor-based pagination for orders

Hello,
I received the warning from shopify that I am using deprecated API for the pagination. Is there a way to do cursor-based pagination for orders? I looked at the commit history and it looks like only products can do that at the moment, is that not supported for orders yet?

Support presentment_prices in variants

What is the plan to add presentment price in the variant object

"presentment_prices": [
      {
        "price": {
          "currency_code": "USD",
          "amount": "199.99"
        },
        "compare_at_price": null
      },
      {
        "price": {
          "currency_code": "EUR",
          "amount": "158.95"
        },
        "compare_at_price": null
      },
      {
        "price": {
          "currency_code": "GBP",
          "amount": "143.00"
        },
        "compare_at_price": null
      },
      {
        "price": {
          "currency_code": "JPY",
          "amount": "22400"
        },
        "compare_at_price": null
      }
    ]

Context in requests - beginning of a breaking change discussion

From @orian on June 8, 2018 7:59

It should be possible to add context.Context to requests. It's particularly useful for a request we want to control a timeout, so one can use context.WithTimeout().

Not sure about how to make it as the current implementation assumes that options is just query.Values.
The go' standard way is to pass context as first argument.

Copied from original issue: getconversio#93

AWS EventBridge Support missing

Shopify has support for AWS EventBridge(here), which can be used to integrate webhook delivery.

When trying to use the current Create method like this:-

resp, err := c.Webhook.Create(goshopify.Webhook{
	Address: "arn:aws:events:ap-south-1::event-source/aws.partner/shopify.com/xxxxxx7/somecustomrule",
	Topic:   "orders/create",
	Format:  "json",
})

The response received is address: is invalid

The official node api has a DeliveryMethod field

omitempty tag prevents fulfillment.notify_customer from being usable at all

From Shopify's REST API Doc on Fulfillments:

notify_customer: Whether the customer should be notified. If set to true, then an email will be sent when the fulfillment is created or updated. For orders that were initially created using the API, the default value is false. For all other orders, the default value is true.Whether the customer should be notified. If set to true, then an email will be sent when the fulfillment is created or updated. For orders that were initially created using the API, the default value is false. For all other orders, the default value is true.

The associated PR #56 removes omitempty from the JSON tag because it prevents false from being sent, and thus the default behavior from being overriden.

Why interfaces for all data operation inputs?

Hello,

Having used this library a bit I don't see why all the input options for operations on endpoints take interface{} as input. It makes sense for the underlying CreateAndDo request helper but for user code calling something like Product.List() or Product.Count() why isn't it taking a pointer(optionally nil) to CountOptions or ListOptions? I don't believe these can be customized or that you'd have any reason to pass in other data.

Perhaps I've overlooked something but any confirmation on this one way or the other would be great. I'm contemplating a patch to switch to proper types and also potentially add context support as a v4 upgrade.

Any attempt to create or update variants fails with error

Hi there,

I'm consistently getting the following error whenever I attempt to create or change a variant using this library:

Write requests to inventory_quantity and inventory_quantity_adjustment are no longer supported. Please use the Inventory Levels API.

For reference, here is a snippet of code where I'm attempting the change:

	ok := YesNoPrompt("Update Shopify Price?", true)
		if ok {
			fmt.Println("Updating Shopify Price")
			currentVariant, err := client.Variant.Get(foundVariantID, nil)
			assertNoErr(err)
			// update price of local variant
			currentVariant.Price = &shopifyPrice
			// update remote variant
			newVar, err := client.Variant.Create(foundProductID, *currentVariant)
			assertNoErr(err)

			fmt.Printf("New variant price is: %v\n", newVar.Price.String())
			// verify update
		}```

Can't Use Multiple Versioned API Clients

I'm about to do this refactor and PR it but wanted to document why you would want to do this. There is a limitation of using the package level variable globalApiPathPrefix. If you call goshopify.NewClient using WithVersion it will always set all previous Clients to the last version passed. The global string should be refactored to a Client.pathPrefix and a first pass refactor could be

path := fmt.Sprintf("%s/%s.json", globalApiPathPrefix, locationsBasePath)
// becomes
path := fmt.Sprintf("%s/%s.json", s.client.pathPrefix, locationsBasePath)

but I would prefer the prefix was prepended in Client.CreateAndDo so you don't have to worry about it at the service level since the client should really dictate where all calls are going and you shouldn't be able to re-path which api version you're using in the Services but could be convinced otherwise. This is the version I will PR so if @gordcurrie or somebody else wants to stop me before I get there then please respond.

e.g. of how to recreate this

func newStableShopifyClient() *goshopify.Client {
	return goshopify.NewClient(shopifyApp, shopifyName, "")
}

func newVersionedShopifyClient(version string) *goshopify.Client {
	return goshopify.NewClient(shopifyApp, shopifyName, "", goshopify.WithVersion(version))
}

_ = newStableShopifyClient()
fmt.Println(goshopify.MetafieldPathPrefix("test", 1234))
_ = newVersionedShopifyClient("2019-04")
fmt.Println(goshopify.MetafieldPathPrefix("test", 1234))

returns something like this

admin/test/1234/metafields
admin/api/2019-04/test/1234/metafields

style: goshopify.App struct member names do not follow standard go style guidlines

From @apkrieg on April 30, 2018 15:56

app := goshopify.App{
    ApiKey: "abcd",
    ApiSecret: "efgh",
    RedirectUrl: "https://example.com/shopify/callback",
    Scope: "read_products,read_orders",
}

should look like:

app := goshopify.App{
    APIKey: "abcd",
    APISecret: "efgh",
    RedirectURL: "https://example.com/shopify/callback",
    Scope: "read_products,read_orders",
}

I understand the backwards compatibility thing, but maybe we can start working on version 2.X.X and fix this?

Cites:
https://github.com/golang/lint/blob/master/lint.go#L749

Copied from original issue: getconversio#78

Maybe remove the panic when creating a new client

panic(err) // something really wrong with shopName

How is my code working now:

import (
    goshopify "github.com/bold-commerce/go-shopify"
    "net/url"
)
func NewClient(app goshopify.App, shopName string, token string) (*goshopify.Client, error){
    _, err := url.Parse(shopName)
    
    if err != nil {
        return nil, err
    }

    return app.NewClient(shopName, token), nil
}

I think it would be right:

func (a App) NewClient(shopName, token string, opts ...Option) (*Client, error) {
	return NewClient(a, shopName, token, opts...)
}
func NewClient(app App, shopName, token string, opts ...Option) (*Client, error) {
    baseURL, err := url.Parse(ShopBaseUrl(shopName))
    if err != nil {
        return nil, err
    }
    //.....
}

Or:

func (a App) NewClient(shopName url.URL, token string, opts ...Option) (*Client, error) {
	return NewClient(a, shopName, token, opts...)
}
func NewClient(app App, shopName url.URL, token string, opts ...Option) (*Client, error) {
    baseURL := shopName
    //.....
}

Pagination parsing regex is incorrect

I was trying out product pagination parsing, and ended up having to change the link parsing regex for the code to work:

Here's the regex in your code:

var linkRegex = regexp.MustCompile(`^ *<([^>]+)>; rel="(previous|next)" *$`)

And the example from the Shopify docs:

#...
Link: "<https://{shop}.myshopify.com/admin/api/2019-07/products.json?page_info=hijgklmn&limit=3>; rel=next"
#...

The quotes around (previous|next) are nowhere to be seen in the docs, so either the Shopify docs are wrong (I haven't tried against their live API yet), or the regex is invalid. Just removing the quote seems to make my tests pass against the Shopify docs example.

ids are int64 in the lib but it's uint64 in Shopify

According to public doc of Shopify [1], ProductID is unsigned int 64, however, Product in the lib has int64 [2]. This diff would be not only in Product but the other models as well.
This can be critical difference once the value exceeds range of int64. (although not sure how it is realistically exceeded)

[1] https://shopify.dev/docs/admin-api/rest/reference/products/product?api[version]=2020-01

An unsigned 64-bit integer that's used as a unique identifier for the product. Each id is unique across the Shopify system. No two products will have the same id, even if they're from different shops.

[2] https://github.com/bold-commerce/go-shopify/blob/master/product.go#L43

Support PriceRule resource

Hi all ✋
I'm very enjoying to use go-shopify.

By the way, is there any plan to support the PriceRule resource

The search results for this repository indicate that the implementation of PriceRule was proposed once in #59.
(But it seems closed and didn't incorporate into the codebase. 😢 )

So, is there any plan to support the PriceRule resource or would you let me support it?

🙏

Unable to nullify string attribute using the update function...

From @ckeyes88 on July 17, 2018 16:39

Currently, string values on any of the data structs have the omitempty tag associated with them. Because of this, if I try to set a string value to it's nil value "", json.Unmarshal and json.Marshal leave off that attribute. This is a problem when trying to update a string field that I want to unset.

Steps to reproduce (using a variant as example):

  • Create a variant with v.InventoryManagement = "shopify"
  • Set v.InventoryManagement = ""
  • Call shopify.Variant.Updated(v)

Expected: The returned updated variant should have v.InventoryManagement == "" and that variant should no longer be set to have shopify track inventory.

Actual: The returned updated variant has v.InventoryManagement == "shopify" and that variant has no changes to it's inventory management in the admin console.

Proposed fix: Convert the string values to string pointers so that they can be explicitly nil-able.

If I'm thinking about the problem incorrectly, I'd love some feedback on how to unset string properties.

Copied from original issue: getconversio#104

Incorrect precision on Id's, int64 != 32 digits

From the shopify docs:

A unique numeric identifier, of up to 32 digits, for the product. 
Each id is unique across the Shopify system.
No two products will have the same id, even if they're from different shops. 

largest int64 (8 bytes)
9223372036854775807 = 19 digits

if its uint64, you can get to 20 digits

It is probably better to represent the Id's as strings or some custom type.

Unable to specify PriceRule prerequisites using existing models.

While working with the PriceRules API client exposed by this package, I noticed that we are currently unable to specify PriceRule prerequisites (minimum purchase eligibility, minimum quantity eligibility, maximum shipping eligibility, etc)

This is a blocker for some work of ours, but would also pose a breaking change due to the rename of the types.

I've opened two PRs to illustrate some options and capture any pointed discussion, anything more general can be captured here in the comments.

Relevant PRs:
#142
#144

IDs should use int64 instead of int

From @orian on June 8, 2018 6:27

For some objects Shopify is using a global ID space and recently the threshold of 2^31-1 has been exceeded.

The Go's int size is at least 32 bits, but not guaranteed to be bigger (e.g. int64).

I propose to switch all int ids into int64.

Copied from original issue: getconversio#92

Unable to filter Customer Metafields on namespace and key

Hi All,

I'm trying to find all metafields for a Customer that has a specific namespace and key
Basically, this cURL request

curl --location --request GET 'https://{{store_domain.com}}/admin/api/2022-01/metafields.json?namespace={{namespace}}&key={{key}}' \
--header 'X-Shopify-Access-Token: {{access_token}}'

In my code I'm using the Customer.ListMetafields method like so:

metafield, err := client.Customer.ListMetafields(shopifyID, goshopify.Metafield{
		Namespace: "{{namespace}}",
		Key:       "{{key}}",
	})
if err != nil {
	lib.CheckError("failed to get Shopify metafield", err)
}

But instead of getting a filtered list of Metafields I'm getting all of the customer's metafields. Is this an issue with the package or am I just doing something silly?

Add a new WithClient option

Adding an option WithClient that will allow to add custom configuration for the http client for example override the transport to add monitoring.

I'm willing to open an PR, if it's decided to move forward with this idea.

go get downloads older version v2.3.0

I am not able to download the latest version using go get. I am always getting the below version.

github.com/bold-commerce/go-shopify v2.3.0+incompatible

Steps I took to install are:

go mod init
go run main.go

Manually trying to install latest version gives errors

go get github.com/bold-commerce/[email protected]
go get github.com/bold-commerce/[email protected]: github.com/bold-commerce/[email protected]: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v3

Any ideas on how to get this fixed?

Product.ListWithPagination Broken

I am trying to grab all of my products from Shopify, which number over 200. When I attempt to use ListWithPagination, the NextPageOptions and PrevPageOptions are both nil. In particular, I have

        var prodList [][]goshopify.Product
	ops := interface{}(goshopify.ProductListOptions{
		ListOptions: goshopify.ListOptions{
			Page:  1,
			Limit: 50,
		},
	})
	for true {
		 prods, page, err := client.Product.ListWithPagination(ops)
                 check(err)
		prodList = append(prodList, prods)
		 if page.NextPageOptions == nil {
		 	break
		 }
		 ops = interface{}(page.NextPageOptions)
	}

Using a debugger, I find that page only contains nil values, and the loop breaks immediately. I currently get around this situation by using Product.List and iterating until it returns an empty Product slice, although I don't know how safe this is. I have

        var prodList [][]goshopify.Product
	ops := goshopify.ProductListOptions{
		ListOptions: goshopify.ListOptions{
			Page:  1,
			Limit: 50,
		},
	}
	for true {
		prods, err := client.Product.List(ops)
		check(err)
		prodList = append(prodList, prods)
		if len(prods) == 0 {
			break
		}
		ops.ListOptions.Page++
	}

This does work and pulls all products from Shopify.

AuthorizeUrl does not handle url parse errors

AuthorizeUrl does not handle errors. If url.Parse returns an error then a panic occurs trying to access a nil pointer. This can occur if the shopName contains invalid web address characters like a space.

AuthorizeURL in oauth.go

func (app App) AuthorizeUrl(shopName string, state string) string {
	shopUrl, _ := url.Parse(ShopBaseUrl(shopName)) // error here is unhandled
	shopUrl.Path = "/admin/oauth/authorize" // panic occurs here on nil pointer
	query := shopUrl.Query()
	query.Set("client_id", app.ApiKey)
	query.Set("redirect_uri", app.RedirectUrl)
	query.Set("scope", app.Scope)
	query.Set("state", state)
	shopUrl.RawQuery = query.Encode()
	return shopUrl.String()
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.