Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation and tests for deprecation of product and variant inventory-related fields #655

Merged
merged 1 commit into from
Mar 19, 2020

Conversation

djhoughton
Copy link
Contributor

@djhoughton djhoughton commented Dec 20, 2019

fixes #632

The following fields on Product (total_inventory) and Variant (inventory_quantity_adjustment, inventory_quantity, old_inventory_quantity) were deprecated earlier this year and were removed in the 2019-10 api version.

This PR updates the code and adds tests to ensure the behaviour is correct.

Reviewer's note: In test/test_helper.rb I changed the code to return the current API version if none was specified. The old code returned the Jan 2019 version. I believe this new behaviour is correct but would like confirmation.

@djhoughton djhoughton self-assigned this Dec 20, 2019
@djhoughton djhoughton changed the title [WIP] Add validation and tests for deprecation of product and variant inventory-related fields Add validation and tests for deprecation of product and variant inventory-related fields Jan 14, 2020
@djhoughton djhoughton marked this pull request as ready for review January 14, 2020 14:38
@djhoughton djhoughton requested a review from a team as a code owner January 14, 2020 14:38
@@ -16,6 +16,15 @@ def price_range
end
end

def serializable_hash(options = {})
super.except("total_inventory")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why this is needed? For versions without the field, it won't be here anyway. And for versions with it, this is a breaking change since it's done unconditionally.

If someone were still on 2019-07 for example, this would be a breaking change.

Same question applies for the variant change below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I see what you mean. Honestly, I can't remember why we added that (it was before Christmas) but looking at it now and based on your arguments above I agree that we can remove it. 👍

@eljojo Do you remember the logic behind the changes in serializable_hash?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in these scenarios we can actually strip them out from serializable_hash in versions 2019-10 and later; if we already know we're going to fail those requests without clients making those changes, we should at least make it easier for them by omitting them in the scenario where they copy the response directly into the request.

@@ -16,6 +16,11 @@ def price_range
end
end

def total_inventory=(new_value)
raise(ShopifyAPI::ValidationException, 'deprecated behaviour') unless Base.api_version < ApiVersion.find_version('2019-10')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the exception messages should point to https://help.shopify.com/en/api/guides/inventory-migration-guide?

@spadae22
Copy link

Are they going to merge this, issue still exists?

@djhoughton
Copy link
Contributor Author

Thanks for the ping! We're still working out a few things with the deprecation and we'll update this PR once it is sorted. Stay tuned...

@Saibotx
Copy link

Saibotx commented Mar 18, 2020

Hello - do we have an ETA on this being merged?

As of now, any shopify product / variant edit / create / deletes are broken right now. I'm on standby waiting on this fix.

@dnswus
Copy link
Contributor

dnswus commented Mar 18, 2020

I'm doing the following temp fix (#632 (comment)) on my side before this is merged:

variant.attributes.delete(:inventory_quantity)
variant.attributes.delete(:old_inventory_quantity)

@EmmaB
Copy link

EmmaB commented Mar 18, 2020

We had to do an urgent workaround.

@djhoughton
Copy link
Contributor Author

I'm working on updating the current PR to remove those fields from the response for requests using the Oct 2019 API release and later.

@slimdave
Copy link

This change just moves the error to the gem, rather than relying on the response from Shopify, is that correct? Or have I misunderstood?

@djhoughton
Copy link
Contributor Author

This change just moves the error to the gem, rather than relying on the response from Shopify, is that correct? Or have I misunderstood?

Yes, that's correct

@jonathankwok jonathankwok self-requested a review March 18, 2020 21:47
@djhoughton
Copy link
Contributor Author

To be clear this PR makes 2 changes if a client is using the 2019-10 API version or later:
1). If the client tries to set a deprecated inventory-related field on Product or Variant, we now throw an exception in the gem rather than going to the server to get the failed request
2). We remove the deprecated fields from being serialized in case clients try to send them back to the server without removing them.

The deprecations are outlined here:
https://shopify.dev/docs/admin-api/rest/reference/products/product-variant
https://shopify.dev/tutorials/migrate-your-app-to-support-multiple-locations
https://shopify.dev/tutorials/manage-product-inventory-with-admin-api

@swalkinshaw Would appreciate your feedback again on this one to make sure we're aligned now that the changes have been shipped to Core.

@spadae22
Copy link

Hi, when will this be merged?

@djhoughton djhoughton merged commit 31f01b1 into master Mar 19, 2020
@djhoughton djhoughton requested a deployment to rubygems March 19, 2020 20:32 Abandoned
@spadae22
Copy link

spadae22 commented Mar 20, 2020

HI,
The fields have been removed with 9.0.3, but I when I PUT this product file, I still get Bad Request? I am NOT updating inventory.

PUT https://chriscoffee-dev.myshopify.com/admin/api/2019-10/products/4340483522611.json

{"product":{"id":4340483522611,"title":"1.0mm #0 Steam Tip","body_html":"\u003cp\u003e8.8mm female thread\u003c/p\u003e","vendor":"Slayer Espresso","product_type":"Slayer","created_at":"2019-11-10T21:17:26-05:00","handle":"1-0mm-0-steam-tip","updated_at":"2020-02-17T00:15:03-05:00","published_at":"2019-11-10T21:17:25-05:00","template_suffix":null,"published_scope":"web","tags":"Accessories","admin_graphql_api_id":"gid://shopify/Product/4340483522611","variants":[{"id":31148589187123,"product_id":4340483522611,"title":"Default Title","price":"25.95","sku":"46000-50340","position":1,"inventory_policy":"deny","compare_at_price":null,"fulfillment_service":"manual","inventory_management":"shopify","option1":"Default Title","option2":null,"option3":null,"created_at":"2019-11-10T21:17:26-05:00","updated_at":"2020-02-11T14:02:55-05:00","taxable":true,"barcode":"false","grams":23,"image_id":null,"weight":0.0507,"weight_unit":"lb","inventory_item_id":32667358199859,"inventory_quantity":49,"old_inventory_quantity":49,"tax_code":"","requires_shipping":true,"admin_graphql_api_id":"gid://shopify/ProductVariant/31148589187123"}],"options":[{"id":5642415570995,"product_id":4340483522611,"name":"Title","position":1,"values":["Default Title"]}],"images":[{"id":13402636582963,"product_id":4340483522611,"position":1,"created_at":"2019-11-10T21:17:26-05:00","updated_at":"2019-11-10T21:17:26-05:00","alt":null,"width":1200,"height":1200,"src":"https://cdn.shopify.com/s/files/1/0251/6398/9043/products/46000-50340.jpg?v=1573438646","variant_ids":[],"admin_graphql_api_id":"gid://shopify/ProductImage/13402636582963"}],"image":{"id":13402636582963,"product_id":4340483522611,"position":1,"created_at":"2019-11-10T21:17:26-05:00","updated_at":"2019-11-10T21:17:26-05:00","alt":null,"width":1200,"height":1200,"src":"https://cdn.shopify.com/s/files/1/0251/6398/9043/products/46000-50340.jpg?v=1573438646","variant_ids":[],"admin_graphql_api_id":"gid://shopify/ProductImage/13402636582963"}}}

Response:BadRequest: Response(code=400, body="{"error":"Write requests to inventory_quantity and inventory_quantity_adjustment are no longer supported. Please use the Inventory Levels API."}", headers={'x-shopify-stage': 'production', 'x-stats-userid': '', 'cf-cache-status': 'DYNAMIC', 'x-shopify-shop-api-call-limit': '1/80', 'x-shopid': '25163989043', 'http_x_shopify_shop_api_call_limit': '1/80', 'x-request-id': 'e3878b1e-6905-4bab-9a43-139e44cce883', 'x-xss-protection': '1; mode=block; report=/xss-report?source%5Baction%5D=update&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=e3878b1e-6905-4bab-9a43-139e44cce883', 'x-content-type-options': 'nosniff', 'x-stats-apiclientid': '3250443', 'x-shardid': '50', 'transfer-encoding': 'chunked', 'referrer-policy': 'origin-when-cross-origin', 'x-sorting-hat-shopid': '25163989043', 'x-download-options': 'noopen', 'x-stats-apipermissionid': '198562512947', 'set-cookie': '__cfduid=d3f64e40387b1a49f6442ae64e2cb1e2f1584723654; expires=Sun, 19-Apr-20 17:00:54 GMT; path=/; domain=.myshopify.com; HttpOnly; SameSite=Lax', 'x-shopify-api-terms': 'By accessing or using the Shopify API you agree to the Shopify API License and Terms of Use at https://www.shopify.com/legal/api-terms', 'x-shopify-api-version': '2019-10', 'date': 'Fri, 20 Mar 2020 17:00:55 GMT', 'cf-ray': '5770fdfb4900eff5-EWR', 'expect-ct': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', 'alt-svc': 'h3-27=":443"; ma=86400, h3-25=":443"; ma=86400, h3-24=":443"; ma=86400, h3-23=":443"; ma=86400', 'content-security-policy': "default-src 'self' data: blob: 'unsafe-inline' 'unsafe-eval' https://* shopify-pos://; block-all-mixed-content; child-src 'self' https:// shopify-pos://; connect-src 'self' wss:// https://*; frame-ancestors 'none'; img-src 'self' data: blob: https:; script-src https://cdn.shopify.com https://cdn.shopify.cn https://checkout.shopifycs.com https://js-agent.newrelic.com https://bam.nr-data.net https://api.stripe.com https://mpsnare.iesnare.com https://appcenter.intuit.com https://www.paypal.com https://js.braintreegateway.com https://c.paypal.com https://maps.googleapis.com https://www.google-analytics.com https://v.shopify.com https://widget.intercom.io https://js.intercomcdn.com 'self' 'unsafe-inline' 'unsafe-eval'; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=update&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=e3878b1e-6905-4bab-9a43-139e44cce883", 'x-dc': 'gcp-us-central1,gcp-us-east1,gcp-us-east1', 'strict-transport-security': 'max-age=7889238', 'x-sorting-hat-podid': '50', 'server': 'cloudflare', 'connection': 'close', 'x-permitted-cross-domain-policies': 'none', 'x-frame-options': 'DENY', 'content-type': 'application/json; charset=utf-8'}, msg="Bad Request")

@djhoughton
Copy link
Contributor Author

The deprecated parameters are still being sent in the request you pasted above:

        "inventory_quantity": 49,
        "old_inventory_quantity": 49,

@spadae22
Copy link

Correct, I thought the update would remove or ignore them, I GET for same same product, these fields are included. I thought the point of the update was to fix the Bad Request issue. So I still have to strip them out?

@spadae22
Copy link

To be clear this PR makes 2 changes if a client is using the 2019-10 API version or later:
1). If the client tries to set a deprecated inventory-related field on Product or Variant, we now throw an exception in the gem rather than going to the server to get the failed request
2). We remove the deprecated fields from being serialized in case clients try to send them back to the server without removing them.

I am not setting inventory 1., and 2. states the fields would be removed ?

@djhoughton
Copy link
Contributor Author

Do you have some sample code for what you're doing? Also, which API version are you using?

@spadae22
Copy link

spadae22 commented Mar 20, 2020

I set the version 2019-10, as base url. I am just trying to unpublish a product from our ERP. Here is the code, it was working until Mach 16.

@api.multi
def shopify_unpublished(self):
instance = self.shopify_instance_id
instance.connect_in_shopify()
if self.shopify_tmpl_id:
new_product = shopify.Product.find(self.shopify_tmpl_id)
if new_product:
new_product.id = self.shopify_tmpl_id
new_product.published = 'false'
new_product.published_at = None
result = new_product.save()
if result:
result_dict = new_product.to_dict()
updated_at = result_dict.get('updated_at')
published_at = result_dict.get('published_at')
self.write({'updated_at': updated_at, 'published_at': False, 'website_published': False})
return True`

@spadae22
Copy link

The real issue I have is I did refactor code for 2019-10, months ago, what changes occured on the March 16th, that broke this. I have many functions that I would have to update, we update stock using the correct method without issue. I feeling is the GET is pulling all fields, fine. But if I tried to set inventory the old way, then yes I should get a bad request, not when I am updating two fields. So if I change the 'title' it is going to throw the same Bad Request. These fields should be ignored by the server since they are not used.

@spadae22
Copy link

This is the code we pass to update inventory, the fields,

"inventory_quantity": 49,
"old_inventory_quantity": 49,
are not being used>

quantity = product_obj.get_stock_ept(variant.product_id, location_id.warehouse_id.id,variant.fix_stock_type,variant.fix_stock_value, instance.stock_field.name) try: shopify.InventoryLevel.set(location_id.shopify_location_id, variant.inventory_item_id, int(quantity))

@spadae22
Copy link

Same issue as here:#702

@djhoughton
Copy link
Contributor Author

We're going to revert the changes introduced by this PR and work on a new fix for the issue.
Thanks for the comments and prompt feedback!

end

def serializable_hash(options = {})
allow_inventory_params? ? super(options) : super(options).except('inventory_quantity', 'old_inventory_quantity')
Copy link
Contributor

@marschattha marschattha Apr 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing to

  def serializable_hash(options = {})
    allow_inventory_params? ? super(options) : super(options).tap do |s|
      (s['variant'] || s).except!('inventory_quantity', 'old_inventory_quantity')
    end
  end

Fixes this PR.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

end

def serializable_hash(options = {})
allow_inventory_params? ? super(options) : super(options).except('inventory_quantity', 'old_inventory_quantity')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this accurate? Those fields were deprecated for writes, but reading variant.total_inventory should still be supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot update any product/variant attribute on latest API version