diff --git a/lib/easypost/util.rb b/lib/easypost/util.rb index 7a6434f9..a64442dd 100644 --- a/lib/easypost/util.rb +++ b/lib/easypost/util.rb @@ -150,26 +150,28 @@ def self.convert_to_easypost_object(response, api_key, parent = nil, name = nil) when Array response.map { |i| convert_to_easypost_object(i, api_key, parent) } when Hash - if (cls_name = response[:object]) - # TODO: This line was never hit when debugging all unit tests, suggesting it's broken + # Determine class based on the "object" key in the JSON response + cls_name = response[:object] || response['object'] + if cls_name + # Use the "object" key value to look up the class cls = BY_TYPE[cls_name] - elsif response[:id] - if response[:id].index('_').nil? - cls = EasyPost::EasyPostObject - elsif (cls_prefix = response[:id][0..response[:id].index('_')]) - cls = BY_PREFIX[cls_prefix[0..-2]] - end - elsif response['id'] - if response['id'].index('_').nil? + else + # Fallback to determining class based on the "id" prefix in the JSON response + id = response[:id] || response['id'] + if id.nil? || id.index('_').nil? + # ID not present or prefix not present (ID malformed) cls = EasyPost::EasyPostObject - elsif (cls_prefix = response['id'][0..response['id'].index('_')]) - cls = BY_PREFIX[cls_prefix[0..-2]] + else + # Parse the prefix from the ID and use it to look up the class + cls_prefix = id[0..id.index('_')][0..-2] + cls = BY_PREFIX[cls_prefix] end end - + # Fallback to using the generic class if other determination methods fail (or class lookup produced no results) cls ||= EasyPost::EasyPostObject cls.construct_from(response, api_key, parent, name) else + # response is neither a Hash nor Array (used mostly when dealing with final values like strings, booleans, etc.) response end end diff --git a/spec/billing_spec.rb b/spec/billing_spec.rb index 6673ee02..2c334694 100644 --- a/spec/billing_spec.rb +++ b/spec/billing_spec.rb @@ -52,62 +52,7 @@ response = described_class.retrieve_payment_methods - expect(response).to be_an_instance_of(EasyPost::EasyPostObject) - end - - it 'deserializes the payment methods by ID prefix' do - allow(EasyPost).to receive(:make_request).with( - :get, '/v2/payment_methods', nil, - ).and_return( - { - 'id' => '123', - 'primary_payment_method' => { 'id' => 'bank_123' }, - 'secondary_payment_method' => { 'id' => 'card_123' }, - }, - ) - - response = described_class.retrieve_payment_methods - - expect(response).to be_an_instance_of(EasyPost::EasyPostObject) - expect(response[:primary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) - expect(response[:secondary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) - end - - # TODO: Reactivate when deserialization by "object" is fixed - skip it 'deserializes the payment methods by object type' do - allow(EasyPost).to receive(:make_request).with( - :get, '/v2/payment_methods', nil, - ).and_return( - { - 'id' => '123', - 'primary_payment_method' => { 'object' => 'BankAccount' }, - 'secondary_payment_method' => { 'object' => 'CreditCard' }, - }, - ) - - response = described_class.retrieve_payment_methods - - expect(response).to be_an_instance_of(EasyPost::EasyPostObject) - expect(response[:primary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) - expect(response[:secondary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) - end - - it 'does not deserialize the payment methods if prefix and object type missing' do - allow(EasyPost).to receive(:make_request).with( - :get, '/v2/payment_methods', nil, - ).and_return( - { - 'id' => '123', - 'primary_payment_method' => { 'random_key' => 'random_value' }, - 'secondary_payment_method' => { 'random_key' => 'random_value' }, - }, - ) - - response = described_class.retrieve_payment_methods - - expect(response).to be_an_instance_of(EasyPost::EasyPostObject) - expect(response[:primary_payment_method]).to be_an_instance_of(EasyPost::EasyPostObject) - expect(response[:secondary_payment_method]).to be_an_instance_of(EasyPost::EasyPostObject) + expect(response).to be_an_instance_of(EasyPost::EasyPostObject) # TODO: There's not "PaymentMethodSummary"-equivalent class yet end end diff --git a/spec/util_spec.rb b/spec/util_spec.rb index 8096c9a2..01245ab3 100644 --- a/spec/util_spec.rb +++ b/spec/util_spec.rb @@ -45,4 +45,87 @@ .to eq({ 'parent_key[nested_key]' => '123' }) end end + + describe '.convert_to_easypost_object' do + it 'converts a hash to a specific class by matching ID prefix' do + data = { + id: 'adr_123', + }.to_hash + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_a(EasyPost::Address) + end + + it 'converts a hash to a specific class by matching object type' do + data = { + object: 'Address', + } + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_a(EasyPost::Address) + end + + it 'converts a hash to a generic EasyPostObject if no matching ID or object type' do + data = { + id: 'foo_123', + object: 'Nothing', + } + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_a(EasyPost::EasyPostObject) + end + + it 'converts a hash to a generic EasyPostObject if missing ID and object type' do + data = { + random_key: 'random_value', + } + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_a(EasyPost::EasyPostObject) + end + + it 'converts sub-hashes to EasyPost object' do + data = { + 'id' => '123', + 'primary_payment_method' => { + 'id' => 'bank_123', + }, + 'secondary_payment_method' => { + 'id' => 'card_123', + }, + } + object = described_class.convert_to_easypost_object(data, nil) + + # No matching ID prefix or "object" key means the outer object will just be deserialized to an EasyPostObject + expect(object).to be_a(EasyPost::EasyPostObject) + + # The sub-hashes have proper prefixes, so they will be converted to their respective objects + expect(object[:primary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) + expect(object[:secondary_payment_method]).to be_an_instance_of(EasyPost::PaymentMethod) + end + + it 'converts an array of hashes to an array of EasyPostObjects' do + data = [ + { + 'id' => 'foo_123', + }, + ] + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_an(Array) + expect(object.first).to be_a(EasyPost::EasyPostObject) + end + + it 'converts an array of hashes to an array of specific classes if matching ID prefix or object type' do + data = [ + { + 'id' => 'adr_123', + }, + ] + object = described_class.convert_to_easypost_object(data, nil) + + expect(object).to be_an(Array) + expect(object.first).to be_a(EasyPost::Address) + end + end end