aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuc Donnet2015-02-23 14:21:43 +0100
committerLuc Donnet2015-02-23 14:21:43 +0100
commit6fdfb5e292994215a663349d17e349ce5ed1db29 (patch)
treed7200738856335a34f953a47520d7ab2319ab295
parent811d5a768b1d0d226693db7d1e44dd54dd8b3c77 (diff)
downloadchouette-core-6fdfb5e292994215a663349d17e349ce5ed1db29.tar.bz2
Initialize Iev Api Client
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock2
-rw-r--r--config/environments/development.rb3
-rw-r--r--config/environments/production.rb3
-rw-r--r--config/environments/test.rb3
-rw-r--r--lib/iev_api.rb23
-rw-r--r--lib/iev_api/client.rb103
-rw-r--r--lib/iev_api/configuration.rb58
-rw-r--r--lib/iev_api/middleware/raise_response_error.rb13
-rw-r--r--lib/iev_api/middleware/raise_server_error.rb18
-rw-r--r--spec/lib/iev_api/client_spec.rb261
11 files changed, 488 insertions, 0 deletions
diff --git a/Gemfile b/Gemfile
index ddc15b7d8..849c167f0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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