From 6478717e1857bb3c5bab11cc0078b794096bfe1c Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 11:15:18 +0200 Subject: [PATCH 01/18] init circle-ci --- .circleci/config.yml | 46 ++++++++++++++++++++++++++++++++++++++++++ .rspec | 2 ++ .ruby-version | 1 + Gemfile.lock | 12 +++++++++++ http_forwarder.gemspec | 6 ++++-- spec/spec_helper.rb | 7 +++++++ 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 .ruby-version diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..7c25adb --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,46 @@ +version: 2 +jobs: + test: + docker: + - image: circleci/ruby:2.5.1 + working_directory: ~/http_forwarder + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "Gemfile.lock" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: + name: install dependencies + command: | + bundle install --jobs=4 --retry=3 --path vendor/bundle + + - save_cache: + paths: + - ./vendor/bundle + key: v1-dependencies-{{ checksum "Gemfile.lock" }} + + # run tests! + - run: + name: run tests + command: | + mkdir /tmp/test-results + TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)" + + bundle exec rspec --format progress \ + --format RspecJunitFormatter \ + --out /tmp/test-results/rspec.xml \ + --format progress \ + $TEST_FILES + + # collect reports + - store_test_results: + path: /tmp/test-results + - store_artifacts: + path: /tmp/test-results + destination: test-results \ No newline at end of file diff --git a/.rspec b/.rspec index c99d2e7..bb69742 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,3 @@ --require spec_helper +--format documentation +--color \ No newline at end of file diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..4fd0fe3 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.5.1 \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 06b13b5..019e770 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ PATH remote: . specs: http_forwarder (0.1.0) + activesupport http GEM @@ -57,6 +58,7 @@ GEM safe_yaml (~> 1.0.0) crass (1.0.4) diff-lcs (1.3) + docile (1.3.1) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) erubi (1.7.1) @@ -74,6 +76,7 @@ GEM http_parser.rb (0.6.0) i18n (1.0.1) concurrent-ruby (~> 1.0) + json (2.1.0) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -135,7 +138,14 @@ GEM rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) rspec-support (3.7.1) + rspec_junit_formatter (0.4.1) + rspec-core (>= 2, < 4, != 2.12.0) safe_yaml (1.0.4) + simplecov (0.16.1) + docile (~> 1.1) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -167,6 +177,8 @@ DEPENDENCIES rails rake (~> 10.0) rspec-rails + rspec_junit_formatter + simplecov webmock BUNDLED WITH diff --git a/http_forwarder.gemspec b/http_forwarder.gemspec index a7bf43c..632071a 100644 --- a/http_forwarder.gemspec +++ b/http_forwarder.gemspec @@ -30,12 +30,14 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] + spec.add_dependency 'activesupport' spec.add_dependency 'http' spec.add_development_dependency 'bundler', '~> 1.16' - spec.add_development_dependency 'rake', '~> 10.0' - # spec.add_dependency "activesupport" spec.add_development_dependency 'rails' + spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rspec-rails' + spec.add_development_dependency 'rspec_junit_formatter' + spec.add_development_dependency 'simplecov' spec.add_development_dependency 'webmock' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ef5c6aa..2d3cff3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,13 @@ # Configure Rails Environment ENV["RAILS_ENV"] = "test" +# Setup simplecov +require 'simplecov' +SimpleCov.start do + add_filter '/spec/dummy' +end + +# load the dummy app require_relative "../spec/dummy/config/environment" RSpec.configure do |config| From 7d6258f6419b28b8e1a1092f6f033d70db682d35 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 11:25:19 +0200 Subject: [PATCH 02/18] wake up circle ci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c25adb..46d13d0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,4 +43,4 @@ jobs: path: /tmp/test-results - store_artifacts: path: /tmp/test-results - destination: test-results \ No newline at end of file + destination: test-results From e3b664349e2f3ab288e89a4654ad2ff1caf69da5 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 11:27:34 +0200 Subject: [PATCH 03/18] fixed task name --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 46d13d0..4c8d363 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2 jobs: - test: + build: docker: - image: circleci/ruby:2.5.1 working_directory: ~/http_forwarder @@ -43,4 +43,4 @@ jobs: path: /tmp/test-results - store_artifacts: path: /tmp/test-results - destination: test-results + destination: test-results \ No newline at end of file From 5e90e69120780e0d5200152526cc9014fb28e483 Mon Sep 17 00:00:00 2001 From: aminedhobb Date: Thu, 12 Jul 2018 11:29:51 +0200 Subject: [PATCH 04/18] module automatically included in application controller + renamed module --- lib/http_forwarder.rb | 9 ++++----- .../{forwarder_controller.rb => forwarder.rb} | 2 +- spec/dummy/app/controllers/application_controller.rb | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) rename lib/http_forwarder/{forwarder_controller.rb => forwarder.rb} (97%) diff --git a/lib/http_forwarder.rb b/lib/http_forwarder.rb index e27794e..8474a67 100644 --- a/lib/http_forwarder.rb +++ b/lib/http_forwarder.rb @@ -1,9 +1,8 @@ require 'http_forwarder/version' -require 'http_forwarder/forwarder_controller' +require 'http_forwarder/forwarder' module HttpForwarder - # TODO make this happens - # class ApplicationController - # class_eval 'include HttpForwarder::ForwarderController' - # end + class Engine < Rails::Engine + ActionController::Base.send :include, HttpForwarder::Forwarder + end end diff --git a/lib/http_forwarder/forwarder_controller.rb b/lib/http_forwarder/forwarder.rb similarity index 97% rename from lib/http_forwarder/forwarder_controller.rb rename to lib/http_forwarder/forwarder.rb index 7ed2473..83ed282 100644 --- a/lib/http_forwarder/forwarder_controller.rb +++ b/lib/http_forwarder/forwarder.rb @@ -1,6 +1,6 @@ # Forwards requests to registered services module HttpForwarder - module ForwarderController + module Forwarder # TODO Target must be configurable using external config file # maybe load YAML and read key: value TARGET = { dummy: 'http://another-dummy.org' }.freeze diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb index eefeeee..09705d1 100644 --- a/spec/dummy/app/controllers/application_controller.rb +++ b/spec/dummy/app/controllers/application_controller.rb @@ -1,3 +1,2 @@ class ApplicationController < ActionController::Base - include HttpForwarder::ForwarderController end From 3b558464c40bb3b113c45f161be745d598e9465c Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 11:39:11 +0200 Subject: [PATCH 05/18] Adding CI badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 361142a..56e1ba2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # HttpForwarder +![Build Status](https://circleci.com/gh/leikir/http_forwarder.svg?style=shield) Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/http_forwarder`. To experiment with that code, run `bin/console` for an interactive prompt. From 3dd22827df5b9d82d7e2e14954d0f88bf4bbfb86 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 13:15:04 +0200 Subject: [PATCH 06/18] added configurable forward url --- .gitignore | 1 + Gemfile.lock | 2 ++ http_forwarder.gemspec | 1 + lib/http_forwarder.rb | 4 ++++ lib/http_forwarder/forwarder.rb | 20 +++++++++++++---- lib/http_forwarder/forwarder_exception.rb | 0 spec/dummy/config/initializers/forwarder.rb | 4 ++++ spec/forwarder/forwarder_model_spec.rb | 25 +++++++++++++++++++++ spec/spec_helper.rb | 2 ++ 9 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 lib/http_forwarder/forwarder_exception.rb create mode 100644 spec/dummy/config/initializers/forwarder.rb create mode 100644 spec/forwarder/forwarder_model_spec.rb diff --git a/.gitignore b/.gitignore index a18c4b2..9373e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /spec/dummy/log/ /spec/reports/ /tmp/ +.byebug_history diff --git a/Gemfile.lock b/Gemfile.lock index 019e770..04035b7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,6 +53,7 @@ GEM public_suffix (>= 2.0.2, < 4.0) arel (9.0.0) builder (3.2.3) + byebug (10.0.2) concurrent-ruby (1.0.5) crack (0.4.3) safe_yaml (~> 1.0.0) @@ -173,6 +174,7 @@ PLATFORMS DEPENDENCIES bundler (~> 1.16) + byebug http_forwarder! rails rake (~> 10.0) diff --git a/http_forwarder.gemspec b/http_forwarder.gemspec index 632071a..7659170 100644 --- a/http_forwarder.gemspec +++ b/http_forwarder.gemspec @@ -34,6 +34,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'http' spec.add_development_dependency 'bundler', '~> 1.16' + spec.add_development_dependency 'byebug' spec.add_development_dependency 'rails' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rspec-rails' diff --git a/lib/http_forwarder.rb b/lib/http_forwarder.rb index 8474a67..6424ec4 100644 --- a/lib/http_forwarder.rb +++ b/lib/http_forwarder.rb @@ -5,4 +5,8 @@ module HttpForwarder class Engine < Rails::Engine ActionController::Base.send :include, HttpForwarder::Forwarder end + + HttpForwarder::Forwarder.configure do |config| + config.routes = [] + end end diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index 83ed282..0f93025 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -1,9 +1,7 @@ # Forwards requests to registered services module HttpForwarder module Forwarder - # TODO Target must be configurable using external config file - # maybe load YAML and read key: value - TARGET = { dummy: 'http://another-dummy.org' }.freeze + include ActiveSupport::Configurable ACTIONS_MAP = { show: :get, @@ -31,7 +29,7 @@ def forward(opts = {}) def send_request action = ACTIONS_MAP[action_name.to_sym] - base_url = TARGET[controller_name.to_sym] + base_url = find_target path = @request.original_fullpath HTTP.headers( accept: request.headers['Accept'], @@ -43,6 +41,20 @@ def send_request ) end + def find_target + entries = routes.select { |hash| hash[:controller] == controller_name.to_sym } + return entries.first[:to] if entries.one? + action = entries.select { |hash| hash[:action] == action_name.to_sym } + raise 'Action route was not specified for the given controller' if action.empty? + action.first[:to] + end + + def routes + r = Forwarder.config.routes + raise 'No routes specified for HttpForwarder::Forwarder' if r.empty? + r + end + def render_response(resp) raw_body = resp.body.to_s if raw_body.present? diff --git a/lib/http_forwarder/forwarder_exception.rb b/lib/http_forwarder/forwarder_exception.rb new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/config/initializers/forwarder.rb b/spec/dummy/config/initializers/forwarder.rb new file mode 100644 index 0000000..6ed29f1 --- /dev/null +++ b/spec/dummy/config/initializers/forwarder.rb @@ -0,0 +1,4 @@ +HttpForwarder::Forwarder.configure do |config| + config.routes << { controller: :dummy, action: :index, to: 'http://another-dummy.org' } + config.routes << { controller: :cats, to: 'http://another-cats.org' } +end \ No newline at end of file diff --git a/spec/forwarder/forwarder_model_spec.rb b/spec/forwarder/forwarder_model_spec.rb new file mode 100644 index 0000000..5c1981c --- /dev/null +++ b/spec/forwarder/forwarder_model_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +RSpec.describe HttpForwarder::Forwarder, type: :model do + context 'configuration' do + let(:target) { 'http://another-dummy.org' } + before do + HttpForwarder::Forwarder.configure do |config| + config.clear + config.routes = [] + config.routes << { controller: :dummy, action: :index, to: target } + end + class DummyClass + include HttpForwarder::Forwarder + end + end + + subject { DummyClass.new.send(:routes) } + it 'has the correct number of configured routes' do + expect(subject.size).to eq(1) + end + it 'has the correct route' do + expect(subject.first[:to]).to eq(target) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2d3cff3..ba38945 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,6 +7,8 @@ add_filter '/spec/dummy' end +require 'byebug' + # load the dummy app require_relative "../spec/dummy/config/environment" From 0ac7dc6632b8a38f13d70dc7ad74bd06e4113fb0 Mon Sep 17 00:00:00 2001 From: aminedhobb Date: Thu, 12 Jul 2018 16:28:29 +0200 Subject: [PATCH 07/18] changing module and specs [ci skip] --- lib/http_forwarder.rb | 1 + .../exceptions/no_route_exception.rb | 9 ++++ lib/http_forwarder/forwarder.rb | 9 +++- .../dummy/app/controllers/cats_controller.rb | 0 spec/dummy/app/controllers/dogs_controller.rb | 15 ++++++ .../dummy/app/controllers/dummy_controller.rb | 21 -------- spec/dummy/config/initializers/forwarder.rb | 5 +- spec/dummy/config/routes.rb | 2 +- spec/forwarder/forwarder_model_spec.rb | 49 +++++++++++++++++-- spec/forwarder/forwarder_request_spec.rb | 27 ++++++++++ spec/forwarder_spec.rb | 27 ---------- 11 files changed, 108 insertions(+), 57 deletions(-) create mode 100644 lib/http_forwarder/exceptions/no_route_exception.rb rename lib/http_forwarder/forwarder_exception.rb => spec/dummy/app/controllers/cats_controller.rb (100%) create mode 100644 spec/dummy/app/controllers/dogs_controller.rb delete mode 100644 spec/dummy/app/controllers/dummy_controller.rb create mode 100644 spec/forwarder/forwarder_request_spec.rb delete mode 100644 spec/forwarder_spec.rb diff --git a/lib/http_forwarder.rb b/lib/http_forwarder.rb index 6424ec4..7938913 100644 --- a/lib/http_forwarder.rb +++ b/lib/http_forwarder.rb @@ -1,5 +1,6 @@ require 'http_forwarder/version' require 'http_forwarder/forwarder' +require 'http_forwarder/exceptions/no_route_exception' module HttpForwarder class Engine < Rails::Engine diff --git a/lib/http_forwarder/exceptions/no_route_exception.rb b/lib/http_forwarder/exceptions/no_route_exception.rb new file mode 100644 index 0000000..d6cebc1 --- /dev/null +++ b/lib/http_forwarder/exceptions/no_route_exception.rb @@ -0,0 +1,9 @@ +module HttpForwarder + module Exception + class NoRouteException < StandardError + def message + 'No routes specified for HttpForwarder::Forwarder' + end + end + end +end \ No newline at end of file diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index 0f93025..e0beb02 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -22,6 +22,13 @@ def forward(opts = {}) end response = send_request if after + byebug + # making possible to modify the response body + response.instance_eval do + def body=(value) + @body = value + end + end send(after, response) if respond_to? after end render_response(response) @@ -51,7 +58,7 @@ def find_target def routes r = Forwarder.config.routes - raise 'No routes specified for HttpForwarder::Forwarder' if r.empty? + raise 'No routes specified for HttpForwarder::Forwarder' if r.nil? || r.empty? r end diff --git a/lib/http_forwarder/forwarder_exception.rb b/spec/dummy/app/controllers/cats_controller.rb similarity index 100% rename from lib/http_forwarder/forwarder_exception.rb rename to spec/dummy/app/controllers/cats_controller.rb diff --git a/spec/dummy/app/controllers/dogs_controller.rb b/spec/dummy/app/controllers/dogs_controller.rb new file mode 100644 index 0000000..31c15f1 --- /dev/null +++ b/spec/dummy/app/controllers/dogs_controller.rb @@ -0,0 +1,15 @@ +class DogsController < ApplicationController + def index + forward after: :update_body + end + + def create + forward after: :update_body + end + + def update_body(response) + parsed_response = response.parse(:json) + parsed_response['data']['name'] = 'rex' + response.body = parsed_response.to_json + end +end \ No newline at end of file diff --git a/spec/dummy/app/controllers/dummy_controller.rb b/spec/dummy/app/controllers/dummy_controller.rb deleted file mode 100644 index 880abb6..0000000 --- a/spec/dummy/app/controllers/dummy_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -class DummyController < ApplicationController - def index - forward after: :update_status - end - - def create - forward - end - - def update_status(response) - response.instance_variable_set(:@status, 201) - end - - def remove_id(request) - @prec = JSON.parse(request.body)['data']['id'].delete - end - - def put_id(response) - JSON.parse(response.body)['data']['id'] = @prec - end -end \ No newline at end of file diff --git a/spec/dummy/config/initializers/forwarder.rb b/spec/dummy/config/initializers/forwarder.rb index 6ed29f1..fcb952d 100644 --- a/spec/dummy/config/initializers/forwarder.rb +++ b/spec/dummy/config/initializers/forwarder.rb @@ -1,4 +1,5 @@ HttpForwarder::Forwarder.configure do |config| - config.routes << { controller: :dummy, action: :index, to: 'http://another-dummy.org' } - config.routes << { controller: :cats, to: 'http://another-cats.org' } + config.routes << { controller: :dogs, action: :index, to: 'http://doggos.woof' } + config.routes << { controller: :dogs, action: :create, to: 'http://doggy.woof' } + config.routes << { controller: :cats, to: 'http://cats.org' } end \ No newline at end of file diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 9283052..32e3e4d 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -1,3 +1,3 @@ Rails.application.routes.draw do - resources :dummy, only: %i[create index] + resources :dogs, only: %i[create index] end diff --git a/spec/forwarder/forwarder_model_spec.rb b/spec/forwarder/forwarder_model_spec.rb index 5c1981c..3f038cb 100644 --- a/spec/forwarder/forwarder_model_spec.rb +++ b/spec/forwarder/forwarder_model_spec.rb @@ -14,12 +14,51 @@ class DummyClass end end - subject { DummyClass.new.send(:routes) } - it 'has the correct number of configured routes' do - expect(subject.size).to eq(1) + context 'one route' do + subject { DummyClass.new.send(:routes) } + it 'has the correct number of configured routes' do + expect(subject.size).to eq(1) + end + it 'has the correct route' do + expect(subject.first[:to]).to eq(target) + end end - it 'has the correct route' do - expect(subject.first[:to]).to eq(target) + + context 'multiple routes' do + before do + HttpForwarder::Forwarder.configure do |config| + config.routes << { controller: :dummy, action: :create, to: target } + end + end + + subject { DummyClass.new.send(:routes) } + it 'has the correct number of configured routes' do + expect(subject.size).to eq(2) + end + + it 'has the correct destinations' do + expect(subject[0][:to]).to eq(subject[1][:to]) + expect(subject[1][:to]).to eq(target) + end end + + # context 'find destination' do + # context 'success' do + # before do + # HttpForwarder::Forwarder.configure do |config| + # config.routes << { controller: :dummy, action: :create, to: target } + # end + # end + + # subject { DummyClass.new.send(:routes) } + + + # # it 'raise a no route error' do + # # expect{subject}.to raise(RuntimeError) + # # end + # end + # end + + end end diff --git a/spec/forwarder/forwarder_request_spec.rb b/spec/forwarder/forwarder_request_spec.rb new file mode 100644 index 0000000..566be75 --- /dev/null +++ b/spec/forwarder/forwarder_request_spec.rb @@ -0,0 +1,27 @@ +require 'rails_helper' + +RSpec.describe 'forward spec gem', type: :request do + let(:body) do + { data: { + name: 'bobby' + } + }.to_json + end + + before do + stub_request(:post, 'http://doggy.woof/dogs') + .to_return(status: 200, body: body) + end + + it 'forward with modification' do + post '/dogs', params: body + expect(response.status).to eq(200) + parsed_response = JSON.parse(response.body) + expect(parsed_response['data']['name']).to eq('rex') + end + + # it 'forward with modification' do + # get '/dogs', params: body + # expect(response.status).to eq(201) + # end +end diff --git a/spec/forwarder_spec.rb b/spec/forwarder_spec.rb deleted file mode 100644 index 68077dc..0000000 --- a/spec/forwarder_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'rails_helper' - -RSpec.describe 'forward spec gem', type: :request do - let(:body) do - { data: { - id: 4 - } - }.to_json - end - - before do - stub_request(:post, 'http://another-dummy.org/dummy') - .to_return(status: 200) - stub_request(:get, /http:\/\/another-dummy.org\/dummy*/) - .to_return(status: 200) - end - - it 'forward without modification' do - post '/dummy', params: body - expect(response.status).to eq(200) - end - - it 'forward with modification' do - get '/dummy', params: body - expect(response.status).to eq(201) - end -end From 55b3bd5875eb85cd9721d47eec9661125b771dc4 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 17:50:29 +0200 Subject: [PATCH 08/18] added procs & yield to forwarder --- lib/http_forwarder/forwarder.rb | 38 ++++++------------- spec/dummy/app/controllers/dogs_controller.rb | 33 +++++++++++++--- spec/dummy/config/initializers/forwarder.rb | 3 +- spec/dummy/config/routes.rb | 2 +- spec/forwarder/forwarder_request_spec.rb | 24 +++++++++++- 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index e0beb02..874d9d0 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -13,38 +13,26 @@ module Forwarder private - def forward(opts = {}) - before = opts[:before] - after = opts[:after] - @request = request.dup - if before - send(before, @request) if respond_to? before - end - response = send_request - if after - byebug - # making possible to modify the response body - response.instance_eval do - def body=(value) - @body = value - end - end - send(after, response) if respond_to? after - end + def forward_and_render + response = forward render_response(response) end - def send_request + def forward action = ACTIONS_MAP[action_name.to_sym] base_url = find_target - path = @request.original_fullpath + path = request.original_fullpath + body = request.raw_post + body, path = yield(body, path) if block_given? + # as path is not required, it can be destroyed in yield + path ||= request.original_fullpath HTTP.headers( accept: request.headers['Accept'], content_type: request.headers['Content-Type'] - ).send( + ).request( action, "#{base_url}#{path}", - body: @request.raw_post + body: body ) end @@ -64,11 +52,7 @@ def routes def render_response(resp) raw_body = resp.body.to_s - if raw_body.present? - render body: raw_body, status: resp.status, content_type: resp.content_type - else - head resp.status and return - end + render body: raw_body, status: resp.status, content_type: resp.content_type end end end diff --git a/spec/dummy/app/controllers/dogs_controller.rb b/spec/dummy/app/controllers/dogs_controller.rb index 31c15f1..2e1d234 100644 --- a/spec/dummy/app/controllers/dogs_controller.rb +++ b/spec/dummy/app/controllers/dogs_controller.rb @@ -1,15 +1,36 @@ class DogsController < ApplicationController + + def initialize + super + set_up_procs + end + def index - forward after: :update_body + forward_and_render end + # modify the dog name to rex before forwarding the request def create - forward after: :update_body + response_from_other_api = forward do |body| + @update_dog_name.call(body) + end + render_response(response_from_other_api) end - def update_body(response) - parsed_response = response.parse(:json) - parsed_response['data']['name'] = 'rex' - response.body = parsed_response.to_json + def update + response = forward + parsed_response = JSON.parse(response.body) + parsed_response['data']['name'] = 'droopy' + render json: parsed_response, status: response.status + end + + private + + def set_up_procs + @update_dog_name = Proc.new do |body| + body = JSON.parse(body) + body['data']['name'] = 'rex' + body.to_json + end end end \ No newline at end of file diff --git a/spec/dummy/config/initializers/forwarder.rb b/spec/dummy/config/initializers/forwarder.rb index fcb952d..65e7ce5 100644 --- a/spec/dummy/config/initializers/forwarder.rb +++ b/spec/dummy/config/initializers/forwarder.rb @@ -1,5 +1,6 @@ HttpForwarder::Forwarder.configure do |config| config.routes << { controller: :dogs, action: :index, to: 'http://doggos.woof' } config.routes << { controller: :dogs, action: :create, to: 'http://doggy.woof' } + config.routes << { controller: :dogs, action: :update, to: 'http://doggos.woof' } config.routes << { controller: :cats, to: 'http://cats.org' } -end \ No newline at end of file +end diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 32e3e4d..387446b 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -1,3 +1,3 @@ Rails.application.routes.draw do - resources :dogs, only: %i[create index] + resources :dogs, only: %i[create index update] end diff --git a/spec/forwarder/forwarder_request_spec.rb b/spec/forwarder/forwarder_request_spec.rb index 566be75..6b8f7c6 100644 --- a/spec/forwarder/forwarder_request_spec.rb +++ b/spec/forwarder/forwarder_request_spec.rb @@ -8,16 +8,36 @@ }.to_json end + let(:returned_body) do + { + data: { + id: 4, + name: 'rex' + } + }.to_json + end + before do stub_request(:post, 'http://doggy.woof/dogs') - .to_return(status: 200, body: body) + .to_return(status: 200, body: returned_body) + stub_request(:put, 'http://doggos.woof/dogs/4') + .to_return(status: 201, body: returned_body) end - it 'forward with modification' do + it 'forward with modification on request' do post '/dogs', params: body expect(response.status).to eq(200) parsed_response = JSON.parse(response.body) expect(parsed_response['data']['name']).to eq('rex') + expect(parsed_response['data']['id']).to eq(4) + end + + it 'forward with modification on response' do + put '/dogs/4', params: body + expect(response.status).to eq(201) + parsed_response = JSON.parse(response.body) + expect(parsed_response['data']['name']).to eq('droopy') + expect(parsed_response['data']['id']).to eq(4) end # it 'forward with modification' do From d8300ae81615e23080f7a7d5c0bf9b69dfdd2da1 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Thu, 12 Jul 2018 17:55:02 +0200 Subject: [PATCH 09/18] added index spec --- spec/forwarder/forwarder_request_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/forwarder/forwarder_request_spec.rb b/spec/forwarder/forwarder_request_spec.rb index 6b8f7c6..74c3e00 100644 --- a/spec/forwarder/forwarder_request_spec.rb +++ b/spec/forwarder/forwarder_request_spec.rb @@ -22,6 +22,8 @@ .to_return(status: 200, body: returned_body) stub_request(:put, 'http://doggos.woof/dogs/4') .to_return(status: 201, body: returned_body) + stub_request(:get, 'http://doggos.woof/dogs') + .to_return(status: 200, body: body) end it 'forward with modification on request' do @@ -40,8 +42,9 @@ expect(parsed_response['data']['id']).to eq(4) end - # it 'forward with modification' do - # get '/dogs', params: body - # expect(response.status).to eq(201) - # end + it 'forward without modification' do + get '/dogs' + expect(response.status).to eq(200) + expect(JSON.parse(response.body)['data']['name']).to eq('bobby') + end end From 397ce3a5942e36ae5c0a5e8890176c85975fc76d Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Fri, 13 Jul 2018 10:26:08 +0200 Subject: [PATCH 10/18] temp commit [skip ci] --- lib/http_forwarder.rb | 1 + lib/http_forwarder/forwarder.rb | 6 +++++- spec/dummy/app/controllers/dogs_controller.rb | 2 +- spec/dummy/config/initializers/forwarder.rb | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/http_forwarder.rb b/lib/http_forwarder.rb index 7938913..7fb40d0 100644 --- a/lib/http_forwarder.rb +++ b/lib/http_forwarder.rb @@ -9,5 +9,6 @@ class Engine < Rails::Engine HttpForwarder::Forwarder.configure do |config| config.routes = [] + config.forward = ForwarderConfigurator.new end end diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index 874d9d0..200465f 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -23,9 +23,13 @@ def forward base_url = find_target path = request.original_fullpath body = request.raw_post - body, path = yield(body, path) if block_given? + headers = request.headers + # todo clean headers + # todo white headers config + yield(body, path) if block_given? # as path is not required, it can be destroyed in yield path ||= request.original_fullpath + body = @body if defined? @body HTTP.headers( accept: request.headers['Accept'], content_type: request.headers['Content-Type'] diff --git a/spec/dummy/app/controllers/dogs_controller.rb b/spec/dummy/app/controllers/dogs_controller.rb index 2e1d234..d08a7cc 100644 --- a/spec/dummy/app/controllers/dogs_controller.rb +++ b/spec/dummy/app/controllers/dogs_controller.rb @@ -30,7 +30,7 @@ def set_up_procs @update_dog_name = Proc.new do |body| body = JSON.parse(body) body['data']['name'] = 'rex' - body.to_json + @body = body.to_json end end end \ No newline at end of file diff --git a/spec/dummy/config/initializers/forwarder.rb b/spec/dummy/config/initializers/forwarder.rb index 65e7ce5..04c0003 100644 --- a/spec/dummy/config/initializers/forwarder.rb +++ b/spec/dummy/config/initializers/forwarder.rb @@ -1,6 +1,7 @@ HttpForwarder::Forwarder.configure do |config| - config.routes << { controller: :dogs, action: :index, to: 'http://doggos.woof' } + config.forward[:dogs] = 'http://doggos.woof' config.routes << { controller: :dogs, action: :create, to: 'http://doggy.woof' } config.routes << { controller: :dogs, action: :update, to: 'http://doggos.woof' } config.routes << { controller: :cats, to: 'http://cats.org' } + config.forward(:cats).on(:index).to('http://cats.org') end From 170af6b61d0b843529c37020a79ec6bd1dbcb898 Mon Sep 17 00:00:00 2001 From: aminedhobb Date: Fri, 13 Jul 2018 15:13:53 +0200 Subject: [PATCH 11/18] Router configurator + rubocop + more specs --- .editorconfig | 13 +++ .gitignore | 2 +- .rubocop.yml | 93 +++++++++++++++++++ lib/http_forwarder.rb | 9 +- .../exceptions/no_route_exception.rb | 9 -- lib/http_forwarder/forwarder.rb | 5 +- lib/http_forwarder/router_configurator.rb | 34 +++++++ spec/dummy/app/controllers/cats_controller.rb | 12 +++ spec/dummy/app/controllers/dogs_controller.rb | 2 + spec/dummy/config/initializers/forwarder.rb | 9 +- spec/dummy/config/routes.rb | 1 + spec/forwarder/forwarder_model_spec.rb | 25 +---- spec/forwarder/forwarder_request_spec.rb | 27 ++++++ spec/forwarder/router_configurator_spec.rb | 0 14 files changed, 195 insertions(+), 46 deletions(-) create mode 100644 .editorconfig create mode 100644 .rubocop.yml delete mode 100644 lib/http_forwarder/exceptions/no_route_exception.rb create mode 100644 lib/http_forwarder/router_configurator.rb create mode 100644 spec/forwarder/router_configurator_spec.rb diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f6cc4f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9373e4a..339b0f8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ /spec/dummy/log/ /spec/reports/ /tmp/ -.byebug_history +.byebug_history \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..86e9bef --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,93 @@ +# List of all cops and their description/default values is available here: +# https://github.com/bbatsov/rubocop/blob/master/manual/cops.md + +AllCops: + TargetRubyVersion: 2.5 + +# ==================================== Department Bundler ========================================== + +Bundler/OrderedGems: + Enabled: true + +# ===================================== Department Layout ========================================== + +Layout/AlignParameters: + EnforcedStyle: with_fixed_indentation + +Layout/CaseIndentation: + EnforcedStyle: end + +Layout/DotPosition: + EnforcedStyle: trailing + +Layout/EmptyLinesAroundClassBody: + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - no_empty_lines + +Layout/EmptyLinesAroundModuleBody: + EnforcedStyle: no_empty_lines + SupportedStyles: + - empty_lines + - no_empty_lines + +Layout/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + Enabled: false + +Layout/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: true + +Layout/EndOfLine: + Description: 'Use Unix-style line endings.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' + Enabled: true + +Layout/ExtraSpacing: + # When true, allows most uses of extra spacing if the intent is to align + # things with the previous or next line, not counting empty lines or comment + # lines. + AllowForAlignment: true + +Layout/MultilineOperationIndentation: + EnforcedStyle: indented + +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented + +# ==================================== Department Metrics ========================================== + +Metrics/AbcSize: + Max: 15 # Default is 15 + +Metrics/LineLength: + Max: 100 # Default is 80 + Exclude: + - config/schedule.rb + +Metrics/MethodLength: + Max: 25 # Default is 10 + +Metrics/ParameterLists: + Max: 5 # Default is 5 + +# ===================================== Department Style =========================================== + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/GlobalVars: + Description: 'Do not introduce global variables.' + StyleGuide: '#instance-vars' + Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' + AllowedVariables: + - $rollout + - $redis + - $redis_cache + - $redis_rollout + Enabled: true + +Style/StringLiterals: + EnforcedStyle: single_quotes \ No newline at end of file diff --git a/lib/http_forwarder.rb b/lib/http_forwarder.rb index 7fb40d0..7ae3573 100644 --- a/lib/http_forwarder.rb +++ b/lib/http_forwarder.rb @@ -1,14 +1,9 @@ require 'http_forwarder/version' require 'http_forwarder/forwarder' -require 'http_forwarder/exceptions/no_route_exception' +require 'http_forwarder/router_configurator' module HttpForwarder - class Engine < Rails::Engine - ActionController::Base.send :include, HttpForwarder::Forwarder - end - HttpForwarder::Forwarder.configure do |config| - config.routes = [] - config.forward = ForwarderConfigurator.new + config.router = RouterConfigurator.new end end diff --git a/lib/http_forwarder/exceptions/no_route_exception.rb b/lib/http_forwarder/exceptions/no_route_exception.rb deleted file mode 100644 index d6cebc1..0000000 --- a/lib/http_forwarder/exceptions/no_route_exception.rb +++ /dev/null @@ -1,9 +0,0 @@ -module HttpForwarder - module Exception - class NoRouteException < StandardError - def message - 'No routes specified for HttpForwarder::Forwarder' - end - end - end -end \ No newline at end of file diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index 200465f..8a810f2 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -28,8 +28,8 @@ def forward # todo white headers config yield(body, path) if block_given? # as path is not required, it can be destroyed in yield - path ||= request.original_fullpath body = @body if defined? @body + path = @path if defined? @path HTTP.headers( accept: request.headers['Accept'], content_type: request.headers['Content-Type'] @@ -45,11 +45,12 @@ def find_target return entries.first[:to] if entries.one? action = entries.select { |hash| hash[:action] == action_name.to_sym } raise 'Action route was not specified for the given controller' if action.empty? + raise 'two routes have been defined for two same actions' if action.size > 1 action.first[:to] end def routes - r = Forwarder.config.routes + r = Forwarder.config.router.routes raise 'No routes specified for HttpForwarder::Forwarder' if r.nil? || r.empty? r end diff --git a/lib/http_forwarder/router_configurator.rb b/lib/http_forwarder/router_configurator.rb new file mode 100644 index 0000000..5f58ba2 --- /dev/null +++ b/lib/http_forwarder/router_configurator.rb @@ -0,0 +1,34 @@ +class RouterConfigurator + + attr_reader :routes + + def initialize + @routes = [] + end + + def forward(controller) + @controller = controller.to_sym + self + end + + def on(action) + @action = action.to_sym + self + end + + def to(url) + @url = url.to_s + add_route + self + end + + private + + def add_route + if @action.nil? + @routes << { controller: @controller, to: @url } + else + @routes << { controller: @controller, action: @action, to: @url } + end + end +end \ No newline at end of file diff --git a/spec/dummy/app/controllers/cats_controller.rb b/spec/dummy/app/controllers/cats_controller.rb index e69de29..73d699a 100644 --- a/spec/dummy/app/controllers/cats_controller.rb +++ b/spec/dummy/app/controllers/cats_controller.rb @@ -0,0 +1,12 @@ +class CatsController < ApplicationController + + include HttpForwarder::Forwarder + + def index + forward_and_render + end + + def create + forward_and_render + end +end \ No newline at end of file diff --git a/spec/dummy/app/controllers/dogs_controller.rb b/spec/dummy/app/controllers/dogs_controller.rb index d08a7cc..ae74585 100644 --- a/spec/dummy/app/controllers/dogs_controller.rb +++ b/spec/dummy/app/controllers/dogs_controller.rb @@ -1,5 +1,7 @@ class DogsController < ApplicationController + include HttpForwarder::Forwarder + def initialize super set_up_procs diff --git a/spec/dummy/config/initializers/forwarder.rb b/spec/dummy/config/initializers/forwarder.rb index 04c0003..e85e2f8 100644 --- a/spec/dummy/config/initializers/forwarder.rb +++ b/spec/dummy/config/initializers/forwarder.rb @@ -1,7 +1,6 @@ HttpForwarder::Forwarder.configure do |config| - config.forward[:dogs] = 'http://doggos.woof' - config.routes << { controller: :dogs, action: :create, to: 'http://doggy.woof' } - config.routes << { controller: :dogs, action: :update, to: 'http://doggos.woof' } - config.routes << { controller: :cats, to: 'http://cats.org' } - config.forward(:cats).on(:index).to('http://cats.org') + config.router.forward(:dogs).on(:create).to('http://doggy.woof') + config.router.forward(:dogs).on(:update).to('http://doggos.woof') + config.router.forward(:dogs).on(:index).to('http://doggos.woof') + config.router.forward(:cats).to('http://kittykitty.miaw') end diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index 387446b..035ff9b 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -1,3 +1,4 @@ Rails.application.routes.draw do resources :dogs, only: %i[create index update] + resources :cats, only: %i[create index] end diff --git a/spec/forwarder/forwarder_model_spec.rb b/spec/forwarder/forwarder_model_spec.rb index 3f038cb..7eee4a0 100644 --- a/spec/forwarder/forwarder_model_spec.rb +++ b/spec/forwarder/forwarder_model_spec.rb @@ -5,9 +5,8 @@ let(:target) { 'http://another-dummy.org' } before do HttpForwarder::Forwarder.configure do |config| - config.clear - config.routes = [] - config.routes << { controller: :dummy, action: :index, to: target } + config.router = RouterConfigurator.new + config.router.forward(:dummy).on(:index).to(target) end class DummyClass include HttpForwarder::Forwarder @@ -27,7 +26,7 @@ class DummyClass context 'multiple routes' do before do HttpForwarder::Forwarder.configure do |config| - config.routes << { controller: :dummy, action: :create, to: target } + config.router.forward(:dummy).on(:create).to(target) end end @@ -42,23 +41,5 @@ class DummyClass end end - # context 'find destination' do - # context 'success' do - # before do - # HttpForwarder::Forwarder.configure do |config| - # config.routes << { controller: :dummy, action: :create, to: target } - # end - # end - - # subject { DummyClass.new.send(:routes) } - - - # # it 'raise a no route error' do - # # expect{subject}.to raise(RuntimeError) - # # end - # end - # end - - end end diff --git a/spec/forwarder/forwarder_request_spec.rb b/spec/forwarder/forwarder_request_spec.rb index 74c3e00..b366229 100644 --- a/spec/forwarder/forwarder_request_spec.rb +++ b/spec/forwarder/forwarder_request_spec.rb @@ -17,6 +17,15 @@ }.to_json end + let(:returned_cat_body) do + { + data: { + id: 5, + name: 'felix' + } + }.to_json + end + before do stub_request(:post, 'http://doggy.woof/dogs') .to_return(status: 200, body: returned_body) @@ -24,6 +33,10 @@ .to_return(status: 201, body: returned_body) stub_request(:get, 'http://doggos.woof/dogs') .to_return(status: 200, body: body) + stub_request(:get, 'http://kittykitty.miaw/cats') + .to_return(status: 200) + stub_request(:post, 'http://kittykitty.miaw/cats') + .to_return(status: 200, body: returned_cat_body) end it 'forward with modification on request' do @@ -47,4 +60,18 @@ expect(response.status).to eq(200) expect(JSON.parse(response.body)['data']['name']).to eq('bobby') end + + context 'forward to the same target for all actions if not specified' do + it 'forwards to the target in one action' do + get '/cats' + expect(response.status).to eq(200) + end + + it 'forwards to the target in another action' do + post '/cats' + expect(response.status).to eq(200) + parsed_response = JSON.parse(response.body) + expect(parsed_response['data']['name']).to eq('felix') + end + end end diff --git a/spec/forwarder/router_configurator_spec.rb b/spec/forwarder/router_configurator_spec.rb new file mode 100644 index 0000000..e69de29 From 88be7eb7db5a3effdd27c7905f3c88edf662290c Mon Sep 17 00:00:00 2001 From: aminedhobb Date: Fri, 13 Jul 2018 17:02:22 +0200 Subject: [PATCH 12/18] Updated specs + readme --- README.md | 95 +++++++++++++++++-- lib/http_forwarder/forwarder.rb | 3 +- lib/http_forwarder/router_configurator.rb | 7 +- .../{ => models}/forwarder_model_spec.rb | 0 .../{ => requests}/forwarder_request_spec.rb | 10 +- .../router/router_configurator_spec.rb | 47 +++++++++ spec/forwarder/router_configurator_spec.rb | 0 7 files changed, 144 insertions(+), 18 deletions(-) rename spec/forwarder/{ => models}/forwarder_model_spec.rb (100%) rename spec/forwarder/{ => requests}/forwarder_request_spec.rb (86%) create mode 100644 spec/forwarder/router/router_configurator_spec.rb delete mode 100644 spec/forwarder/router_configurator_spec.rb diff --git a/README.md b/README.md index 56e1ba2..4f742cd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# HttpForwarder +# About HttpForwarder ![Build Status](https://circleci.com/gh/leikir/http_forwarder.svg?style=shield) -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/http_forwarder`. To experiment with that code, run `bin/console` for an interactive prompt. - -TODO: Delete this and the text above, and describe your gem +HttpForwarder is a gem that lets you easily forward a request from your app to +a specified target. It also enables you to transform the request before doing so. +For example, it is very handy when it comes to service communications in a microservices architecture. ## Installation @@ -21,16 +21,93 @@ Or install it yourself as: $ gem install http_forwarder +To use it you'll also need to add the [http](https://github.com/httprb/http) gem for the gem to work: + +```ruby +gem 'http' +``` + ## Usage -TODO: Write usage instructions here +### Setup + +Once you installed the gem, you have to include it in the controller you want to use it in: + +```ruby + include HttpForwarder::Forwarder +``` + +Then you need to set up your routes in a new file `config/initializers/forwarder.rb` like +this: + +```ruby +HttpForwarder::Forwarder.configure do |config| + config.router.forward(:dogs).on(:create).to('http://doggy.woof') + config.router.forward(:dogs).on(:index).to('http://doggos.woof') + config.router.forward(:cats).to('http://kittykitty.miaw') +end +``` +The argument of the method `forward()`is your controller name, the argument of the method `on()` is your method name and the argument of the method `to()` is the target you want to forward to. For example, in the example above, the method `create` of the dogs controller redirects the request to `http://doggy.woof`. +It is mandatory to specify the controller and the target, whereas if you don't specify the action it will assume that all your controller methods redirect to the same url. In the example above, all the cats controller actions will redirect to `http://kittykitty.miaw`. -## Development +### Forwarding without modification + +If you want to forward the request and render the response without any modification whatsoever, simply user the `forward_and_render` method: + +```ruby +def index + forward_and_render +end +``` + +If you want to manipulate the response, simply use the `forward` method : + +```ruby +def update + response = forward + parsed_response = JSON.parse(response.body) + parsed_response['data']['name'] = 'droopy' + render json: parsed_response, status: response.status +end +``` + +### Modifying the request before forwarding + +Sometimes you might want to change the arriving request before forwarding it. To do so, you can pass a proc to our forward method to make the changes you want : + +```ruby +class DogsController < ApplicationController + + include HttpForwarder::Forwarder + + def initialize + super + set_up_procs + end + + def create + response = forward do |body| + @update_dog_name.call(body) + end + render_response(response_from_other_api) + end + + private + + def set_up_procs + @update_dog_name = Proc.new do |body| + body = JSON.parse(body) + body['data']['name'] = 'rex' + @body = body.to_json + end + end +end +``` -After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +In the example above we update the `body`, but it is also possible to change the `path` of the request or the `headers`. + Be careful though ! The variable you want to modify must contains @ before affecting the value. -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/http_forwarder. +Bug reports and pull requests are welcome on GitHub at https://github.com/leikir/http_forwarder. diff --git a/lib/http_forwarder/forwarder.rb b/lib/http_forwarder/forwarder.rb index 8a810f2..99c769f 100644 --- a/lib/http_forwarder/forwarder.rb +++ b/lib/http_forwarder/forwarder.rb @@ -26,10 +26,11 @@ def forward headers = request.headers # todo clean headers # todo white headers config - yield(body, path) if block_given? + yield(body, path, headers) if block_given? # as path is not required, it can be destroyed in yield body = @body if defined? @body path = @path if defined? @path + headers = @headers if defined? @headers HTTP.headers( accept: request.headers['Accept'], content_type: request.headers['Content-Type'] diff --git a/lib/http_forwarder/router_configurator.rb b/lib/http_forwarder/router_configurator.rb index 5f58ba2..15212fd 100644 --- a/lib/http_forwarder/router_configurator.rb +++ b/lib/http_forwarder/router_configurator.rb @@ -25,10 +25,7 @@ def to(url) private def add_route - if @action.nil? - @routes << { controller: @controller, to: @url } - else - @routes << { controller: @controller, action: @action, to: @url } - end + @routes << { controller: @controller, to: @url } and return if @action.nil? + @routes << { controller: @controller, action: @action, to: @url } end end \ No newline at end of file diff --git a/spec/forwarder/forwarder_model_spec.rb b/spec/forwarder/models/forwarder_model_spec.rb similarity index 100% rename from spec/forwarder/forwarder_model_spec.rb rename to spec/forwarder/models/forwarder_model_spec.rb diff --git a/spec/forwarder/forwarder_request_spec.rb b/spec/forwarder/requests/forwarder_request_spec.rb similarity index 86% rename from spec/forwarder/forwarder_request_spec.rb rename to spec/forwarder/requests/forwarder_request_spec.rb index b366229..ef7d8c0 100644 --- a/spec/forwarder/forwarder_request_spec.rb +++ b/spec/forwarder/requests/forwarder_request_spec.rb @@ -27,12 +27,16 @@ end before do + # we have to reload our initializer because it has been overwritten + # when we tested our forwarder model + require 'dummy/config/initializers/forwarder' + stub_request(:post, 'http://doggy.woof/dogs') - .to_return(status: 200, body: returned_body) + .to_return(status: 200, body: returned_body) stub_request(:put, 'http://doggos.woof/dogs/4') - .to_return(status: 201, body: returned_body) + .to_return(status: 201, body: returned_body) stub_request(:get, 'http://doggos.woof/dogs') - .to_return(status: 200, body: body) + .to_return(status: 200, body: body) stub_request(:get, 'http://kittykitty.miaw/cats') .to_return(status: 200) stub_request(:post, 'http://kittykitty.miaw/cats') diff --git a/spec/forwarder/router/router_configurator_spec.rb b/spec/forwarder/router/router_configurator_spec.rb new file mode 100644 index 0000000..8a9466b --- /dev/null +++ b/spec/forwarder/router/router_configurator_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +RSpec.describe RouterConfigurator do + + let(:router_configurator) { RouterConfigurator.new } + let(:target) { 'http://dummy.org' } + let(:another_target) { 'http://another-dummy.org' } + + context 'we add one route' do + + before do + router_configurator.forward(:user).on(:index).to(target) + end + + it 'contains the specified route' do + r = router_configurator.routes + expect(r.size).to eq(1) + expect(r.first[:to]).to eq(target) + end + + end + + context 'we add several routes' do + + before do + router_configurator.forward(:user).on(:create).to(target) + router_configurator.forward(:dogs).on(:index).to(another_target) + end + + it 'contains the specified routes' do + r = router_configurator.routes + expect(r.size).to eq(2) + expect(r.last[:to]).to eq(another_target) + end + end + + context 'we add a route with no action' do + before do + router_configurator.forward(:user).to(target) + end + + it 'redirects to the target' do + r = router_configurator.routes + expect(r.first[:to]).to eq(target) + end + end +end \ No newline at end of file diff --git a/spec/forwarder/router_configurator_spec.rb b/spec/forwarder/router_configurator_spec.rb deleted file mode 100644 index e69de29..0000000 From 1e0709aef4772ec247437775ede06f28217a2ba2 Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Fri, 13 Jul 2018 17:28:35 +0200 Subject: [PATCH 13/18] fixed readme and gemspec --- README.md | 65 +++++++++++++++++++++++------------------- http_forwarder.gemspec | 20 +++++++------ 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 4f742cd..e5891d6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ For example, it is very handy when it comes to service communications in a micro Add this line to your application's Gemfile: ```ruby -gem 'http_forwarder' +gem 'http_forwarder', github: 'leikir/http_forwarder' ``` And then execute: @@ -21,12 +21,6 @@ Or install it yourself as: $ gem install http_forwarder -To use it you'll also need to add the [http](https://github.com/httprb/http) gem for the gem to work: - -```ruby -gem 'http' -``` - ## Usage ### Setup @@ -50,7 +44,9 @@ end The argument of the method `forward()`is your controller name, the argument of the method `on()` is your method name and the argument of the method `to()` is the target you want to forward to. For example, in the example above, the method `create` of the dogs controller redirects the request to `http://doggy.woof`. It is mandatory to specify the controller and the target, whereas if you don't specify the action it will assume that all your controller methods redirect to the same url. In the example above, all the cats controller actions will redirect to `http://kittykitty.miaw`. -### Forwarding without modification +### Forwarding + +#### Forwarding without modification If you want to forward the request and render the response without any modification whatsoever, simply user the `forward_and_render` method: @@ -60,7 +56,9 @@ def index end ``` -If you want to manipulate the response, simply use the `forward` method : +#### Forwarding with response modification + +If you want to manipulate the response, simply use the `forward` method, then manipulate the response object : ```ruby def update @@ -71,7 +69,7 @@ def update end ``` -### Modifying the request before forwarding +#### Modifying the request before forwarding Sometimes you might want to change the arriving request before forwarding it. To do so, you can pass a proc to our forward method to make the changes you want : @@ -80,34 +78,43 @@ class DogsController < ApplicationController include HttpForwarder::Forwarder - def initialize - super - set_up_procs - end - def create - response = forward do |body| - @update_dog_name.call(body) - end - render_response(response_from_other_api) - end - - private - - def set_up_procs - @update_dog_name = Proc.new do |body| + response = forward do |body, _path, _headers| body = JSON.parse(body) body['data']['name'] = 'rex' @body = body.to_json end + render_response(response_from_other_api) end end ``` -In the example above we update the `body`, but it is also possible to change the `path` of the request or the `headers`. - Be careful though ! The variable you want to modify must contains @ before affecting the value. - +In the example above we updated a json `body` to alter a key inside it. +We didn't modify the `path` or `headers` so we could have went for: +```ruby + # modify only body + forward do |body| + ... + # modify only path + forward do |_body, path| + ... + # modify only body and headers + forward do |body, _path, headers| +end +``` +**To effectively modify the variable inside the block you must assign value to the instance variable** +```ruby + @body = body.to_json + + @body = '' + + @body = 'nil' + + @path = '/api/another/path' + + @headers = headers.select { |header| header == 'Content-Type'} +``` -## Contributing +## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/leikir/http_forwarder. diff --git a/http_forwarder.gemspec b/http_forwarder.gemspec index 7659170..65b6b4b 100644 --- a/http_forwarder.gemspec +++ b/http_forwarder.gemspec @@ -1,17 +1,21 @@ lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "http_forwarder/version" +require 'http_forwarder/version' Gem::Specification.new do |spec| - spec.name = "http_forwarder" + spec.name = 'http_forwarder' spec.version = HttpForwarder::VERSION - spec.authors = ["alexis delahaye"] - spec.email = ["alexis.delahaye@leikir.io"] - - spec.summary = %q{Write a short summary, because RubyGems requires one.} - spec.description = %q{Write a longer description or delete this line.} - # spec.homepage = "TODO: Put your gem's website or public repo URL here." + spec.authors = ['alexis delahaye', 'amine dhobb', 'sami ben-yahia'] + spec.email = ['alexis.delahaye@leikir.io', 'amine.dhobb@leikir.io', 'contact@leikir.io'] + + spec.summary = 'A gem that simply adds a forward mechanism to a Rails controller,\ + with possible modification of the request.' + spec.description = 'HttpForwarder is a gem that lets you easily forward a request from your app\ + to a specified target.\ + It also enables you to transform the request before doing so.\ + Very handy when it comes to service communications in a microservices architecture.' + spec.homepage = 'https://github.com/leikir/http_forwarder' # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' # to allow pushing to a single host or delete this section to allow pushing to any host. From f053ef7d999cec72d3d2d9d5a42c79b6c19da25e Mon Sep 17 00:00:00 2001 From: alexis delahaye Date: Fri, 13 Jul 2018 17:35:18 +0200 Subject: [PATCH 14/18] fixed readme --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e5891d6..765e0e7 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,14 @@ def index end ``` +It will forward to your configured domain, with the same path as in input. + +Examples: +- `:get, http://my-dogs.mine/4` => `:get, http://doggy.woof/4` +- `:post, http://my-cats.meow` => `:post, http://kittykitty.miaw` + +Modification is still possible, see below section. + #### Forwarding with response modification If you want to manipulate the response, simply use the `forward` method, then manipulate the response object : @@ -79,7 +87,7 @@ class DogsController < ApplicationController include HttpForwarder::Forwarder def create - response = forward do |body, _path, _headers| + response_from_other_api = forward do |body, _path, _headers| body = JSON.parse(body) body['data']['name'] = 'rex' @body = body.to_json From a0506c6f7cd53f63a8055bc9be58d2f64a7da721 Mon Sep 17 00:00:00 2001 From: alternatif Date: Mon, 16 Jul 2018 15:27:11 +0200 Subject: [PATCH 15/18] Update forwarder_model_spec.rb --- spec/forwarder/models/forwarder_model_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/forwarder/models/forwarder_model_spec.rb b/spec/forwarder/models/forwarder_model_spec.rb index 7eee4a0..5f864e6 100644 --- a/spec/forwarder/models/forwarder_model_spec.rb +++ b/spec/forwarder/models/forwarder_model_spec.rb @@ -40,6 +40,5 @@ class DummyClass expect(subject[1][:to]).to eq(target) end end - end end From 1502d1da4b2f7c39c8faa1963f4fd6b980cd6bee Mon Sep 17 00:00:00 2001 From: alternatif Date: Mon, 16 Jul 2018 15:28:38 +0200 Subject: [PATCH 16/18] Update router_configurator_spec.rb --- spec/forwarder/router/router_configurator_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/forwarder/router/router_configurator_spec.rb b/spec/forwarder/router/router_configurator_spec.rb index 8a9466b..d303ac4 100644 --- a/spec/forwarder/router/router_configurator_spec.rb +++ b/spec/forwarder/router/router_configurator_spec.rb @@ -17,7 +17,6 @@ expect(r.size).to eq(1) expect(r.first[:to]).to eq(target) end - end context 'we add several routes' do @@ -44,4 +43,4 @@ expect(r.first[:to]).to eq(target) end end -end \ No newline at end of file +end From fe47d9bf30ecd32ce05f1dc502613bbbd07061c2 Mon Sep 17 00:00:00 2001 From: alternatif Date: Mon, 16 Jul 2018 15:31:35 +0200 Subject: [PATCH 17/18] Update forwarder_request_spec.rb --- spec/forwarder/requests/forwarder_request_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/forwarder/requests/forwarder_request_spec.rb b/spec/forwarder/requests/forwarder_request_spec.rb index ef7d8c0..943e81b 100644 --- a/spec/forwarder/requests/forwarder_request_spec.rb +++ b/spec/forwarder/requests/forwarder_request_spec.rb @@ -2,18 +2,19 @@ RSpec.describe 'forward spec gem', type: :request do let(:body) do - { data: { + { + data: { name: 'bobby' - } + } }.to_json end let(:returned_body) do { - data: { + data: { id: 4, name: 'rex' - } + } }.to_json end From d928ea8f0e7b7c9c1fb2c0af1d314cdf61c60b58 Mon Sep 17 00:00:00 2001 From: Alexis DELAHAYE <16288690+Ex-Ark@users.noreply.github.com> Date: Mon, 16 Jul 2018 15:39:16 +0200 Subject: [PATCH 18/18] Create LICENSE.md --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..33e6c0b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Leikir Web + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.