From 025f5e4ad750ac006f2aecde75efc5a93f9be3ae Mon Sep 17 00:00:00 2001 From: Kirill Mokevnin Date: Tue, 6 Oct 2026 17:51:23 -0400 Subject: [PATCH 1/2] fix: accept null code and group_id in the custom field response amoCRM answers custom_fields.get_by_id with explicit nulls for code and group_id. The non-nilable CustomField variant rejected them, so the union picked Problem and a successful response was read as an error. Fixes #19 --- .../models/custom_field_get_by_id_response.rb | 8 ++-- .../custom_field_get_by_id_response.rbi | 12 ++--- .../custom_field_get_by_id_response.rbs | 16 +++---- .../custom_field_get_by_id_response_test.rb | 47 +++++++++++++++++++ 4 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 test/amocrm/models/custom_field_get_by_id_response_test.rb diff --git a/lib/amocrm/models/custom_field_get_by_id_response.rb b/lib/amocrm/models/custom_field_get_by_id_response.rb index 3e5cb13..13c2b87 100644 --- a/lib/amocrm/models/custom_field_get_by_id_response.rb +++ b/lib/amocrm/models/custom_field_get_by_id_response.rb @@ -19,7 +19,7 @@ class CustomField < Amocrm::Internal::Type::BaseModel # @!attribute code # # @return [String, nil] - optional :code, String + optional :code, String, nil?: true # @!attribute entity_type # @@ -34,7 +34,7 @@ class CustomField < Amocrm::Internal::Type::BaseModel # @!attribute group_id # # @return [Integer, nil] - optional :group_id, Integer + optional :group_id, Integer, nil?: true # @!attribute is_deletable # @@ -78,10 +78,10 @@ class CustomField < Amocrm::Internal::Type::BaseModel # @!method initialize(id: nil, code: nil, entity_type: nil, enums: nil, group_id: nil, is_deletable: nil, is_multiple: nil, is_required: nil, is_visible: nil, name: nil, settings: nil, sort: nil, type: nil) # @param id [Integer] - # @param code [String] + # @param code [String, nil] # @param entity_type [String] # @param enums [Array] - # @param group_id [Integer] + # @param group_id [Integer, nil] # @param is_deletable [Boolean] # @param is_multiple [Boolean] # @param is_required [Boolean] diff --git a/rbi/amocrm/models/custom_field_get_by_id_response.rbi b/rbi/amocrm/models/custom_field_get_by_id_response.rbi index 74da7be..fa1f5ba 100644 --- a/rbi/amocrm/models/custom_field_get_by_id_response.rbi +++ b/rbi/amocrm/models/custom_field_get_by_id_response.rbi @@ -31,7 +31,7 @@ module Amocrm sig { returns(T.nilable(String)) } attr_reader :code - sig { params(code: String).void } + sig { params(code: T.nilable(String)).void } attr_writer :code sig { returns(T.nilable(String)) } @@ -49,7 +49,7 @@ module Amocrm sig { returns(T.nilable(Integer)) } attr_reader :group_id - sig { params(group_id: Integer).void } + sig { params(group_id: T.nilable(Integer)).void } attr_writer :group_id sig { returns(T.nilable(T::Boolean)) } @@ -103,10 +103,10 @@ module Amocrm sig do params( id: Integer, - code: String, + code: T.nilable(String), entity_type: String, enums: T::Array[T.anything], - group_id: Integer, + group_id: T.nilable(Integer), is_deletable: T::Boolean, is_multiple: T::Boolean, is_required: T::Boolean, @@ -138,10 +138,10 @@ module Amocrm override.returns( { id: Integer, - code: String, + code: T.nilable(String), entity_type: String, enums: T::Array[T.anything], - group_id: Integer, + group_id: T.nilable(Integer), is_deletable: T::Boolean, is_multiple: T::Boolean, is_required: T::Boolean, diff --git a/sig/amocrm/models/custom_field_get_by_id_response.rbs b/sig/amocrm/models/custom_field_get_by_id_response.rbs index 9181b18..3cf4048 100644 --- a/sig/amocrm/models/custom_field_get_by_id_response.rbs +++ b/sig/amocrm/models/custom_field_get_by_id_response.rbs @@ -10,10 +10,10 @@ module Amocrm type custom_field = { id: Integer, - code: String, + code: String?, entity_type: String, enums: ::Array[top], - group_id: Integer, + group_id: Integer?, is_deletable: bool, is_multiple: bool, is_required: bool, @@ -31,7 +31,7 @@ module Amocrm attr_reader code: String? - def code=: (String) -> String + def code=: (String?) -> String? attr_reader entity_type: String? @@ -43,7 +43,7 @@ module Amocrm attr_reader group_id: Integer? - def group_id=: (Integer) -> Integer + def group_id=: (Integer?) -> Integer? attr_reader is_deletable: bool? @@ -79,10 +79,10 @@ module Amocrm def initialize: ( ?id: Integer, - ?code: String, + ?code: String?, ?entity_type: String, ?enums: ::Array[top], - ?group_id: Integer, + ?group_id: Integer?, ?is_deletable: bool, ?is_multiple: bool, ?is_required: bool, @@ -95,10 +95,10 @@ module Amocrm def to_hash: -> { id: Integer, - code: String, + code: String?, entity_type: String, enums: ::Array[top], - group_id: Integer, + group_id: Integer?, is_deletable: bool, is_multiple: bool, is_required: bool, diff --git a/test/amocrm/models/custom_field_get_by_id_response_test.rb b/test/amocrm/models/custom_field_get_by_id_response_test.rb new file mode 100644 index 0000000..f72d0da --- /dev/null +++ b/test/amocrm/models/custom_field_get_by_id_response_test.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +class Amocrm::Test::CustomFieldGetByIDResponseModelTest < Minitest::Test + extend Minitest::Serial + include WebMock::API + + def before_all + super + WebMock.enable! + end + + def teardown + WebMock.reset! + super + end + + def after_all + WebMock.disable! + super + end + + # amoCRM answers with an explicit null for code and group_id; the success + # variant must still win the union instead of being read as a Problem. + def test_get_by_id_with_null_fields_is_a_custom_field + stub_request(:get, "http://localhost/api/v4/leads/custom_fields/921871") + .to_return_json( + status: 200, + body: { + id: 921_871, name: "stack", type: "select", code: nil, group_id: nil, + currency: nil, tracking_callback: nil, remind: nil, chained_lists: nil, + enums: [{id: 1, value: "ruby"}] + } + ) + + response = client.custom_fields.get_by_id(921_871, entity_type: "leads") + + assert_instance_of(Amocrm::Models::CustomFieldGetByIDResponse::CustomField, response) + assert_equal("stack", response.name) + assert_equal(1, response.enums.size) + end + + private + + def client = Amocrm::Client.new(base_url: "http://localhost", token: "t", subdomain: "s", max_retries: 0) +end From a9d43bc880cc36dfa8a98e9097a33f01212e56a1 Mon Sep 17 00:00:00 2001 From: Kirill Mokevnin Date: Tue, 6 Oct 2026 17:52:40 -0400 Subject: [PATCH 2/2] test: lay out the custom field response hash one key per line --- .../models/custom_field_get_by_id_response_test.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/amocrm/models/custom_field_get_by_id_response_test.rb b/test/amocrm/models/custom_field_get_by_id_response_test.rb index f72d0da..e174acc 100644 --- a/test/amocrm/models/custom_field_get_by_id_response_test.rb +++ b/test/amocrm/models/custom_field_get_by_id_response_test.rb @@ -28,8 +28,15 @@ def test_get_by_id_with_null_fields_is_a_custom_field .to_return_json( status: 200, body: { - id: 921_871, name: "stack", type: "select", code: nil, group_id: nil, - currency: nil, tracking_callback: nil, remind: nil, chained_lists: nil, + id: 921_871, + name: "stack", + type: "select", + code: nil, + group_id: nil, + currency: nil, + tracking_callback: nil, + remind: nil, + chained_lists: nil, enums: [{id: 1, value: "ruby"}] } )