diff options
| author | Luc Donnet | 2015-02-23 14:21:43 +0100 |
|---|---|---|
| committer | Luc Donnet | 2015-02-23 14:21:43 +0100 |
| commit | 6fdfb5e292994215a663349d17e349ce5ed1db29 (patch) | |
| tree | d7200738856335a34f953a47520d7ab2319ab295 | |
| parent | 811d5a768b1d0d226693db7d1e44dd54dd8b3c77 (diff) | |
| download | chouette-core-6fdfb5e292994215a663349d17e349ce5ed1db29.tar.bz2 | |
Initialize Iev Api Client
| -rw-r--r-- | Gemfile | 1 | ||||
| -rw-r--r-- | Gemfile.lock | 2 | ||||
| -rw-r--r-- | config/environments/development.rb | 3 | ||||
| -rw-r--r-- | config/environments/production.rb | 3 | ||||
| -rw-r--r-- | config/environments/test.rb | 3 | ||||
| -rw-r--r-- | lib/iev_api.rb | 23 | ||||
| -rw-r--r-- | lib/iev_api/client.rb | 103 | ||||
| -rw-r--r-- | lib/iev_api/configuration.rb | 58 | ||||
| -rw-r--r-- | lib/iev_api/middleware/raise_response_error.rb | 13 | ||||
| -rw-r--r-- | lib/iev_api/middleware/raise_server_error.rb | 18 | ||||
| -rw-r--r-- | spec/lib/iev_api/client_spec.rb | 261 |
11 files changed, 488 insertions, 0 deletions
@@ -43,6 +43,7 @@ gem "sitemap_generator" gem 'faraday', '~> 0.9.1' gem 'faraday_middleware', '~> 0.9.1' gem 'kleisli' +gem 'hashie' platforms :jruby do gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.3' diff --git a/Gemfile.lock b/Gemfile.lock index e6a721332..fd52a11bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,6 +184,7 @@ GEM has_scope (0.6.0.rc) actionpack (>= 3.2, < 5) activesupport (>= 3.2, < 5) + hashie (3.4.0) highline (1.6.15) hike (1.2.3) hitimes (1.2.2) @@ -480,6 +481,7 @@ DEPENDENCIES google-analytics-rails guard guard-rspec + hashie inherited_resources jbuilder (~> 2.0) jquery-rails diff --git a/config/environments/development.rb b/config/environments/development.rb index f74798e78..66244c886 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -41,6 +41,9 @@ Rails.application.configure do # api key to geoportail IGN (production key link to application url root referer) #config.geoportail_api_key = "aaaaaaaaaaaaaa" + # Iev server url + config.iev_url="localhost:8080/mobi.chouette.api" + # Specific theme for each company # AFIMB config.company_name = "afimb" diff --git a/config/environments/production.rb b/config/environments/production.rb index fab960e90..9106a434b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -130,6 +130,9 @@ Rails.application.configure do config.geoportail_api_key = ENV['CHOUETTE_GEOPORTAIL_KEY'] end + # Iev server url + config.iev_url="mobi.chouette.api" + # Specific theme for each company # AFIMB config.company_name = "afimb" diff --git a/config/environments/test.rb b/config/environments/test.rb index e878822cd..b1745bf90 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -42,6 +42,9 @@ Rails.application.configure do # api key to geoportail IGN (production key link to application url root referer) config.geoportail_api_key = "aaaaaaaaaaaaaa" + + # Iev server url + config.iev_url="localhost:8080/mobi.chouette.api" # Specific theme for each company # AFIMB diff --git a/lib/iev_api.rb b/lib/iev_api.rb new file mode 100644 index 000000000..77a10d1ac --- /dev/null +++ b/lib/iev_api.rb @@ -0,0 +1,23 @@ +require 'iev_api/configuration' + +module IevApi + extend Configuration + + class IevError < StandardError; end + + def self.client(options={}) + IevApi::Client.new(options) + end + + # Delegate to Instapaper::Client + def self.method_missing(method, *args, &block) + return super unless client.respond_to?(method) + client.send(method, *args, &block) + end + + def self.respond_to?(method, include_private = false) + client.respond_to?(method, include_private) || super(method, include_private) + end +end + +require 'iev_api/client' diff --git a/lib/iev_api/client.rb b/lib/iev_api/client.rb new file mode 100644 index 000000000..11a39dd10 --- /dev/null +++ b/lib/iev_api/client.rb @@ -0,0 +1,103 @@ +module IevApi + class Client + + PER_PAGE = 12 + PARALLEL_WORKERS = 10 + + attr_accessor *IevApi::Configuration::VALID_OPTIONS_KEYS + + def initialize(options={}) + attrs = IevApi.options.merge(options) + IevApi::Configuration::VALID_OPTIONS_KEYS.each do |key| + send("#{key}=", attrs[key]) + end + end + + def url_for(endpoint, *args) + path = case endpoint.to_s + when 'jobs' then jobs_path(args) + when 'job' then job_path(args) + when 'report' then report_path(args) + else raise ArgumentError.new("Unrecognized path: #{path}") + end + + [account_path, path.split('.').first].join('') + end + + def jobs(referential_id, options = {}) + results = request(:get, jobs_path(referential_id), options) + results.respond_to?(:jobs) ? results.jobs : [] + end + + def job(referential_id, job_id, options = {}) + results = request(:get, job_path(referential_id, job_id), options) + results.respond_to?(:job) ? results.job : [] + end + + def jobs_path(referential_id) + "/referentials/#{referential_id}/jobs" + end + + def job_path(referential_id, job_id) + "/referentials/#{referential_id}/jobs/#{job_id}" + end + + def report(referential_id, report_id, options = {}) + results = request(:get, report_path(referential_id, report_id), options) + results.respond_to?(:report) ? results.report : [] + end + + def report_path(referential_id, report_id) + "/referential/#{referential_id}/job/#{report_id}" + end + + def account_path + "#{protocol}://#{Rails.application.config.iev_url}" + end + + def protocol + @secure ? "https" : "http" + end + + # Perform an HTTP request + def request(method, path, params = {}, options = {}) + + response = connection(options).run_request(method, nil, nil, nil) do |request| + case method + when :delete, :get + request.url(connection.path_prefix + path, params) + when :post, :put + request.url(connection.path_prefix + path) + request.body = params unless params.empty? + end + end + + response.body + end + + def connection(options={}) + default_options = { + :headers => { + :accept => 'application/json', + :user_agent => user_agent, + }, + :ssl => {:verify => false}, + :url => account_path, + } + + @connection ||= Faraday.new(default_options.deep_merge(connection_options)) do |builder| + middleware.each { |mw| builder.use *mw } + + builder.adapter adapter + end + + # cache_dir = File.join(ENV['TMPDIR'] || '/tmp', 'cache') + + # @connection.response :caching do + # ActiveSupport::Cache::FileStore.new cache_dir, :namespace => 'iev', + # :expires_in => 3600 # one hour + # end + end + + end +end diff --git a/lib/iev_api/configuration.rb b/lib/iev_api/configuration.rb new file mode 100644 index 000000000..e01cbe102 --- /dev/null +++ b/lib/iev_api/configuration.rb @@ -0,0 +1,58 @@ +module IevApi + module Configuration + VALID_OPTIONS_KEYS = [ + :account, + :auth_token, + :secure, + :connection_options, + :adapter, + :user_agent, + :middleware] + + attr_accessor *VALID_OPTIONS_KEYS + + DEFAULT_ADAPTER = :net_http + DEFAULT_USER_AGENT = "IEV Ruby Gem Api" + DEFAULT_CONNECTION_OPTIONS = {} + DEFAULT_MIDDLEWARE = [ + Faraday::Request::UrlEncoded, + IevApi::Middleware::RaiseResponseError, + Faraday::Request::Multipart, + FaradayMiddleware::Mashify, + #FaradayMiddleware::Caching, + FaradayMiddleware::FollowRedirects, + FaradayMiddleware::ParseJson, + IevApi::Middleware::RaiseServerError, + ] + + def self.extended(base) + base.reset + end + + def configure(options={}) + @account = options[:account] if options.has_key?(:account) + @auth_token = options[:auth_token] if options.has_key?(:auth_token) + @secure = options[:secure] if options.has_key?(:secure) + @middleware = options[:middleware] if options.has_key?(:middleware) + yield self if block_given? + self + end + + def options + options = {} + VALID_OPTIONS_KEYS.each{|k| options[k] = send(k)} + options + end + + def reset + @account = nil + @auth_token = nil + @secure = false + @adapter = DEFAULT_ADAPTER + @user_agent = DEFAULT_USER_AGENT + @connection_options = DEFAULT_CONNECTION_OPTIONS + @middleware = DEFAULT_MIDDLEWARE + end + + end +end diff --git a/lib/iev_api/middleware/raise_response_error.rb b/lib/iev_api/middleware/raise_response_error.rb new file mode 100644 index 000000000..e299e3410 --- /dev/null +++ b/lib/iev_api/middleware/raise_response_error.rb @@ -0,0 +1,13 @@ +require 'faraday' + +module IevApi + module Middleware + class RaiseResponseError < Faraday::Response::Middleware + + def on_complete(env) + raise IevError.new('No results found.') if env[:body].nil? + end + + end + end +end diff --git a/lib/iev_api/middleware/raise_server_error.rb b/lib/iev_api/middleware/raise_server_error.rb new file mode 100644 index 000000000..cb6f96f98 --- /dev/null +++ b/lib/iev_api/middleware/raise_server_error.rb @@ -0,0 +1,18 @@ +require 'faraday' + +module IevApi + module Middleware + class RaiseServerError < Faraday::Response::Middleware + + def on_complete(env) + case env[:status].to_i + when 403 + raise IevError.new('SSL should be enabled - use AirbrakeAPI.secure = true in configuration') + when 404 + raise IevError.new('No resource found') + end + end + + end + end +end diff --git a/spec/lib/iev_api/client_spec.rb b/spec/lib/iev_api/client_spec.rb new file mode 100644 index 000000000..b0f4537dd --- /dev/null +++ b/spec/lib/iev_api/client_spec.rb @@ -0,0 +1,261 @@ +require 'spec_helper' + +describe IevApi::Client do + describe 'initialization' do + before do + @keys = IevApi::Configuration::VALID_OPTIONS_KEYS + end + + context "with module configuration" do + before do + IevApi.configure do |config| + @keys.each do |key| + config.send("#{key}=", key) + end + end + end + + after do + IevApi.reset + end + + it "should inherit module configuration" do + api = IevApi::Client.new + @keys.each do |key| + expect(api.send(key)).to eq(key) + end + end + + context "with class configuration" do + + before do + @configuration = { + :account => 'test', + :auth_token => 'token', + :secure => true, + :connection_options => {}, + :adapter => :em_http, + :user_agent => 'Iev API Tests', + :middleware => IevApi::Configuration::DEFAULT_MIDDLEWARE + } + end + + context "during initialization" do + it "should override module configuration" do + api = IevApi::Client.new(@configuration) + @keys.each do |key| + expect(api.send(key)).to eq(@configuration[key]) + end + end + end + + context "after initilization" do + it "should override module configuration after initialization" do + api = IevApi::Client.new + @configuration.each do |key, value| + api.send("#{key}=", value) + end + @keys.each do |key| + expect(api.send(key)).to eq(@configuration[key]) + end + end + end + end + end + + # context 'with customized middleware' do + # let(:logdev) { StringIO.new } + # # Client#connection is a private method. + # # Adding logger middleware component with an argument it should receive + # # when a connection is initialized + # let(:logger_middleware) { [Faraday::Response::Logger, Logger.new(logdev)] } + # let(:options) do + # { + # :account => 'myapp', :auth_token => 'abcdefg123456', :secure => false, + # :middleware => IevApi::Configuration::DEFAULT_MIDDLEWARE + [logger_middleware] + # } + # end + # let(:api) { IevApi::Client.new(options) } + + # # request something to initialize @connection with middleware + # #before { api.jobs("test") } + + # it 'splats array to initialize middleware with arguments' do + # # check that the logger added above did receive the argument + # expect(logdev.string).to include(api.projects_path) + # end + # end + end + + describe 'api requests'do + before(:all) do + options = { :account => 'myapp', :secure => false } + IevApi.configure(options) + + @client = IevApi::Client.new + end + + # it "should fail with errors" do + # expect { + # @client.notices(1696172) + # }.to raise_error(IevApi::AirbrakeError, /You are not authorized to see that page/) + # end + + # describe '#deploys' do + # it 'returns an array of deploys' do + # expect(@client.deploys('12345')).to be_kind_of(Array) + # end + + # it 'returns deploy data' do + # deploys = @client.deploys('12345') + # first_deploy = deploys.first + + # expect(first_deploy.rails_env).to eq('production') + # end + + # it 'returns empty when no data' do + # expect(@client.deploys('67890')).to be_kind_of(Array) + # end + # end + + # describe '#projects' do + # it 'returns an array of projects' do + # expect(@client.projects).to be_kind_of(Array) + # end + + # it 'returns project data' do + # projects = @client.projects + # expect(projects.size).to eq(4) + # expect(projects.first.id).to eq('1') + # expect(projects.first.name).to eq('Venkman') + # end + # end + + # describe '#update' do + # it 'should update the status of an error' do + # error = @client.update(1696170, :group => { :resolved => true}) + # expect(error.resolved).to be_truthy + # end + # end + + # describe '#errors' do + # it "should find a page of the 30 most recent errors" do + # errors = @client.errors + # ordered = errors.sort_by(&:most_recent_notice_at).reverse + # expect(ordered).to eq(errors) + # expect(errors.size).to eq(30) + # end + + # it "should paginate errors" do + # errors = @client.errors(:page => 2) + # ordered = errors.sort_by(&:most_recent_notice_at).reverse + # expect(ordered).to eq(errors) + # expect(errors.size).to eq(2) + # end + + # it "should use project_id for error path" do + # expect(@client).to receive(:request).with(:get, "/projects/123/groups.xml", {}).and_return(double(:group => 111)) + # @client.errors(:project_id => 123) + # end + # end + + # describe '#error' do + # it "should find an individual error" do + # error = @client.error(1696170) + # expect(error.action).to eq('index') + # expect(error.id).to eq(1696170) + # end + # end + + # describe '#notice' do + # it "finds individual notices" do + # expect(@client.notice(1234, 1696170)).not_to be_nil + # end + + # it "finds broken notices" do + # expect(@client.notice(666, 1696170)).not_to be_nil + # end + # end + + # describe '#notices' do + # it "finds all error notices" do + # notices = @client.notices(1696170) + # expect(notices.size).to eq(42) + # end + + # it "finds error notices for a specific page" do + # notices = @client.notices(1696170, :page => 1) + # expect(notices.size).to eq(30) + # expect(notices.first.backtrace).not_to eq(nil) + # expect(notices.first.id).to eq(1234) + # end + + # it "finds all error notices with a page limit" do + # notices = @client.notices(1696171, :pages => 2) + # expect(notices.size).to eq(60) + # end + + # it "yields batches" do + # batches = [] + # notices = @client.notices(1696171, :pages => 2) do |batch| + # batches << batch + # end + # expect(notices.size).to eq(60) + # expect(batches.map(&:size)).to eq([30,30]) + # end + + # it "can return raw results" do + # notices = @client.notices(1696170, :raw => true) + # expect(notices.first.backtrace).to eq(nil) + # expect(notices.first.id).to eq(1234) + # end + # end + + # describe '#connection' do + # it 'returns a Faraday connection' do + # expect(@client.send(:connection)).to be_kind_of(Faraday::Connection) + # end + # end + end + + # describe '#url_for' do + # before(:all) do + # options = { :account => 'myapp', :auth_token => 'abcdefg123456', :secure => false } + # IevApi.configure(options) + + # @client = IevApi::Client.new + # end + + # it 'generates web urls for projects' do + # expect(@client.url_for(:projects)).to eq('http://myapp.airbrake.io/projects') + # end + + # it 'generates web urls for deploys' do + # expect(@client.url_for(:deploys, '2000')).to eq('http://myapp.airbrake.io/projects/2000/deploys') + # end + + # it 'generates web urls for errors' do + # expect(@client.url_for(:errors)).to eq('http://myapp.airbrake.io/groups') + # end + + # it 'generates web urls for errors with project_id' do + # expect(@client.url_for(:errors, :project_id => 123)).to eq('http://myapp.airbrake.io/projects/123/groups') + # end + + # it 'generates web urls for individual errors' do + # expect(@client.url_for(:error, 1696171)).to eq('http://myapp.airbrake.io/errors/1696171') + # end + + # it 'generates web urls for notices' do + # expect(@client.url_for(:notices, 1696171)).to eq('http://myapp.airbrake.io/groups/1696171/notices') + # end + + # it 'generates web urls for individual notices' do + # expect(@client.url_for(:notice, 123, 1696171)).to eq('http://myapp.airbrake.io/groups/1696171/notices/123') + # end + + # it 'raises an exception when passed an unknown endpoint' do + # expect { @client.url_for(:foo) }.to raise_error(ArgumentError) + # end + # end +end |
