diff --git a/Gemfile b/Gemfile index d4fbf7c0..fc257b3c 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,8 @@ gem 'jbuilder', '~> 2.5' # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', '>= 1.1.0', require: false +gem 'faraday' + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] @@ -54,5 +56,6 @@ group :development do end + # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 9c8920e9..55d2c8f2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,6 +72,10 @@ GEM diff-lcs (1.4.4) erubi (1.10.0) execjs (2.8.1) + faraday (2.6.0) + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.1) ffi (1.15.4) globalid (0.5.2) activesupport (>= 5.0) @@ -92,9 +96,13 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.2) + mini_portile2 (2.6.1) minitest (5.14.4) msgpack (1.4.2) nio4r (2.5.8) + nokogiri (1.12.5) + mini_portile2 (~> 2.6.1) + racc (~> 1.4) nokogiri (1.12.5-arm64-darwin) racc (~> 1.4) pg (1.2.3) @@ -153,6 +161,7 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.2) + ruby2_keywords (0.0.5) ruby_dep (1.5.0) sass (3.7.4) sass-listen (~> 4.0.0) @@ -199,12 +208,14 @@ GEM PLATFORMS arm64-darwin-20 + ruby DEPENDENCIES bootsnap (>= 1.1.0) byebug capybara coffee-rails (~> 4.2) + faraday jbuilder (~> 2.5) listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb new file mode 100644 index 00000000..976da56a --- /dev/null +++ b/app/controllers/items_controller.rb @@ -0,0 +1,9 @@ +class ItemsController < ApplicationController + def index + @items = ItemsFacade.get_items + end + + def show + @item = ItemsFacade.get_item(params[:id]) + end +end \ No newline at end of file diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 00000000..fc11654f --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,9 @@ +class MerchantsController < ApplicationController + def index + @merchants = MerchantsFacade.get_merchants + end + + def show + @merchant = MerchantsFacade.get_merchant(params[:id]) + end +end \ No newline at end of file diff --git a/app/facades/items_facade.rb b/app/facades/items_facade.rb new file mode 100644 index 00000000..93c5417b --- /dev/null +++ b/app/facades/items_facade.rb @@ -0,0 +1,20 @@ +class ItemsFacade + def self.get_merchant_items(merchant_id) + items_data = ItemsService.get_merchant_items(merchant_id)[:data] + items_data.map do |item_data| + Item.new(item_data) + end + end + + def self.get_item(item_id) + item_data = ItemsService.get_item(item_id)[:data] + Item.new(item_data) + end + + def self.get_items + items_data = ItemsSerice.get_items[:data] + items_data.map do |item_data| + Item.new(item_data) + end + end +end \ No newline at end of file diff --git a/app/facades/merchants_facade.rb b/app/facades/merchants_facade.rb new file mode 100644 index 00000000..e354b2d2 --- /dev/null +++ b/app/facades/merchants_facade.rb @@ -0,0 +1,13 @@ +class MerchantsFacade + def self.get_merchants + merchants_data = MerchantsService.get_merchants[:data] + merchants_data.map do |merchant_data| + Merchant.new(merchant_data) + end + end + + def self.get_merchant(merchant_id) + merchant_data = MerchantsService.get_merchant(merchant_id)[:data] + Merchant.new(merchant_data) + end +end \ No newline at end of file diff --git a/app/poros/item.rb b/app/poros/item.rb new file mode 100644 index 00000000..167f463d --- /dev/null +++ b/app/poros/item.rb @@ -0,0 +1,15 @@ +class Item + attr_reader :id, :name, :description, :unit_price + + def initialize(item_data) + @id = item_data[:id].to_i + @name = item_data[:attributes][:name] + @description = item_data[:attributes][:description] + @unit_price = item_data[:attributes][:unit_price] + @merchant_id = item_data[:attributes][:merchant_id] + end + + def merchant + MerchantsFacade.get_merchant(@merchant_id) + end +end \ No newline at end of file diff --git a/app/poros/merchant.rb b/app/poros/merchant.rb new file mode 100644 index 00000000..ef50912e --- /dev/null +++ b/app/poros/merchant.rb @@ -0,0 +1,12 @@ +class Merchant + attr_reader :id, :name, :items + + def initialize(merchant_data) + @id = merchant_data[:id].to_i + @name = merchant_data[:attributes][:name] + end + + def items + ItemsFacade.get_merchant_items(@id) + end +end \ No newline at end of file diff --git a/app/services/items_service.rb b/app/services/items_service.rb new file mode 100644 index 00000000..640a2963 --- /dev/null +++ b/app/services/items_service.rb @@ -0,0 +1,22 @@ +class ItemsService + def self.get_merchant_items(merchant_id) + response = conn.get("/api/v1/merchants/#{merchant_id}/items") + JSON.parse(response.body, symbolize_names: true) + end + + def self.get_items + response = conn.get("/api/v1/items") + JSON.parse(response.body, symbolize_names: true) + end + + def self.get_item(item_id) + response = conn.get("/api/v1/items/#{item_id}") + JSON.parse(response.body, symbolize_names: true) + end + + private + + def self.conn + Faraday.new(url: "http://localhost:3000") + end +end \ No newline at end of file diff --git a/app/services/merchants_service.rb b/app/services/merchants_service.rb new file mode 100644 index 00000000..aac2b005 --- /dev/null +++ b/app/services/merchants_service.rb @@ -0,0 +1,18 @@ +class MerchantsService + + def self.get_merchants + response = conn.get("/api/v1/merchants") + JSON.parse(response.body, symbolize_names: true) + end + + def self.get_merchant(merchant_id) + response = conn.get("/api/v1/merchants/#{merchant_id}") + JSON.parse(response.body, symbolize_names: true) + end + + private + + def self.conn + Faraday.new(url: "http://localhost:3000/") + end +end \ No newline at end of file diff --git a/app/views/items/index.html.erb b/app/views/items/index.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/items/show.html.erb b/app/views/items/show.html.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/merchants/index.html.erb b/app/views/merchants/index.html.erb new file mode 100644 index 00000000..890c5386 --- /dev/null +++ b/app/views/merchants/index.html.erb @@ -0,0 +1,5 @@ + diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb new file mode 100644 index 00000000..a54f3fe6 --- /dev/null +++ b/app/views/merchants/show.html.erb @@ -0,0 +1,6 @@ +

<%= @merchant.name %>

+ \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 787824f8..92725056 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + resources :merchants, only: [:index, :show] + resources :items, only: [:index, :show] end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 00000000..2611543b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,18 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 0) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + +end diff --git a/spec/features/merchants/index_spec.rb b/spec/features/merchants/index_spec.rb new file mode 100644 index 00000000..d69ab875 --- /dev/null +++ b/spec/features/merchants/index_spec.rb @@ -0,0 +1,28 @@ +require 'rails_helper' + +RSpec.describe "merchants index page" do + + before :each do + @merchants = MerchantsFacade.get_merchants + end + + it 'lists all the merchants' do + visit merchants_path + + @merchants.each do |merchant| + expect(page).to have_content(merchant.name) + end + end + + it 'each merchant name is a link to their show page' do + visit merchants_path + + @merchants.each do |merchant| + expect(page).to have_link(merchant.name) + end + + click_link @merchants.first.name + + expect(current_path).to eq(merchant_path(@merchants.first.id)) + end +end \ No newline at end of file diff --git a/spec/features/merchants/show_spec.rb b/spec/features/merchants/show_spec.rb new file mode 100644 index 00000000..2ff525f6 --- /dev/null +++ b/spec/features/merchants/show_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +RSpec.describe "merchants show page" do + + before :each do + @merchant = MerchantsFacade.get_merchants.first + visit merchant_path(@merchant.id) + end + + it 'shows the merchants name' do + expect(page).to have_content(@merchant.name) + end + + it 'shows the merchant items as links to their show pages' do + @merchant.items.each do |item| + expect(page).to have_link(item.name) + end + first_item = @merchant.items.first + click_link first_item.name + expect(current_path).to eq(item_path(first_item.id)) + end +end \ No newline at end of file diff --git a/spec/fixtures/files/merchants_response.json b/spec/fixtures/files/merchants_response.json new file mode 100644 index 00000000..71ee1473 --- /dev/null +++ b/spec/fixtures/files/merchants_response.json @@ -0,0 +1 @@ +{"data":[{"id":"1","type":"merchant","attributes":{"name":"Schroeder-Jerde"}},{"id":"2","type":"merchant","attributes":{"name":"Klein, Rempel and Jones"}},{"id":"3","type":"merchant","attributes":{"name":"Willms and Sons"}},{"id":"4","type":"merchant","attributes":{"name":"Cummings-Thiel"}},{"id":"5","type":"merchant","attributes":{"name":"Williamson Group"}},{"id":"6","type":"merchant","attributes":{"name":"Williamson Group"}},{"id":"7","type":"merchant","attributes":{"name":"Bernhard-Johns"}},{"id":"8","type":"merchant","attributes":{"name":"Osinski, Pollich and Koelpin"}},{"id":"9","type":"merchant","attributes":{"name":"Hand-Spencer"}},{"id":"10","type":"merchant","attributes":{"name":"Bechtelar, Jones and Stokes"}},{"id":"11","type":"merchant","attributes":{"name":"Pollich and Sons"}},{"id":"12","type":"merchant","attributes":{"name":"Kozey Group"}},{"id":"13","type":"merchant","attributes":{"name":"Tillman Group"}},{"id":"14","type":"merchant","attributes":{"name":"Dicki-Bednar"}},{"id":"15","type":"merchant","attributes":{"name":"Adams-Kovacek"}},{"id":"16","type":"merchant","attributes":{"name":"Bosco, Howe and Davis"}},{"id":"17","type":"merchant","attributes":{"name":"Ullrich-Moen"}},{"id":"18","type":"merchant","attributes":{"name":"Koepp LLC"}},{"id":"19","type":"merchant","attributes":{"name":"Brown Inc"}},{"id":"20","type":"merchant","attributes":{"name":"Schulist, Wilkinson and Leannon"}}]} \ No newline at end of file diff --git a/spec/poros/item_spec.rb b/spec/poros/item_spec.rb new file mode 100644 index 00000000..711587a6 --- /dev/null +++ b/spec/poros/item_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Item do + before :each do + + end +end \ No newline at end of file diff --git a/spec/poros/merchant_spec.rb b/spec/poros/merchant_spec.rb new file mode 100644 index 00000000..2ea490d9 --- /dev/null +++ b/spec/poros/merchant_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +RSpec.describe Merchant do + before :each do + merchants = JSON.parse(file_fixture("merchants_response.json").read, symbolize_names: true) + merchant_data = merchants[:data].first + + @merchant = Merchant.new(merchant_data) + end + + it 'exists and has attributes' do + expect(@merchant).to be_instance_of(Merchant) + expect(@merchant.id).to eq(1) + expect(@merchant.name).to eq("Schroeder-Jerde") + end +end \ No newline at end of file diff --git a/spec/services/merchants_service_spec.rb b/spec/services/merchants_service_spec.rb new file mode 100644 index 00000000..642dd541 --- /dev/null +++ b/spec/services/merchants_service_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +RSpec.describe MerchantsService do + it 'returns a list of the first 20 merchants' do + merchants = MerchantsService.get_merchants + + expect(merchants).to be_a(Hash) + expect(merchants).to have_key(:data) + expect(merchants[:data]).to be_an(Array) + + merchants[:data].each do |merchant| + expect(merchant).to have_key(:id) + expect(merchant[:type]).to eq("merchant") + expect(merchant).to have_key(:attributes) + expect(merchant[:attributes]).to have_key(:name) + expect(merchant[:attributes][:name]).to be_a(String) + end + end + + it 'returns a single merchant' do + first_merchant_id = MerchantsService.get_merchants[:data].first[:id] + + first_merchant = MerchantsService.get_merchant(first_merchant_id) + + expect(first_merchant).to be_a(Hash) + expect(first_merchant).to have_key(:data) + expect(first_merchant[:data]).to be_a(Hash) + expect(first_merchant[:data][:id]).to eq(first_merchant_id) + expect(first_merchant[:data][:type]).to eq("merchant") + expect(first_merchant[:data][:attributes]).to be_a(Hash) + expect(first_merchant[:data][:attributes]).to have_key(:name) + expect(first_merchant[:data][:attributes][:name]).to be_a(String) + end +end \ No newline at end of file