Skip to content

feat(empty-response): handle empty response in json deserialize #43

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

Merged
merged 3 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apimatic_core.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'apimatic_core'
s.version = '0.3.8'
s.version = '0.3.9'
s.summary = 'A library that contains apimatic-apimatic-core logic and utilities for consuming REST APIs using Python SDKs generated '\
'by APIMatic.'
s.description = 'The APIMatic Core libraries provide a stable runtime that powers all the functionality of SDKs.'\
Expand Down
11 changes: 11 additions & 0 deletions lib/apimatic-core/response_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def initialize
@is_date_response = false
@is_response_array = false
@is_response_void = false
@is_nullable_response = false
end

# Sets deserializer for the response.
Expand Down Expand Up @@ -138,6 +139,14 @@ def is_response_void(is_response_void)
@is_response_void = is_response_void
self
end

# Sets the is_nullable_response property.
# @param [Boolean] is_nullable_response Flag to return early in case of empty response payload.
# @return [ResponseHandler] An updated instance of ResponseHandler.
def is_nullable_response(is_nullable_response)
@is_nullable_response = is_nullable_response
self
end
# rubocop:enable Naming/PredicateName

# Main method to handle the response with all the set properties.
Expand Down Expand Up @@ -191,6 +200,8 @@ def apply_xml_deserializer(response)
# Applies deserializer to the response.
# @param [Boolean] should_symbolize_hash Flag to symbolize the hash during response deserialization.
def apply_deserializer(response, should_symbolize_hash)
return if @is_nullable_response && (response.raw_body.nil? || response.raw_body.to_s.strip.empty?)

return apply_xml_deserializer(response) if @is_xml_response
return response.raw_body if @deserializer.nil?

Expand Down
2 changes: 1 addition & 1 deletion lib/apimatic-core/utilities/api_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def self.valid_type?(value, type_callable, is_model_hash: false, is_inner_model_
# @return [Hash, Array, nil] The parsed JSON object, or nil if the input string is nil.
# @raise [TypeError] if the server responds with invalid JSON and primitive type parsing is not allowed.
def self.json_deserialize(json, should_symbolize = false, allow_primitive_type_parsing = false)
return if json.nil?
return if json.nil? || json.to_s.strip.empty?

begin
JSON.parse(json, symbolize_names: should_symbolize)
Expand Down
133 changes: 133 additions & 0 deletions test/test-apimatic-core/response_handler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,93 @@ def test_primitive_response_body
assert_equal expected_response, actual_response
end

def test_empty_response_body
response_body_mock = ''
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler.deserializer(ApiHelper.method(:deserialize_primitive_types))
.is_primitive_response(true)
.deserialize_into(proc do |response_body|
response_body.to_i
end)
.handle(response_mock, MockHelper.get_global_errors)
expected_response = 0

refute_nil actual_response
assert_equal expected_response, actual_response

actual_response = @response_handler
.deserializer(ApiHelper.method(:custom_type_deserializer))
.deserialize_into(Validate.method(:from_hash))
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response

response_body_mock = ' '
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler.deserializer(ApiHelper.method(:deserialize_primitive_types))
.is_primitive_response(true)
.deserialize_into(proc do |response_body|
response_body.to_i
end)
.handle(response_mock, MockHelper.get_global_errors)

refute_nil actual_response
assert_equal expected_response, actual_response

actual_response = @response_handler
.deserializer(ApiHelper.method(:custom_type_deserializer))
.deserialize_into(Validate.method(:from_hash))
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response
end

def test_empty_response_body_with_nullable_response_flag
response_body_mock = ''
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler.deserializer(ApiHelper.method(:deserialize_primitive_types))
.is_primitive_response(true)
.is_nullable_response(true)
.deserialize_into(proc do |response_body|
response_body.to_i
end)
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response

actual_response = @response_handler
.is_nullable_response(true)
.deserializer(ApiHelper.method(:custom_type_deserializer))
.deserialize_into(Validate.method(:from_hash))
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response

response_body_mock = ' '
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler.deserializer(ApiHelper.method(:deserialize_primitive_types))
.is_nullable_response(true)
.is_primitive_response(true)
.deserialize_into(proc do |response_body|
response_body.to_i
end)
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response

actual_response = @response_handler
.is_nullable_response(true)
.deserializer(ApiHelper.method(:custom_type_deserializer))
.deserialize_into(Validate.method(:from_hash))
.handle(response_mock, MockHelper.get_global_errors)

assert_nil actual_response
end

def test_primitive_response_body_oaf
response_body_mock = '"Sunday"'
response_mock = MockHelper.create_response status_code: 200,
Expand Down Expand Up @@ -439,6 +526,30 @@ def test_converted_sdk_api_response_with_custom_fields
assert_equal expected_response.data.to_s, actual_response.data.to_s
assert_equal expected_response.body.to_s, actual_response.body.to_s
assert_equal expected_response.cursor, actual_response.cursor

response_body_mock = ''
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler
.deserializer(ApiHelper.method(:json_deserialize))
.is_api_response(true)
.convertor(SdkApiResponseWithCustomFields.method(:create))
.handle(response_mock, MockHelper.get_global_errors, true)
expected_response = SdkApiResponseWithCustomFields.new(response_mock,
data: nil,
errors: nil)

refute_nil(actual_response)

assert_instance_of SdkApiResponseWithCustomFields, actual_response
assert_nil actual_response.errors
assert !actual_response.error?
assert actual_response.success?
assert_equal expected_response.status_code, actual_response.status_code
assert_equal expected_response.raw_body, actual_response.raw_body
assert_nil actual_response.data
assert_nil actual_response.body
assert_nil actual_response.cursor
end

def test_converted_sdk_api_response
Expand All @@ -463,6 +574,28 @@ def test_converted_sdk_api_response
assert_equal expected_response.status_code, actual_response.status_code
assert_equal expected_response.raw_body, actual_response.raw_body
assert_equal expected_response.data, actual_response.data

response_body_mock = ' '
response_mock = MockHelper.create_response status_code: 200,
raw_body: response_body_mock
actual_response = @response_handler
.deserializer(ApiHelper.method(:json_deserialize))
.is_api_response(true)
.convertor(SdkApiResponse.method(:create))
.handle(response_mock, MockHelper.get_global_errors)
expected_response = SdkApiResponse.new(response_mock,
data: nil,
errors: nil)

refute_nil(actual_response)

assert_instance_of SdkApiResponse, actual_response
assert !actual_response.error?
assert actual_response.success?
assert_nil actual_response.errors
assert_equal expected_response.status_code, actual_response.status_code
assert_equal expected_response.raw_body, actual_response.raw_body
assert_nil actual_response.data
end

def test_converted_sdk_api_response_errors
Expand Down
8 changes: 5 additions & 3 deletions test/test-apimatic-core/utilities/api_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,18 @@ def test_clean_url
end

def test_json_deserialize
assert_nil(ApiHelper.json_deserialize(nil, false))
assert_nil(ApiHelper.json_deserialize(nil, ))
assert_nil(ApiHelper.json_deserialize('', ))
assert_nil(ApiHelper.json_deserialize(' ', ))
assert_equal(ApiHelper.json_deserialize(
'{"name":"Jone","age":23,"address":"H # 531, S # 20","uid":"1234","birthday":"2016-03-13",'\
'"birthtime":"2016-03-13T12:52:32.123Z"}',
'"birthtime":"2016-03-13T12:52:32.123Z"}',
false),
{ "name" => "Jone", "age" => 23, "address" => "H # 531, S # 20", "uid" => "1234",
"birthday" => "2016-03-13", "birthtime" => "2016-03-13T12:52:32.123Z" })
assert_equal(ApiHelper.json_deserialize(
'{"name":"Jone","age":23,"address":"H # 531, S # 20","uid":"1234",'\
'"birthday":"2016-03-13","birthtime":"2016-03-13T12:52:32.123Z"}',
'"birthday":"2016-03-13","birthtime":"2016-03-13T12:52:32.123Z"}',
true),
{ :name => "Jone", :age => 23, :address => "H # 531, S # 20", :uid => "1234",
:birthday => "2016-03-13", :birthtime => "2016-03-13T12:52:32.123Z" })
Expand Down
Loading