From 2af6a44dcba0f841c812d364e880098aadc9cb7a Mon Sep 17 00:00:00 2001 From: Sebastian Jimenez Date: Thu, 12 May 2022 17:59:47 -0700 Subject: [PATCH 1/2] [FIX] Multi-word custom endpoint not respecting route format --- lib/json_api_client/resource.rb | 17 +++++- test/unit/custom_endpoint_test.rb | 86 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/lib/json_api_client/resource.rb b/lib/json_api_client/resource.rb index 0665c996..4ef7313d 100644 --- a/lib/json_api_client/resource.rb +++ b/lib/json_api_client/resource.rb @@ -260,10 +260,11 @@ def collection_endpoint(name, options = {}) metaclass = class << self self end + endpoint_name = _name_for_route_format(name) metaclass.instance_eval do define_method(name) do |*params| request_params = params.first || {} - requestor.custom(name, options, request_params) + requestor.custom(endpoint_name, options, request_params) end end end @@ -274,10 +275,11 @@ def collection_endpoint(name, options = {}) # @param options [Hash] endpoint options # @option options [Symbol] :request_method The request method (:get, :post, etc) def member_endpoint(name, options = {}) + endpoint_name = self._name_for_route_format(name) define_method name do |*params| request_params = params.first || {} request_params[self.class.primary_key] = attributes.fetch(self.class.primary_key) - self.class.requestor.custom(name, options, request_params) + self.class.requestor.custom(endpoint_name, options, request_params) end end @@ -348,6 +350,17 @@ def _build_connection(rebuild = false) yield(conn) if block_given? end end + + def _name_for_route_format(name) + case self.route_format + when :dasherized_route + name.to_s.dasherize + when :camelized_route + name.to_s.camelize(:lower) + else + name + end + end end # Instantiate a new resource object diff --git a/test/unit/custom_endpoint_test.rb b/test/unit/custom_endpoint_test.rb index dba73c0a..a63557a1 100644 --- a/test/unit/custom_endpoint_test.rb +++ b/test/unit/custom_endpoint_test.rb @@ -7,6 +7,24 @@ class Country < TestResource end +class Pet < TestResource + self.site = "http://example.com/" + self.route_format = :dasherized_route + + custom_endpoint :related_pets, on: :member, request_method: :get + custom_endpoint :vip_pets, on: :collection, request_method: :get + +end + +class MythicBeasts < TestResource + self.site = "http://example.com/" + self.route_format = :camelized_route + + custom_endpoint :related_beasts, on: :member, request_method: :get + custom_endpoint :ancient_beasts, on: :collection, request_method: :get + +end + class CustomEndpointTest < MiniTest::Test def test_collection_get @@ -43,4 +61,72 @@ def test_collection_methods_should_not_add_methods_to_all_classes assert !Class.respond_to?(:autocomplete), "adding a custom method should not add methods to all classes" end + def test_member_dasherized_route_format_converts_custom_endpoint + stub_request(:get, "http://example.com/pets/1/related-pets") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + {id: 1, type: 'pets'}, + {id: 2, type: 'pets'}, + ] + }.to_json) + + pet = Pet.new({id: 1, name: 'Otto'}) + pet.mark_as_persisted! + + related_pets = pet.related_pets + + assert_equal 2, related_pets.length + assert_equal [1,2], related_pets.map(&:id) + end + + def test_collection_dasherized_route_format_converts_custom_endpoint + stub_request(:get, "http://example.com/pets/vip-pets") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + {id: 4, type: 'pets'}, + {id: 5, type: 'pets'}, + {id: 6, type: 'pets'} + ] + }.to_json) + + vip_pets = Pet.vip_pets + + assert_equal 3, vip_pets.length + assert_equal [4,5,6], vip_pets.map(&:id) + end + + def test_member_camelized_route_format_converts_custom_endpoint + stub_request(:get, "http://example.com/mythicBeasts/1/relatedBeasts") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + {id: 1, type: 'mythic-beasts'}, + {id: 2, type: 'mythic-beasts'}, + ] + }.to_json) + + dragon = MythicBeasts.new({id: 1, name: 'Dragon'}) + dragon.mark_as_persisted! + + related_beasts = dragon.related_beasts + + assert_equal 2, related_beasts.length + assert_equal [1,2], related_beasts.map(&:id) + end + + def test_collection_camelized_route_format_converts_custom_endpoint + stub_request(:get, "http://example.com/mythicBeasts/ancientBeasts") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + {id: 4, type: 'mythic-beasts'}, + {id: 5, type: 'mythic-beasts'}, + {id: 6, type: 'mythic-beasts'} + ] + }.to_json) + + ancient_beasts = MythicBeasts.ancient_beasts + + assert_equal 3, ancient_beasts.length + assert_equal [4,5,6], ancient_beasts.map(&:id) + end + end From 9cf3d0f9d6a045349cb9ca80065c5db9b63b851b Mon Sep 17 00:00:00 2001 From: Sebastian Jimenez Date: Fri, 12 Aug 2022 17:06:08 -0700 Subject: [PATCH 2/2] Changelog update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 353ff818..abcada5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +## 1.22.0 +- [#400](https://github.com/JsonApiClient/json_api_client/pull/400) - Fix for multi-word custom endpoint and route format + ## 1.21.1 - [#404](https://github.com/JsonApiClient/json_api_client/pull/404) - Expose NotFound json errors - [#378](https://github.com/JsonApiClient/json_api_client/pull/378) - Add the ability to create a subclass of JsonApiClient::Resource to have a modified id method