Skip to content

Commit

Permalink
Add support for bodies other than objects
Browse files Browse the repository at this point in the history
  • Loading branch information
phylor committed Feb 1, 2023
1 parent 447f20b commit fd79a52
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 20 deletions.
6 changes: 1 addition & 5 deletions lib/ioki/apis/endpoints/show.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ def call(client, args = [], options = {})
model = options[:model] if options[:model].is_a?(model_class)
attributes, etag = model_params(client, args, options, model)

if model_class == Array
attributes
else
model_class.new(attributes, etag)
end
model_class.new(attributes, etag)
end

private
Expand Down
6 changes: 3 additions & 3 deletions lib/ioki/apis/passenger_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,19 @@ class PassengerApi
:notification_settings,
path: 'notification_settings',
base_path: [API_BASE_PATH],
model_class: Array
model_class: Ioki::Model::Passenger::NotificationSettings
),
Endpoints::ShowSingular.new(
:default_notification_settings,
path: %w[passenger notification_settings defaults],
base_path: [API_BASE_PATH],
model_class: Array
model_class: Ioki::Model::Passenger::NotificationSettings
),
Endpoints::ShowSingular.new(
:available_notification_settings,
path: %w[passenger notification_settings available],
base_path: [API_BASE_PATH],
model_class: Array
model_class: Ioki::Model::Passenger::NotificationSettings
),
Endpoints.crud_endpoints(
:notification_setting,
Expand Down
45 changes: 43 additions & 2 deletions lib/ioki/model/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,26 @@ def attribute_definitions
.collect(&:class_instance_attribute_definitions)
.reduce(&:merge)
end

def base(class_name, item_class_name: nil)
define_method :set_base_class do
@_base_class_name = class_name
@_item_class_name = item_class_name
end
end
end

attr_accessor :_raw_attributes, :_attributes, :_etag

def initialize(raw_attributes = {}, etag = nil)
def initialize(raw_attributes = base_class.new, etag = nil)
set_base_class if respond_to?(:set_base_class)

@_initial_attributes = raw_attributes
@_raw_attributes = (raw_attributes || {}).transform_keys(&:to_sym)
@_raw_attributes = if raw_attributes.is_a?(Hash)
(raw_attributes || {}).transform_keys(&:to_sym)
else
raw_attributes || base_class.new
end
@_etag = etag
reset_attributes!
end
Expand All @@ -88,6 +101,26 @@ def attributes(**attributes)
@_attributes
end

def data
return attributes if base_class == Hash

case base_class.to_s
when 'Array'
_raw_attributes.map do |item|
class_name = class_name_from_value_type(@_item_class_name, item)
model_class = constantize_in_module(class_name)

model_class.new(item)
end
else
_raw_attributes
end
end

def base_class
Object.const_get(@_base_class_name || 'Hash')
end

def type_cast_attribute_value(attribute, value)
type = self.class.attribute_definitions.dig(attribute, :type)
class_name = self.class.attribute_definitions.dig(attribute, :class_name)
Expand Down Expand Up @@ -136,6 +169,12 @@ def type_cast_attribute_value(attribute, value)
# may get overridden in hard ways by subclasses. This default
# implementation should bring us a long way.
def serialize(usecase = :read)
if !@_raw_attributes.is_a?(Hash)
return @_raw_attributes.map { |object| object.serialize(usecase) } if @_raw_attributes.is_a?(Array)

return @_raw_attributes
end

self.class.attribute_definitions.each_with_object({}) do |(attribute, definition), data|
value = public_send(attribute)

Expand Down Expand Up @@ -173,6 +212,8 @@ def ==(other)

def reset_attributes!
@_attributes = {}
return if !@_raw_attributes.is_a?(Hash)

self.class.attribute_definitions.each do |attribute, definition|
value = @_raw_attributes.key?(attribute) ? @_raw_attributes[attribute] : definition[:default]
public_send("#{attribute}=", value)
Expand Down
8 changes: 1 addition & 7 deletions lib/ioki/model/passenger/notification_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ module Ioki
module Model
module Passenger
class NotificationSettings < Base
attribute :root, on: [:read, :update], type: :array

def serialize(usecase = :read)
serialized_data = super(usecase)

serialized_data[:root]
end
base 'Array', item_class_name: 'NotificationSetting'
end
end
end
Expand Down
80 changes: 79 additions & 1 deletion spec/ioki/endpoints/show_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

before { model._etag = 'ETAG' }

it "passes on model'the s etag to the request method via headers" do
it "passes on the model's etag to the request method via headers" do
expect(client).to receive(:request).with(
url: url,
headers: { 'If-None-Match': model._etag },
Expand All @@ -52,4 +52,82 @@
endpoint.call(client, ['0815'], { model: model, params: params })
end
end

context 'with an array based response' do
let(:parsed_data) do
{ 'data' => [{ id: 'ride', name: 'ride', channels: ['sms'], type: 'notification_setting' }] }
end
let(:response) do
instance_double(
Faraday::Response,
'response double',
status: 200,
body: parsed_data,
headers: { etag: 'ETAG' }
)
end
let(:endpoint) do
described_class.new(
'notification_settings',
base_path: ['base'],
model_class: Ioki::Model::Passenger::NotificationSettings
)
end

it 'parses data and etag' do
expect(client).to receive(:request).and_return([parsed_data, response])

notification_settings = endpoint.call(client, ['0815'])

expect(notification_settings).to be_a Ioki::Model::Passenger::NotificationSettings
expect(notification_settings._etag).to eq 'ETAG'
expect(notification_settings._raw_attributes).to eq parsed_data['data']
expect(notification_settings.data).to eq [
Ioki::Model::Passenger::NotificationSetting.new(
id: 'ride',
name: 'ride',
channels: ['sms'],
type: 'notification_setting'
)
]
end
end

context 'with a string response' do
let(:model_class) do
Class.new(Ioki::Model::Base) do
base 'String'
end
end
let(:parsed_data) do
{ 'data' => 'test string' }
end
let(:response) do
instance_double(
Faraday::Response,
'response double',
status: 200,
body: parsed_data,
headers: { etag: 'ETAG' }
)
end
let(:endpoint) do
described_class.new(
'example_class',
base_path: ['base'],
model_class: model_class
)
end

it 'parses data and etag' do
expect(client).to receive(:request).and_return([parsed_data, response])

response = endpoint.call(client, ['0815'])

expect(response).to be_a model_class
expect(response._etag).to eq 'ETAG'
expect(response._raw_attributes).to eq parsed_data['data']
expect(response.data).to eq 'test string'
end
end
end
84 changes: 84 additions & 0 deletions spec/ioki/model/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -732,4 +732,88 @@
end
end
end

describe 'array body' do
let(:example_class) { Ioki::Model::Passenger::NotificationSettings }

it 'allows an array' do
model = example_class.new([])

expect(model.serialize).to eq []
end

context 'with hash objects in array' do
let(:attributes) do
[
{
'id' => 'ride',
'name' => 'ride',
'channels' => %w[sms email],
'type' => 'notification_setting'
},
{
'id' => 'booking',
'name' => 'booking',
'channels' => %w[sms],
'type' => 'notification_setting'
}
]
end

it 'parses objects in array' do
expect(model.data).to eq [
Ioki::Model::Passenger::NotificationSetting.new(
id: 'ride',
name: 'ride',
channels: %w[sms email],
type: 'notification_setting'
),
Ioki::Model::Passenger::NotificationSetting.new(
id: 'booking',
name: 'booking',
channels: %w[sms],
type: 'notification_setting'
)
]
end
end

it 'serializes objects in array' do
model = example_class.new([
Ioki::Model::Passenger::NotificationSetting.new(
name: 'ride',
channels: %w[sms email]
),
Ioki::Model::Passenger::NotificationSetting.new(
name: 'booking',
channels: %w[sms]
)
])

expect(model.serialize(:update)).to eq [
{
name: 'ride',
channels: %w[sms email]
},
{
name: 'booking',
channels: %w[sms]
}
]
end
end

describe 'string body' do
let(:example_class) do
Class.new(Ioki::Model::Base) do
base 'String'
end
end

it 'allows a string' do
model = example_class.new('test')

expect(model.serialize).to eq 'test'
end
end
end
4 changes: 2 additions & 2 deletions spec/ioki/passenger_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@
expect(params[:url].to_s).to eq('passenger/notification_settings/available')
[result_with_array_data, full_response]
end
expect(passenger_client.available_notification_settings).to be_a Array
expect(passenger_client.available_notification_settings).to be_a Ioki::Model::Passenger::NotificationSettings
end
end

Expand All @@ -391,7 +391,7 @@
expect(params[:url].to_s).to eq('passenger/notification_settings/defaults')
[result_with_array_data, full_response]
end
expect(passenger_client.default_notification_settings).to be_a Array
expect(passenger_client.default_notification_settings).to be_a Ioki::Model::Passenger::NotificationSettings
end
end

Expand Down

0 comments on commit fd79a52

Please sign in to comment.