Created
April 30, 2022 02:22
-
-
Save joalbertg/f33cc067584d2b9b8d6fdf35629ad827 to your computer and use it in GitHub Desktop.
EasyBroker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# Quickstart | |
# | |
# Install: | |
# > gem install rspec --no-document | |
# | |
# Run tests | |
# > rspec -f d property_titles_service.rb | |
# | |
# Run code | |
# > ruby property_titles_service.rb | |
# | |
# note: uncomment line block between 229 and 234 to print titles | |
require 'rspec' | |
require 'ostruct' | |
require 'uri' | |
require 'net/http' | |
require 'json' | |
require 'pry' | |
class ApplicationService | |
def self.call(*args, &bloc) | |
new(*args, &bloc).call | |
end | |
private | |
def response(success: false, payload: {}, error: nil) | |
OpenStruct.new(success?: success, payload: payload, error: error) | |
end | |
end | |
class HttpClient | |
def self.call(http_method: 'get', params: {}) | |
uri = URI(params[:url]) | |
uri.query = URI.encode_www_form(params[:query]) if params[:query]&.any? | |
puts params[:url] | |
Net::HTTP.send(http_method, uri, params[:headers]) | |
end | |
end | |
class EasyBrokerHttpConfig | |
def initialize(params) | |
@endpoint = params[:endpoint] | |
@headers = params[:headers] || {} | |
@query = params[:query] || {} | |
@body = params[:body] || {} | |
end | |
def call | |
{ | |
url: "#{BASE_URL}/#{endpoint}?limit=50", | |
headers: header_params.merge(headers), | |
query: query, | |
body: body | |
} | |
end | |
private | |
BASE_URL = 'https://api.stagingeb.com/v1' | |
TOKEN = 'l7u502p8v46ba3ppgvj5y2aad50lb9' | |
attr_reader :endpoint, :headers, :query, :body | |
def header_params | |
{ | |
'X-Authorization': TOKEN, | |
'Accept': 'application/json' | |
} | |
end | |
end | |
class PropertyTitlesService < ApplicationService | |
def initialize(params) | |
super() | |
@http_client = params[:http_client] | |
@provider = params[:provider] | |
end | |
def call | |
response(success: true, payload: titles) | |
rescue StandardError | |
response(error: StandardError.new(self)) | |
end | |
private | |
attr_reader :http_client, :provider | |
def titles | |
titles = [] | |
proc = proc { |property| property['title'] } | |
response = http_client.call(params: provider.call) | |
data = JSON.parse(response) | |
loop do | |
next_page = data.dig('pagination', 'next_page') | |
titles.concat(data['content'].map(&proc)) | |
break unless next_page | |
response = http_client.call(params: provider.call.merge(url: next_page)) | |
data = JSON.parse(response) | |
end | |
titles | |
end | |
end | |
# --------------------------------------------------------------------------------------------------------------------- | |
RSpec.shared_examples 'common errors' do | |
it do | |
expect(subject.class).to eq(OpenStruct) | |
expect(subject.success?).to be_falsy | |
expect(subject.error.class).to eq(StandardError) | |
end | |
end | |
RSpec.describe(PropertyTitlesService, type: :service) do | |
context '#call' do | |
describe 'success' do | |
describe 'without properties' do | |
let(:empty_properties) { { pagination: { next_page: nil }, content: [] } } | |
let(:http_client_mock) { class_double(HttpClient, call: empty_properties.to_json).as_stubbed_const } | |
let(:provider) { EasyBrokerHttpConfig.new({}) } | |
let(:result) do | |
described_class.call( | |
http_client: http_client_mock, | |
provider: provider | |
) | |
end | |
it 'returns empty' do | |
expect(result.success?).to be_truthy | |
expect(result.payload.size).to eq(0) | |
end | |
end | |
describe 'with properties' do | |
let(:property_without_next_page) do | |
{ | |
pagination: { next_page: nil }, | |
content: [{ title: 'house three' }, { title: 'house four' }] | |
} | |
end | |
let(:property_with_next_page) do | |
{ | |
pagination: { next_page: 'https://api.stagingeb.com/v1/properties?limit=50&page=2' }, | |
content: [{ title: 'house one' }, { title: 'house two' }] | |
} | |
end | |
let(:http_client_mock) { class_double(HttpClient, call: property_without_next_page.to_json).as_stubbed_const } | |
let(:provider) { EasyBrokerHttpConfig.new(endpoint: 'properties') } | |
let(:result) do | |
described_class.call( | |
http_client: http_client_mock, | |
provider: provider | |
) | |
end | |
it 'has correct types' do | |
expect(result.class).to eq(OpenStruct) | |
expect(result.success?).to be_truthy | |
expect(result.payload.class).to eq(Array) | |
expect(result.error).to be_nil | |
end | |
it 'returns two titles' do | |
expect(http_client_mock).to receive(:call).exactly(1).times | |
expect(result.payload.size).to eq(2) | |
expect(result.payload).to all(be_a(String)) | |
end | |
it 'returns four titles' do | |
allow(:properties) | |
http_client_mock = class_double(HttpClient).as_stubbed_const | |
allow(http_client_mock).to receive(:call).with(anything).and_return( | |
property_with_next_page.to_json, | |
property_without_next_page.to_json | |
) | |
expect(http_client_mock).to receive(:call).exactly(2).times | |
result = described_class.call(http_client: http_client_mock, provider: provider) | |
expect(result.payload.size).to eq(4) | |
expect(result.payload).to all(be_a(String)) | |
end | |
end | |
end | |
context 'failure' do | |
let(:provider) { EasyBrokerHttpConfig.new({}) } | |
let(:result) do | |
described_class.call( | |
http_client: http_client, | |
provider: provider | |
) | |
end | |
describe 'returns an OpenStruct error due to invalid params' do | |
subject { described_class.call({}) } | |
include_examples 'common errors' | |
end | |
describe 'returns an OpenStruct error due to invalid provider' do | |
subject { described_class.call(http_client: HttpClient, provider: provider) } | |
include_examples 'common errors' | |
end | |
describe 'return an OpenStruct error due to request timeout' do | |
let(:provider) { EasyBrokerHttpConfig.new(endpoint: 'properties') } | |
before do | |
allow(Net::HTTP).to receive(:get).and_raise(Timeout::Error) | |
end | |
subject { described_class.call(http_client: HttpClient, provider: provider) } | |
include_examples 'common errors' | |
end | |
end | |
end | |
end | |
# titles = PropertyTitlesService.call( | |
# http_client: HttpClient, | |
# provider: EasyBrokerHttpConfig.new(endpoint: 'properties') | |
# ).payload | |
# puts titles |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment