From 00b6dc147fd22370c396e35d2c075a7a8aee5263 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 6 Apr 2022 17:00:58 -0400 Subject: [PATCH 1/4] Load unknown REST resource attributes from data --- lib/shopify_api/rest/base.rb | 45 ++++++++++++------------- test/clients/base_rest_resource_test.rb | 10 ++++++ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/shopify_api/rest/base.rb b/lib/shopify_api/rest/base.rb index 9f81a5c61..3691aa202 100644 --- a/lib/shopify_api/rest/base.rb +++ b/lib/shopify_api/rest/base.rb @@ -215,24 +215,21 @@ def create_instance(data:, session:, instance: nil) instance ||= new(session: session) instance.original_state = {} - unless data.empty? - # This retrieves all the setters on the resource and calls them with the data - instance_methods(false).select { |method| !method.to_s.include?("=") }.each do |attribute| - next unless data.key?(attribute.to_s) - - if has_many?(attribute) && data[attribute.to_s] - attr_list = [] - data[attribute.to_s].each do |element| - attr_list << T.unsafe(@has_many[attribute]).create_instance(data: element, session: session) - end - instance.public_send("#{attribute}=", attr_list) - elsif has_one?(attribute) && data[attribute.to_s] - instance.public_send("#{attribute}=", - T.unsafe(@has_one[attribute]).create_instance(data: data[attribute.to_s], session: session)) - else - instance.public_send("#{attribute}=", data[attribute.to_s]) - instance.original_state[attribute] = data[attribute.to_s] + data.each do |attribute, value| + attr_sym = attribute.to_sym + + if has_many?(attr_sym) && value + attr_list = [] + value.each do |element| + attr_list << T.unsafe(@has_many[attr_sym]).create_instance(data: element, session: session) end + instance.public_send("#{attribute}=", attr_list) + elsif has_one?(attr_sym) && value + instance.public_send("#{attribute}=", + T.unsafe(@has_one[attr_sym]).create_instance(data: value, session: session)) + else + instance.public_send("#{attribute}=", value) + instance.original_state[attr_sym] = value end end @@ -240,16 +237,18 @@ def create_instance(data:, session:, instance: nil) end end - sig { params(meth_id: Symbol, val: T.untyped).void } + sig { params(meth_id: Symbol, val: T.untyped).returns(T.untyped) } def method_missing(meth_id, val = nil) - match = meth_id.id2name.match(/([^=]+)=/) - - return super unless match + match = meth_id.id2name.match(/([^=]+)(=)?/) var = match[1] - instance_variable_set("@#{var}", val) - @forced_nils[T.must(var)] = val.nil? + if match[2] + instance_variable_set("@#{var}", val) + @forced_nils[T.must(var)] = val.nil? + else + instance_variable_get("@#{var}") + end end sig { params(meth_id: Symbol, args: T.untyped).void } diff --git a/test/clients/base_rest_resource_test.rb b/test/clients/base_rest_resource_test.rb index b3068120d..0f65580a5 100644 --- a/test/clients/base_rest_resource_test.rb +++ b/test/clients/base_rest_resource_test.rb @@ -173,6 +173,16 @@ def test_deletes_existing_resource_and_fails_on_deleting_nonexistent_resource assert_requested(stubbed_request) end + def test_load_unknown_attribute + body = { fake_resource: { id: 1, attribute: "attribute", unknown: "some-value" } }.to_json + + stub_request(:get, "#{@prefix}/fake_resources/1.json").to_return(body: body) + + resource = TestHelpers::FakeResource.find(id: 1, session: @session) + + assert_equal("some-value", resource.unknown) + end + def test_save_with_unknown_attribute request_body = { fake_resource: { unknown: "some-value" } }.to_json From 84f3653d03c4317b98fb6b716d0929310595c8c5 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 7 Apr 2022 09:56:28 -0400 Subject: [PATCH 2/4] Handle loading attributes with special characters --- lib/shopify_api/rest/base.rb | 57 +++++++++++++++++++------ test/clients/base_rest_resource_test.rb | 14 +++++- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/lib/shopify_api/rest/base.rb b/lib/shopify_api/rest/base.rb index 3691aa202..11c81b0c9 100644 --- a/lib/shopify_api/rest/base.rb +++ b/lib/shopify_api/rest/base.rb @@ -17,6 +17,8 @@ class Base @custom_prefix = T.let(nil, T.nilable(String)) @read_only_attributes = T.let([], T.nilable(T::Array[Symbol])) + @aliased_properties = T.let({}, T::Hash[String, String]) + sig { returns(T::Hash[Symbol, T.untyped]) } attr_accessor :original_state @@ -33,6 +35,7 @@ def initialize(session: nil, from_hash: nil) @original_state = T.let({}, T::Hash[Symbol, T.untyped]) @custom_prefix = T.let(nil, T.nilable(String)) @forced_nils = T.let({}, T::Hash[String, T::Boolean]) + @aliased_properties = T.let({}, T::Hash[String, String]) session ||= ShopifyAPI::Context.active_session @@ -43,7 +46,7 @@ def initialize(session: nil, from_hash: nil) @errors = T.let(Rest::BaseErrors.new, Rest::BaseErrors) from_hash&.each do |key, value| - instance_variable_set("@#{key}", value) + set_property(key, value) end end @@ -241,13 +244,13 @@ def create_instance(data:, session:, instance: nil) def method_missing(meth_id, val = nil) match = meth_id.id2name.match(/([^=]+)(=)?/) - var = match[1] + var = T.must(T.must(match)[1]) - if match[2] - instance_variable_set("@#{var}", val) - @forced_nils[T.must(var)] = val.nil? + if T.must(match)[2] + set_property(var, val) + @forced_nils[var] = val.nil? else - instance_variable_get("@#{var}") + get_property(var) end end @@ -263,18 +266,31 @@ def respond_to_missing?(meth_id, *args) def to_hash hash = {} instance_variables.each do |var| - next if [:"@original_state", :"@session", :"@client", :"@forced_nils", :"@errors"].include?(var) + next if [ + :"@original_state", + :"@session", + :"@client", + :"@forced_nils", + :"@errors", + :"@aliased_properties", + ].include?(var) next if self.class.read_only_attributes&.include?(var) - attribute = var.to_s.delete("@").to_sym + var = var.to_s.delete("@") + attribute = if @aliased_properties.value?(var) + T.must(@aliased_properties.key(var)) + else + var + end.to_sym + if self.class.has_many?(attribute) - hash[attribute.to_s] = instance_variable_get(var).map(&:to_hash).to_a if instance_variable_get(var) + hash[attribute.to_s] = get_property(attribute).map(&:to_hash).to_a if get_property(attribute) elsif self.class.has_one?(attribute) - element_hash = instance_variable_get(var)&.to_hash + element_hash = get_property(attribute)&.to_hash hash[attribute.to_s] = element_hash if element_hash || @forced_nils[attribute.to_s] - elsif !instance_variable_get(var).nil? || @forced_nils[attribute.to_s] + elsif !get_property(attribute).nil? || @forced_nils[attribute.to_s] hash[attribute.to_s] = - instance_variable_get(var) + get_property(attribute) end end hash @@ -319,6 +335,23 @@ def save(update_object: false) @errors.errors << e raise end + + private + + sig { params(var: T.any(String, Symbol), val: T.untyped).void } + def set_property(var, val) + clean = var.to_s.gsub(/[\?\s]/, "") + @aliased_properties[var.to_s] = clean if clean != var + + instance_variable_set("@#{clean}", val) + end + + sig { params(var: T.any(String, Symbol)).returns(T.untyped) } + def get_property(var) + clean = @aliased_properties.key?(var.to_s) ? @aliased_properties[var.to_s] : var + + instance_variable_get("@#{clean}") + end end end end diff --git a/test/clients/base_rest_resource_test.rb b/test/clients/base_rest_resource_test.rb index 0f65580a5..59923ef8a 100644 --- a/test/clients/base_rest_resource_test.rb +++ b/test/clients/base_rest_resource_test.rb @@ -173,7 +173,7 @@ def test_deletes_existing_resource_and_fails_on_deleting_nonexistent_resource assert_requested(stubbed_request) end - def test_load_unknown_attribute + def test_loads_unknown_attribute body = { fake_resource: { id: 1, attribute: "attribute", unknown: "some-value" } }.to_json stub_request(:get, "#{@prefix}/fake_resources/1.json").to_return(body: body) @@ -181,6 +181,18 @@ def test_load_unknown_attribute resource = TestHelpers::FakeResource.find(id: 1, session: @session) assert_equal("some-value", resource.unknown) + assert_equal("some-value", resource.to_hash["unknown"]) + end + + def test_loads_unknown_attribute_with_special_character + body = { fake_resource: { id: 1, attribute: "attribute", "unknown?": "some-value" } }.to_json + + stub_request(:get, "#{@prefix}/fake_resources/1.json").to_return(body: body) + + resource = TestHelpers::FakeResource.find(id: 1, session: @session) + + assert_equal("some-value", resource.unknown?) + assert_equal("some-value", resource.to_hash["unknown?"]) end def test_save_with_unknown_attribute From 032ec7f83b726f8e78c4376c43c97d0265d992db Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 7 Apr 2022 09:58:12 -0400 Subject: [PATCH 3/4] Add changelog entry --- CHANGELOG.md | 430 ++++++++++++++++++++++++++------------------------- 1 file changed, 218 insertions(+), 212 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b7931a7e..94dee7ab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## Unreleased +### Fixed + - [#919](https://github.com/Shopify/shopify_api/pull/919) Allow REST resources to configure a deny list of attributes to be excluded when saving +- [#920](https://github.com/Shopify/shopify_api/pull/920) Set all values received from the API response to REST resource objects, and allow setting / getting attributes with special characters (such as `?`) ## Version 10.0.0 @@ -14,496 +17,499 @@ ## Version 9.5 -* [#883](https://github.com/Shopify/shopify_api/pull/883) Add support for Ruby 3.0 +- [#883](https://github.com/Shopify/shopify_api/pull/883) Add support for Ruby 3.0 ## Version 9.4.1 -* [#847](https://github.com/Shopify/shopify_api/pull/847) Update `create_permission_url` method to use grant_options -* [#852](https://github.com/Shopify/shopify_api/pull/852) Bumping kramdown to fix a security vulnerability +- [#847](https://github.com/Shopify/shopify_api/pull/847) Update `create_permission_url` method to use grant_options +- [#852](https://github.com/Shopify/shopify_api/pull/852) Bumping kramdown to fix a security vulnerability ## Version 9.4.0 -* [#843](https://github.com/Shopify/shopify_api/pull/843) Introduce a new `access_scopes` attribute on the Session class. - * Specifying this in the Session constructor is optional. By default, this attribute returns `nil`. +- [#843](https://github.com/Shopify/shopify_api/pull/843) Introduce a new `access_scopes` attribute on the Session class. + - Specifying this in the Session constructor is optional. By default, this attribute returns `nil`. ## Version 9.3.0 -* [#797](https://github.com/Shopify/shopify_api/pull/797) Release new Endpoint `fulfillment_order.open` and `fulfillment_order.reschedule`. +- [#797](https://github.com/Shopify/shopify_api/pull/797) Release new Endpoint `fulfillment_order.open` and `fulfillment_order.reschedule`. -* [#818](https://github.com/Shopify/shopify_api/pull/818) Avoid depending on ActiveSupport in Sesssion class. +- [#818](https://github.com/Shopify/shopify_api/pull/818) Avoid depending on ActiveSupport in Sesssion class. -* Freeze all string literals. This should have no impact unless your application is modifying ('monkeypatching') the internals of the library in an unusual way. +- Freeze all string literals. This should have no impact unless your application is modifying ('monkeypatching') the internals of the library in an unusual way. -* [#802](https://github.com/Shopify/shopify_api/pull/802) Made `inventory_quantity` a read-only field in Variant +- [#802](https://github.com/Shopify/shopify_api/pull/802) Made `inventory_quantity` a read-only field in Variant -* [#821](https://github.com/Shopify/shopify_api/pull/821) Add logging based on environment variable, move log subscriber out of `detailed_log_subscriber`. +- [#821](https://github.com/Shopify/shopify_api/pull/821) Add logging based on environment variable, move log subscriber out of `detailed_log_subscriber`. The `ActiveResource::DetailedLogSubscriber` no longer automatically attaches when the class is loaded. If you were previously relying on that behaviour, you'll now need to call `ActiveResource::DetailedLogSubscriber.attach_to(:active_resource_detailed)`. (If using the new `SHOPIFY_LOG_PATH` environment setting then this is handled for you). -* Provide `ApiAccess` value object to encapsulate scope operations [#829](https://github.com/Shopify/shopify_api/pull/829) +- Provide `ApiAccess` value object to encapsulate scope operations [#829](https://github.com/Shopify/shopify_api/pull/829) ## Version 9.2.0 -* Removes the `shopify` binary which will be used by the Shopify CLI +- Removes the `shopify` binary which will be used by the Shopify CLI ## Version 9.1.1 -* Make cursor based pagination return relative uri's when fetching next and previous pages. [#726](https://github.com/Shopify/shopify_api/pull/726) +- Make cursor based pagination return relative uri's when fetching next and previous pages. [#726](https://github.com/Shopify/shopify_api/pull/726) ## Version 9.1.0 -* Implements equality operator on `Session` [#714](https://github.com/Shopify/shopify_api/pull/714) +- Implements equality operator on `Session` [#714](https://github.com/Shopify/shopify_api/pull/714) ## Version 9.0.4 -* Contains [#708](https://github.com/Shopify/shopify_api/pull/708) which is a revert for [#655](https://github.com/shopify/shopify_api/pull/655) due to the deprecated inventory parameters not being removed correctly in some cases +- Contains [#708](https://github.com/Shopify/shopify_api/pull/708) which is a revert for [#655](https://github.com/shopify/shopify_api/pull/655) due to the deprecated inventory parameters not being removed correctly in some cases ## Version 9.0.3 -* We now raise a `ShopifyAPI::ValidationException` exception when clients try to use `Product` and `Variant` with deprecated inventory-related fields in API version `2019-10` or later. [#655](https://github.com/shopify/shopify_api/pull/655) Deprecation and migration information can be found in the following documents: - * [Product Variant REST API Reference](https://shopify.dev/docs/admin-api/rest/reference/products/product-variant) - * [Migrate your app to support multiple locations](https://shopify.dev/tutorials/migrate-your-app-to-support-multiple-locations) - * [Manage product inventory with the Admin API](https://shopify.dev/tutorials/manage-product-inventory-with-admin-api) -* Added support for the Discount Code API batch endpoints [#701](https://github.com/shopify/shopify_api/pull/701) - * [Create](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_create-2020-01) - * [Show](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_show-2020-01) - * [List](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_discount_codes_index-2020-01) -* Fix issue in the README to explicitly say clients need to require the `shopify_api` gem [#700](https://github.com/Shopify/shopify_api/pull/700) +- We now raise a `ShopifyAPI::ValidationException` exception when clients try to use `Product` and `Variant` with deprecated inventory-related fields in API version `2019-10` or later. [#655](https://github.com/shopify/shopify_api/pull/655) Deprecation and migration information can be found in the following documents: + - [Product Variant REST API Reference](https://shopify.dev/docs/admin-api/rest/reference/products/product-variant) + - [Migrate your app to support multiple locations](https://shopify.dev/tutorials/migrate-your-app-to-support-multiple-locations) + - [Manage product inventory with the Admin API](https://shopify.dev/tutorials/manage-product-inventory-with-admin-api) +- Added support for the Discount Code API batch endpoints [#701](https://github.com/shopify/shopify_api/pull/701) + - [Create](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_create-2020-01) + - [Show](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_show-2020-01) + - [List](https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode#batch_discount_codes_index-2020-01) +- Fix issue in the README to explicitly say clients need to require the `shopify_api` gem [#700](https://github.com/Shopify/shopify_api/pull/700) ## Version 9.0.2 -* Added optional flag passed to `initialize_clients` to prevent from raising the `InvalidSchema` exception [#693](https://github.com/Shopify/shopify_api/pull/693) +- Added optional flag passed to `initialize_clients` to prevent from raising the `InvalidSchema` exception [#693](https://github.com/Shopify/shopify_api/pull/693) ## Version 9.0.1 -* Added warning message if API version used is unsupported or soon to be unsupported [#685](https://github.com/Shopify/shopify_api/pull/685) -* Take into account "errors" messages from response body [#677](https://github.com/Shopify/shopify_api/pull/677) +- Added warning message if API version used is unsupported or soon to be unsupported [#685](https://github.com/Shopify/shopify_api/pull/685) +- Take into account "errors" messages from response body [#677](https://github.com/Shopify/shopify_api/pull/677) ## Version 9.0.0 -* Breaking change: Improved GraphQL client [#672](https://github.com/Shopify/shopify_api/pull/672). See the [client docs](docs/graphql.md) for usage and a migration guide. +- Breaking change: Improved GraphQL client [#672](https://github.com/Shopify/shopify_api/pull/672). See the [client docs](docs/graphql.md) for usage and a migration guide. + +- Added options hash to create_permission_url and makes redirect_uri required [#670](https://github.com/Shopify/shopify_api/pull/670) -* Added options hash to create_permission_url and makes redirect_uri required [#670](https://github.com/Shopify/shopify_api/pull/670) +- Release new Endpoint `fulfillment_order.locations_for_move` in 2020-01 REST API version [#669](https://github.com/Shopify/shopify_api/pull/669) -* Release new Endpoint `fulfillment_order.locations_for_move` in 2020-01 REST API version [#669](https://github.com/Shopify/shopify_api/pull/669) +- Release new Endpoints for `fulfillment` in 2020-01 REST API version [#639](https://github.com/Shopify/shopify_api/pull/639): -* Release new Endpoints for `fulfillment` in 2020-01 REST API version [#639](https://github.com/Shopify/shopify_api/pull/639): - * `fulfillment.create` with `line_items_by_fulfillment_order` - * `fulfillment.update_tracking` - * `fulfillment.cancel` + - `fulfillment.create` with `line_items_by_fulfillment_order` + - `fulfillment.update_tracking` + - `fulfillment.cancel` -* Release new Endpoints for `fulfillment_order` in 2020-01 REST API version [#637](https://github.com/Shopify/shopify_api/pull/637): - * `fulfillment_order.fulfillment_request` - * `fulfillment_order.fulfillment_request.accept` - * `fulfillment_order.fulfillment_request.reject` - * `fulfillment_order.cancellation_request` - * `fulfillment_order.cancellation_request.accept` - * `fulfillment_order.cancellation_request.reject` +- Release new Endpoints for `fulfillment_order` in 2020-01 REST API version [#637](https://github.com/Shopify/shopify_api/pull/637): -* Release new Endpoints `fulfillment_order.move`, `fulfillment_order.cancel` and `fulfillment_order.close` in 2020-01 REST API version [#635](https://github.com/Shopify/shopify_api/pull/635) + - `fulfillment_order.fulfillment_request` + - `fulfillment_order.fulfillment_request.accept` + - `fulfillment_order.fulfillment_request.reject` + - `fulfillment_order.cancellation_request` + - `fulfillment_order.cancellation_request.accept` + - `fulfillment_order.cancellation_request.reject` -* Release new Endpoint `order.fulfillment_orders`, and active resources `AssignedFulfillmentOrder` and `FulfillmentOrder` in 2020-01 REST API version [#633](https://github.com/Shopify/shopify_api/pull/633) +- Release new Endpoints `fulfillment_order.move`, `fulfillment_order.cancel` and `fulfillment_order.close` in 2020-01 REST API version [#635](https://github.com/Shopify/shopify_api/pull/635) + +- Release new Endpoint `order.fulfillment_orders`, and active resources `AssignedFulfillmentOrder` and `FulfillmentOrder` in 2020-01 REST API version [#633](https://github.com/Shopify/shopify_api/pull/633) ## Version 8.1.0 -* Release 2020-01 REST ADMIN API VERSION [#656](https://github.com/Shopify/shopify_api/pull/656) -* Release new Endpoint `collection.products` and `collection.find()` in 2020-01 REST API version [#657](https://github.com/Shopify/shopify_api/pull/657) -* Enrich 4xx errors with error message from response body [#647](https://github.com/Shopify/shopify_api/pull/647) -* Make relative cursor based pagination work across page loads [#625](https://github.com/Shopify/shopify_api/pull/625) -* Small ruby compat fix [#623](https://github.com/Shopify/shopify_api/pull/623) -* Small consistency change [#621](https://github.com/Shopify/shopify_api/pull/621) +- Release 2020-01 REST ADMIN API VERSION [#656](https://github.com/Shopify/shopify_api/pull/656) +- Release new Endpoint `collection.products` and `collection.find()` in 2020-01 REST API version [#657](https://github.com/Shopify/shopify_api/pull/657) +- Enrich 4xx errors with error message from response body [#647](https://github.com/Shopify/shopify_api/pull/647) +- Make relative cursor based pagination work across page loads [#625](https://github.com/Shopify/shopify_api/pull/625) +- Small ruby compat fix [#623](https://github.com/Shopify/shopify_api/pull/623) +- Small consistency change [#621](https://github.com/Shopify/shopify_api/pull/621) ## Version 8.0.0 -* Api Version changes [#600](https://github.com/Shopify/shopify_api/pull/600) - * Remove static Api Version definitions. - * Introduces Api Version lookup modes: `:define_on_unknown` and `:raise_on_unknown` - * See [migration notes](README.md#-breaking-change-notice-for-version-800-) -* `Session.valid?` checks that api_version `is_a?(ApiVersion)` instead of `present?` -* `ApiVersion::NullVersion` cannot be instantiated and now has a `match?` method [#615](https://github.com/Shopify/shopify_api/pull/615/files) -* Introduces new Collection endpoint for looking up products without knowing collection type. Only available if ApiVersion is `:unstable` [#609](https://github.com/Shopify/shopify_api/pull/609) +- Api Version changes [#600](https://github.com/Shopify/shopify_api/pull/600) + - Remove static Api Version definitions. + - Introduces Api Version lookup modes: `:define_on_unknown` and `:raise_on_unknown` + - See [migration notes](README.md#-breaking-change-notice-for-version-800-) +- `Session.valid?` checks that api_version `is_a?(ApiVersion)` instead of `present?` +- `ApiVersion::NullVersion` cannot be instantiated and now has a `match?` method [#615](https://github.com/Shopify/shopify_api/pull/615/files) +- Introduces new Collection endpoint for looking up products without knowing collection type. Only available if ApiVersion is `:unstable` [#609](https://github.com/Shopify/shopify_api/pull/609) ## Version 7.1.0 -* Add 2019-10 to known API versions -* Add support for cursor pagination [#594](https://github.com/Shopify/shopify_api/pull/594) and -[#611](https://github.com/Shopify/shopify_api/pull/611) -* `ShopifyAPI::Base.api_version` now defaults to `ShopifyAPI::ApiVersion::NullVersion` instead of `nil`. Making requests without first setting an ApiVersion raises `ApiVersionNotSetError` instead of `NoMethodError: undefined method 'construct_api_path' for nil:NilClass'` [#605](https://github.com/Shopify/shopify_api/pull/605) +- Add 2019-10 to known API versions +- Add support for cursor pagination [#594](https://github.com/Shopify/shopify_api/pull/594) and + [#611](https://github.com/Shopify/shopify_api/pull/611) +- `ShopifyAPI::Base.api_version` now defaults to `ShopifyAPI::ApiVersion::NullVersion` instead of `nil`. Making requests without first setting an ApiVersion raises `ApiVersionNotSetError` instead of `NoMethodError: undefined method 'construct_api_path' for nil:NilClass'` [#605](https://github.com/Shopify/shopify_api/pull/605) ## Version 7.0.2 -* Add 2019-07 to known API versions. +- Add 2019-07 to known API versions. ## Version 7.0.1 -* Support passing version string to `ShopifyAPI::Base.api_version` [#563](https://github.com/Shopify/shopify_api/pull/563) +- Support passing version string to `ShopifyAPI::Base.api_version` [#563](https://github.com/Shopify/shopify_api/pull/563) ## Version 7.0.0 -* Removed support for `ActiveResouce` < `4.1`. -* Removed `ShopifyAPI::Oauth`. -* Added api version support, See [migration -notes](README.md#-breaking-change-notice-for-version-700-) -* Changed `ShopifyAPI::Session` method signatures from positional to keyword -arguments, See [migration notes](README.md#-breaking-change-notice-for-version-700-) -* Add support for newer call limit header `X-Shopify-Shop-Api-Call-Limit`. -* Removed all Ping resources. +- Removed support for `ActiveResouce` < `4.1`. +- Removed `ShopifyAPI::Oauth`. +- Added api version support, See [migration + notes](README.md#-breaking-change-notice-for-version-700-) +- Changed `ShopifyAPI::Session` method signatures from positional to keyword + arguments, See [migration notes](README.md#-breaking-change-notice-for-version-700-) +- Add support for newer call limit header `X-Shopify-Shop-Api-Call-Limit`. +- Removed all Ping resources. ## Version 6.0.0 -* Removed undocumented `protocol` and `port` options from `ShopifyAPI::Session`. +- Removed undocumented `protocol` and `port` options from `ShopifyAPI::Session`. ## Version 5.2.4 -* Added `currency` parameter to `ShopifyAPI::Order#capture`. This parameter is required for apps that belong to the -multi-currency beta program. +- Added `currency` parameter to `ShopifyAPI::Order#capture`. This parameter is required for apps that belong to the + multi-currency beta program. ## Version 5.2.3 -* Update delivery confirmation resource to delivery confirmation details resource. +- Update delivery confirmation resource to delivery confirmation details resource. ## Version 5.2.2 -* Add delivery confirmation endpoint to Ping resources. +- Add delivery confirmation endpoint to Ping resources. ## Version 5.2.1 -* Log warning when Shopify indicates deprecated API call was performed +- Log warning when Shopify indicates deprecated API call was performed ## Version 5.2.0 -* Added `ShopifyAPI::Currency` to fetch list of supported currencies on a shop -* Added `ShopifyAPI::TenderTransaction` to fetch list of transactions on a shop -* Fixed bug with X-Shopify-Checkout-Version on ShopifyAPI::Checkout header being applied to all requests +- Added `ShopifyAPI::Currency` to fetch list of supported currencies on a shop +- Added `ShopifyAPI::TenderTransaction` to fetch list of transactions on a shop +- Fixed bug with X-Shopify-Checkout-Version on ShopifyAPI::Checkout header being applied to all requests ## Version 5.1.0 -* Added `ShopifyAPI::Publications` -* Added `ShopifyAPI::ProductPublications` -* Added `ShopifyAPI::CollectionPublications` -* Added support for new collection products endpoint from `ShopifyAPI::Collection#products` +- Added `ShopifyAPI::Publications` +- Added `ShopifyAPI::ProductPublications` +- Added `ShopifyAPI::CollectionPublications` +- Added support for new collection products endpoint from `ShopifyAPI::Collection#products` ## Version 5.0.0 -* Breaking change: `ShopifyAPI::Checkout` now maps to the Checkout API, rather than the Abandoned Checkouts API - * See the README for more details -* Added `ShopifyAPI::AbandonedCheckout` -* Added support for X-Shopify-Checkout-Version header on `ShopifyAPI::Checkout` -* Added `ShopifyAPI::ShippingRate` -* Added `ShopifyAPI::Payment` -* Added support for `Checkout::complete` endpoint -* Fixed session handling support for Rails 5.2.1 +- Breaking change: `ShopifyAPI::Checkout` now maps to the Checkout API, rather than the Abandoned Checkouts API + - See the README for more details +- Added `ShopifyAPI::AbandonedCheckout` +- Added support for X-Shopify-Checkout-Version header on `ShopifyAPI::Checkout` +- Added `ShopifyAPI::ShippingRate` +- Added `ShopifyAPI::Payment` +- Added support for `Checkout::complete` endpoint +- Fixed session handling support for Rails 5.2.1 ## Version 4.13.0 -* Added `ShopifyAPI::ApiPermission` resource for uninstalling an application -* Added a deprecation warning to `ShopifyAPI::OAuth` + +- Added `ShopifyAPI::ApiPermission` resource for uninstalling an application +- Added a deprecation warning to `ShopifyAPI::OAuth` ## Version 4.12.0 -* Added support for the GraphQL API +- Added support for the GraphQL API ## Version 4.11.0 -* Added `ShopifyAPI::InventoryItem` -* Added `ShopifyAPI::InventoryLevel` -* Added `#inventory_levels` method to `ShopifyAPI::Location` +- Added `ShopifyAPI::InventoryItem` +- Added `ShopifyAPI::InventoryLevel` +- Added `#inventory_levels` method to `ShopifyAPI::Location` ## Version 4.10.0 -* Added `ShopifyAPI::AccessScope` +- Added `ShopifyAPI::AccessScope` ## Version 4.9.1 -* Fix a bug with custom properties for orders +- Fix a bug with custom properties for orders ## Version 4.9.0 -* Added `ShopifyAPI::PriceRule` -* Added `ShopifyAPI::DiscountCode` +- Added `ShopifyAPI::PriceRule` +- Added `ShopifyAPI::DiscountCode` ## Version 4.8.0 -* Added `add_engagements` to `ShopifyAPI::MarketingEvent` +- Added `add_engagements` to `ShopifyAPI::MarketingEvent` ## Version 4.7.1 -* Added support for URL parameter (e.g. limit & page) to ShopifyAPI::Metafields -* Added support for URL parameter (e.g. limit & page) to metafield operator in ShopifyAPI::Shop +- Added support for URL parameter (e.g. limit & page) to ShopifyAPI::Metafields +- Added support for URL parameter (e.g. limit & page) to metafield operator in ShopifyAPI::Shop ## Version 4.7.0 -* Removed the mandatory `application_id` parameter from `ShopifyAPI::ProductListing` and `ShopifyAPI::CollectionListing` -* Fixed a bug related to the non-standard primary key for `ShopifyAPI::ProductListing` and `ShopifyAPI::CollectionListing` +- Removed the mandatory `application_id` parameter from `ShopifyAPI::ProductListing` and `ShopifyAPI::CollectionListing` +- Fixed a bug related to the non-standard primary key for `ShopifyAPI::ProductListing` and `ShopifyAPI::CollectionListing` ## Version 4.6.0 -* Added `ShopifyAPI::Report` +- Added `ShopifyAPI::Report` ## Version 4.5.0 -* Added `ShopifyAPI::MarketingEvent` +- Added `ShopifyAPI::MarketingEvent` ## Version 4.4.0 -* Added `ShopifyAPI::CustomerInvite` -* Support for Customer#send_invite endpoint +- Added `ShopifyAPI::CustomerInvite` +- Support for Customer#send_invite endpoint ## Version 4.3.8 -* Added `ShopifyAPI::ResourceFeedback` +- Added `ShopifyAPI::ResourceFeedback` ## Version 4.3.7 -* Added support for `complete` in `ShopifyAPI::DraftOrder` +- Added support for `complete` in `ShopifyAPI::DraftOrder` ## Version 4.3.6 -* Fixed the `customer_saved_search_id` param in `ShopifyAPI::CustomerSavedSearch#customers`. +- Fixed the `customer_saved_search_id` param in `ShopifyAPI::CustomerSavedSearch#customers`. ## Version 4.3.5 -* Added support for online mode access tokens, token expiry, and associated_user information. -* Added `ShopifyAPI::DraftOrder` -* Added `ShopifyAPI::DraftOrderInvoice` +- Added support for online mode access tokens, token expiry, and associated_user information. +- Added `ShopifyAPI::DraftOrder` +- Added `ShopifyAPI::DraftOrderInvoice` ## Version 4.3.4 -* Added `ShopifyAPI::ProductListing` -* Added `ShopifyAPI::CollectionListing` +- Added `ShopifyAPI::ProductListing` +- Added `ShopifyAPI::CollectionListing` ## Version 4.3.3 -* Added `ShopifyAPI::StorefrontAccessToken` +- Added `ShopifyAPI::StorefrontAccessToken` ## Version 4.3.2 -* Relax Ruby version requirement to >= `2.0` +- Relax Ruby version requirement to >= `2.0` ## Version 4.3.1 -* Support for ShopifyAPI::ApplicationCredit +- Support for ShopifyAPI::ApplicationCredit ## Version 4.3.0 -* Require Ruby >= `2.3.0` -* Use inheritance instead of the deprecated Rails `Module#alias_method_chain` +- Require Ruby >= `2.3.0` +- Use inheritance instead of the deprecated Rails `Module#alias_method_chain` ## Version 4.2.2 -* Support for AccessToken#delegate endpoint +- Support for AccessToken#delegate endpoint ## Version 4.2.1 -* Support for Users and Discounts (Shopify Plus only) -* Adds Customer#account_activation_url method -* Adds ability to open a fulfillment. +- Support for Users and Discounts (Shopify Plus only) +- Adds Customer#account_activation_url method +- Adds ability to open a fulfillment. ## Version 4.2.0 -* Threadsafety is now compatible with the latest ActiveResource master +- Threadsafety is now compatible with the latest ActiveResource master ## Version 4.1.1 -* Added explicit 90 second timeout to `ShopifyAPI::Base` +- Added explicit 90 second timeout to `ShopifyAPI::Base` ## Version 4.0.7 -* Added `ShippingAPI::ShippingZone` +- Added `ShippingAPI::ShippingZone` ## Version 4.0.6 -* Replaced `cancelled` with `expired` in `ShopifyAPI::ApplicationCharge` +- Replaced `cancelled` with `expired` in `ShopifyAPI::ApplicationCharge` ## Version 4.0.5 -* Added `pending`, `cancelled`, `accepted`, `declined` helper methods to `ShopifyAPI::ApplicationCharge` +- Added `pending`, `cancelled`, `accepted`, `declined` helper methods to `ShopifyAPI::ApplicationCharge` ## Version 4.0.4 -* Fixed truthiness for order cancellations. Requests are now sent in the request body and as JSON +- Fixed truthiness for order cancellations. Requests are now sent in the request body and as JSON ## Version 4.0.3 -* Fixed hmac signature validation for params with delimiters (`&`, `=` or `%`) +- Fixed hmac signature validation for params with delimiters (`&`, `=` or `%`) ## Version 4.0.2 -* Verify that the shop domain is a subdomain of .myshopify.com which creating the session +- Verify that the shop domain is a subdomain of .myshopify.com which creating the session ## Version 4.0.1 -* Added `ShopifyAPI::OAuth.revoke` for easy token revocation. +- Added `ShopifyAPI::OAuth.revoke` for easy token revocation. ## Version 3.2.6 -* Fixed CustomerSavedSearch#customers method to now correctly return only relevant customers +- Fixed CustomerSavedSearch#customers method to now correctly return only relevant customers ## Version 3.2.5 -* More useful error messages for activating nil sessions -* Add tests for commonly deleted objects, and metafield tests, fix naming error in order_risk_test.rb +- More useful error messages for activating nil sessions +- Add tests for commonly deleted objects, and metafield tests, fix naming error in order_risk_test.rb ## Version 3.2.4 -* No API changes +- No API changes ## Version 3.2.3 -* Added pry to the CLI +- Added pry to the CLI ## Version 3.2.2 -* Temporary fix for the CLI -* Add a specific exception for signature validation failures +- Temporary fix for the CLI +- Add a specific exception for signature validation failures ## Version 3.2.1 -* Added CarrierService resource -* Added optionally using threadsafe ActiveResource (see readme) -* Fixed bug in validate_signature +- Added CarrierService resource +- Added optionally using threadsafe ActiveResource (see readme) +- Fixed bug in validate_signature ## Version 3.2.0 -* in Session::request_token params is no longer optional, you must pass all the params and the method will now extract the code -* Fixed JSON errors handling (#103) -* Fixed compatibility with Ruby 2.1.x (#83) -* Fixed getting parent ID from nested resources like Variants (#44) -* Cleaned up compatibility with ActiveResource 4.0.x -* Added OrderRisk resource -* Added FulfillmentService resource -* Removed discontinued ProductSearchEngine resource -* Added convenience method Customer#search (#45) +- in Session::request_token params is no longer optional, you must pass all the params and the method will now extract the code +- Fixed JSON errors handling (#103) +- Fixed compatibility with Ruby 2.1.x (#83) +- Fixed getting parent ID from nested resources like Variants (#44) +- Cleaned up compatibility with ActiveResource 4.0.x +- Added OrderRisk resource +- Added FulfillmentService resource +- Removed discontinued ProductSearchEngine resource +- Added convenience method Customer#search (#45) ## Version 3.1.8 -* Expose `index` and `show` actions of `Location` -* Added create_permission_url and request_token helper methods -* Edited the readme to better describe the getting started procedure +- Expose `index` and `show` actions of `Location` +- Added create_permission_url and request_token helper methods +- Edited the readme to better describe the getting started procedure ## Version 3.1.7 -* Expose `authors` and `tags` action on Article +- Expose `authors` and `tags` action on Article ## Version 3.1.6 -* Add LineItem::Property resource +- Add LineItem::Property resource ## Version 3.1.5 -* Expose `orders` action on Customer +- Expose `orders` action on Customer ## Version 3.1.3 -* Expose `complete` action on Fulfillment +- Expose `complete` action on Fulfillment ## Version 3.1.2 -* Includes port in domain URI (when other than http/80 or https/443) -* Adds access to CustomerSavedSearch -* Adds resources: Order::DefaultAddress, Client::ClientDetails, Announcement -* Allows access to Articles without a blog_id -* Moves encode and as_json overrides to ShopifyAPI::Base scope -* Exposes the `order` action in SmartCollection for general use +- Includes port in domain URI (when other than http/80 or https/443) +- Adds access to CustomerSavedSearch +- Adds resources: Order::DefaultAddress, Client::ClientDetails, Announcement +- Allows access to Articles without a blog_id +- Moves encode and as_json overrides to ShopifyAPI::Base scope +- Exposes the `order` action in SmartCollection for general use ## Version 3.0.3 -* Add a `customers` helper method to the CustomerGroup resource +- Add a `customers` helper method to the CustomerGroup resource ## Version 3.0.2 -* Brevity in require statements +- Brevity in require statements ## Version 3.0.1 -* Fix saving nested resources in ActiveResource 3.1+ +- Fix saving nested resources in ActiveResource 3.1+ ## Version 3.0.0 -* Added support for OAuth Authentication -* Removal of support for Legacy Authentication -* Added Cart resource +- Added support for OAuth Authentication +- Removal of support for Legacy Authentication +- Added Cart resource ## Version 2.3.0 -* Fix double root bug with ActiveSupport 3.2.0 -* Add metafields methods on Customer resource -* Fix prefix_options on assets returned from Asset.find +- Fix double root bug with ActiveSupport 3.2.0 +- Add metafields methods on Customer resource +- Fix prefix_options on assets returned from Asset.find ## Version 2.2.0 -* Fix issues with resources that have both direct and namespaced routes -* Added detailed logger to help with debugging ActiveResource -requests/responses -* Add fulfillment#cancel +- Fix issues with resources that have both direct and namespaced routes +- Added detailed logger to help with debugging ActiveResource + requests/responses +- Add fulfillment#cancel ## Version 2.1.0 -* Fix JSON errors handling -* Remove global limit from ShopifyAPI::Limits +- Fix JSON errors handling +- Remove global limit from ShopifyAPI::Limits ## Version 2.0.0 -* Bump to 2.0.0 as this release breaks Rails 2 compatibility; we're now officially only supporting Rails 3. Rails 2 devs can follow the rails2 tag in this repo to know where we broke off -* Refactored resources into their own source files -* Added API limits functionality -* Patched ActiveResource issue with roots in JSON -* Added pending, cancelled, accepted, and declined convenience methods to ShopifyAPI::RecurringApplicationCharge -* ShopifyAPI::Session#temp now available as a convenience method to support temporarily switching to other shops when making calls -* Fixes to `shopify console` CLI tool +- Bump to 2.0.0 as this release breaks Rails 2 compatibility; we're now officially only supporting Rails 3. Rails 2 devs can follow the rails2 tag in this repo to know where we broke off +- Refactored resources into their own source files +- Added API limits functionality +- Patched ActiveResource issue with roots in JSON +- Added pending, cancelled, accepted, and declined convenience methods to ShopifyAPI::RecurringApplicationCharge +- ShopifyAPI::Session#temp now available as a convenience method to support temporarily switching to other shops when making calls +- Fixes to `shopify console` CLI tool ## Version 1.2.5 -* Fix for Article#comments +- Fix for Article#comments ## Version 1.2.4 -* Added Article#comments -* Added Order#cancel -* Added Comment#restore, #not_spam +- Added Article#comments +- Added Order#cancel +- Added Comment#restore, #not_spam ## Version 1.2.3 -* Added Customer, CustomerGroup support +- Added Customer, CustomerGroup support ## Version 1.2.2 -* Added ScriptTag support +- Added ScriptTag support ## Version 1.2.1 -* Allow abbreviated names for all commands like rails does, e.g. 'shopify c' instead of 'shopify console' -* Fix Variant to support accessing both nested variants with a product prefix as well as top level variants directly -* Add 'grande' to supported product image size variants +- Allow abbreviated names for all commands like rails does, e.g. 'shopify c' instead of 'shopify console' +- Fix Variant to support accessing both nested variants with a product prefix as well as top level variants directly +- Add 'grande' to supported product image size variants ## Version 1.2.0 -* Command-line interface -* Allow custom params when fetching a single Asset +- Command-line interface +- Allow custom params when fetching a single Asset ## Version 1.1.3 (November 4, 2010) -* Add ProductSearchEngines resource +- Add ProductSearchEngines resource ## Version 1.1.2 (October 20, 2010) -* Fix for users of ActiveResource 3.x +- Fix for users of ActiveResource 3.x ## Version 1.1.1 (October 5, 2010) -* Remove hard coded xml formatting in API calls -* Remove jeweler stuff -* Ruby 1.9 encoding fix +- Remove hard coded xml formatting in API calls +- Remove jeweler stuff +- Ruby 1.9 encoding fix ## Version 1.1.0 (September 24, 2010) -* Add new Events API for Shop, Order, Product, CustomCollection, SmartCollection, Page, Blog and Article -* Add new 'compact' product image size variant -* Rails 3 fix: attribute_accessors has to be explicitly included since activesupport 3.0.0 +- Add new Events API for Shop, Order, Product, CustomCollection, SmartCollection, Page, Blog and Article +- Add new 'compact' product image size variant +- Rails 3 fix: attribute_accessors has to be explicitly included since activesupport 3.0.0 ## Version 1.0.6 -* Add metafields -* Add latest changes from Shopify including asset support, token validation and a common base class +- Add metafields +- Add latest changes from Shopify including asset support, token validation and a common base class ## Version 1.0.0 -* extracting ShopifyAPI from Shopify into Gem +- extracting ShopifyAPI from Shopify into Gem From 239606d536462b6c2f250d79e3f50f28b3e001c0 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 7 Apr 2022 14:24:01 -0400 Subject: [PATCH 4/4] Apply comments from review --- lib/shopify_api/rest/base.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/shopify_api/rest/base.rb b/lib/shopify_api/rest/base.rb index 11c81b0c9..a8127ca80 100644 --- a/lib/shopify_api/rest/base.rb +++ b/lib/shopify_api/rest/base.rb @@ -16,7 +16,6 @@ class Base @paths = T.let([], T::Array[T::Hash[Symbol, T.any(T::Array[Symbol], String, Symbol)]]) @custom_prefix = T.let(nil, T.nilable(String)) @read_only_attributes = T.let([], T.nilable(T::Array[Symbol])) - @aliased_properties = T.let({}, T::Hash[String, String]) sig { returns(T::Hash[Symbol, T.untyped]) } @@ -338,19 +337,23 @@ def save(update_object: false) private - sig { params(var: T.any(String, Symbol), val: T.untyped).void } - def set_property(var, val) - clean = var.to_s.gsub(/[\?\s]/, "") - @aliased_properties[var.to_s] = clean if clean != var + sig { params(key: T.any(String, Symbol), val: T.untyped).void } + def set_property(key, val) + # Some API fields contain invalid characters, like `?`, which causes issues when setting them as instance + # variables. To work around that, we're cleaning them up here but keeping track of the properties that were + # aliased this way. When loading up the property, we can map back from the "invalid" field so that it is + # transparent to outside callers + clean_key = key.to_s.gsub(/[\?\s]/, "") + @aliased_properties[key.to_s] = clean_key if clean_key != key - instance_variable_set("@#{clean}", val) + instance_variable_set("@#{clean_key}", val) end - sig { params(var: T.any(String, Symbol)).returns(T.untyped) } - def get_property(var) - clean = @aliased_properties.key?(var.to_s) ? @aliased_properties[var.to_s] : var + sig { params(key: T.any(String, Symbol)).returns(T.untyped) } + def get_property(key) + clean_key = @aliased_properties.key?(key.to_s) ? @aliased_properties[key.to_s] : key - instance_variable_get("@#{clean}") + instance_variable_get("@#{clean_key}") end end end