diff --git a/Gemfile b/Gemfile index 01bb5f3a..22cd778d 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ gem 'travis-support', github: 'travis-ci/travis-support' gem 'travis-core', github: 'travis-ci/travis-core', branch: 'sf-more-services' gem 'hubble', github: 'roidrage/hubble' gem 'yard-sinatra', github: 'rkh/yard-sinatra' +gem 'rack-contrib', github: 'rack/rack-contrib' gem 'gh', github: 'rkh/gh' gem 'bunny' diff --git a/Gemfile.lock b/Gemfile.lock index c7f16036..26a10c9a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,13 @@ GIT specs: micro_migrations (0.0.1) +GIT + remote: git://github.com/rack/rack-contrib.git + revision: b7e7c38fd02c3b5da91aa57af78b3f571c6ebcd0 + specs: + rack-contrib (1.1.0) + rack (>= 0.9.1) + GIT remote: git://github.com/rkh/gh.git revision: affde20a4fecb1023f2e7031734b9386a76d22c2 @@ -25,15 +32,15 @@ GIT GIT remote: git://github.com/roidrage/hubble.git - revision: 8972b940a4f927927d2a4bdb250b3c98c04692a6 + revision: f5e6301ac24eabeebaf8f4485d71cdcf93b2f3f8 specs: hubble (0.1.2) faraday - json (~> 1.6.5) + json (~> 1.6) GIT remote: git://github.com/travis-ci/travis-core.git - revision: ea7a1678a0388e586ac4778a9b6ee56a11dfb0aa + revision: 6c974f0c0ad529748e95222c3145a0f085311ad1 branch: sf-more-services specs: travis-core (0.0.1) @@ -121,15 +128,15 @@ GEM activesupport faraday (0.8.4) multipart-post (~> 1.1) - foreman (0.59.0) + foreman (0.60.0) thor (>= 0.13.6) hashr (0.0.22) hike (1.2.1) hitimes (1.1.1) i18n (0.6.1) journey (1.0.4) - json (1.6.7) - listen (0.5.1) + json (1.7.5) + listen (0.5.2) mail (2.4.4) i18n (>= 0.4.0) mime-types (~> 1.16) @@ -140,7 +147,7 @@ GEM avl_tree (~> 1.1.2) hitimes (~> 1.1) mime-types (1.19) - mocha (0.12.4) + mocha (0.12.5) metaclass (~> 0.0.1) multi_json (1.3.6) multipart-post (1.1.5) @@ -162,13 +169,11 @@ GEM rack (1.4.1) rack-cache (1.2) rack (>= 0.4) - rack-contrib (1.1.0) - rack (>= 0.9.1) rack-protection (1.2.0) rack rack-ssl (1.3.2) rack - rack-test (0.6.1) + rack-test (0.6.2) rack (>= 1.0) railties (3.2.8) actionpack (= 3.2.8) @@ -212,7 +217,7 @@ GEM hike (~> 1.2) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - thin (1.4.1) + thin (1.5.0) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) @@ -235,6 +240,7 @@ DEPENDENCIES hubble! micro_migrations! mocha (~> 0.12) + rack-contrib! rake (~> 0.9.2) rerun rspec (~> 2.11) diff --git a/config/newrelic.yml b/config/newrelic.yml new file mode 100644 index 00000000..d1ab9cd0 --- /dev/null +++ b/config/newrelic.yml @@ -0,0 +1,39 @@ +--- +staging: + error_collector: + capture_source: true + enabled: true + ignore_errors: ActionController::RoutingError + apdex_t: 0.5 + ssl: false + monitor_mode: true + license_key: <%= ENV["NEW_RELIC_LICENSE_KEY"] %> + developer_mode: false + app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> + transaction_tracer: + record_sql: obfuscated + enabled: true + stack_trace_threshold: 0.5 + transaction_threshold: apdex_f + capture_params: false + log_level: info + +production: + error_collector: + capture_source: true + enabled: true + ignore_errors: ActionController::RoutingError + apdex_t: 0.5 + ssl: false + monitor_mode: true + license_key: <%= ENV["NEW_RELIC_LICENSE_KEY"] %> + developer_mode: false + app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> + transaction_tracer: + record_sql: obfuscated + enabled: true + stack_trace_threshold: 0.5 + transaction_threshold: apdex_f + capture_params: false + log_level: info + diff --git a/lib/travis/api/app.rb b/lib/travis/api/app.rb index 7a06db03..200c1d6c 100644 --- a/lib/travis/api/app.rb +++ b/lib/travis/api/app.rb @@ -10,6 +10,9 @@ require 'rack/contrib' require 'active_record' require 'redis' require 'gh' +require 'hubble' +require 'hubble/middleware' +require 'newrelic_rpm' # Rack class implementing the HTTP API. # Instances respond to #call. @@ -51,8 +54,10 @@ class Travis::Api::App def initialize @app = Rack::Builder.app do + use Hubble::Rescuer, env: Travis.env, codename: ENV['CODENAME'] if Endpoint.production? && ENV['HUBBLE_ENDPOINT'] use Rack::Protection::PathTraversal use Rack::SSL if Endpoint.production? + use Rack::PostBodyContentTypeParser use Rack::JSONP use ActiveRecord::ConnectionAdapters::ConnectionManagement @@ -80,6 +85,7 @@ class Travis::Api::App end def self.setup_travis + Travis::Amqp.config = Travis.config.amqp Travis::Database.connect Travis::Services.constants.each do |name| diff --git a/lib/travis/api/app/endpoint/accounts.rb b/lib/travis/api/app/endpoint/accounts.rb new file mode 100644 index 00000000..40ea1dad --- /dev/null +++ b/lib/travis/api/app/endpoint/accounts.rb @@ -0,0 +1,11 @@ +require 'travis/api/app' + +class Travis::Api::App + class Endpoint + class Accounts < Endpoint + get '/', scope: :private do + body service(:account).find_all, type: :accounts + end + end + end +end diff --git a/lib/travis/api/app/endpoint/authorization.rb b/lib/travis/api/app/endpoint/authorization.rb index f9be8f6a..4a8c633b 100644 --- a/lib/travis/api/app/endpoint/authorization.rb +++ b/lib/travis/api/app/endpoint/authorization.rb @@ -115,7 +115,7 @@ class Travis::Api::App get '/post_message' do handshake do |user, token, target_origin| halt 403, invalid_target(target_origin) unless target_ok? target_origin - rendered_user = Travis::Api.data(service(:user, user).find_one, type: :user, version: :v2) + rendered_user = Travis::Api.data(user, version: :v2) post_message(token: token, user: rendered_user, target_origin: target_origin) end end @@ -126,13 +126,18 @@ class Travis::Api::App private + def oauth_endpoint + proxy = Travis.config.oauth2.proxy + proxy ? File.join(proxy, request.fullpath) : url + end + def handshake config = Travis.config.oauth2 endpoint = Addressable::URI.parse(config.authorization_server) values = { client_id: config.client_id, scope: config.scope, - redirect_uri: url + redirect_uri: oauth_endpoint } if params[:code] and state_ok?(params[:state]) @@ -180,8 +185,8 @@ class Travis::Api::App def user_for_github_token(token) data = GH.with(token: token.to_s) { GH['user'] } scopes = parse_scopes data.headers['x-oauth-scopes'] - user = User.find_by_github_id(data['id']) - user ||= User.create! user_info(data, github_oauth_token: token) + user = ::User.find_by_github_id(data['id']) + user ||= ::User.create! user_info(data, github_oauth_token: token) halt 403, 'not a Travis user' if user.nil? halt 403, 'insufficient access' unless acceptable? scopes diff --git a/lib/travis/api/app/endpoint/hooks.rb b/lib/travis/api/app/endpoint/hooks.rb index fd590db5..e2ea4c78 100644 --- a/lib/travis/api/app/endpoint/hooks.rb +++ b/lib/travis/api/app/endpoint/hooks.rb @@ -5,14 +5,13 @@ class Travis::Api::App # TODO: Add documentation. class Hooks < Endpoint # TODO: Add implementation and documentation. - # TODO scope: :private - get('/') do + get('/', scope: :private) do body service(:hooks).find_all(params), type: :hooks end # TODO: Add implementation and documentation. - put('/:id', scope: :admin) do - body service(:hooks).update(params) + put('/:id', scope: :private) do + body service(:hooks).update(params[:hook]) end end end diff --git a/lib/travis/api/app/endpoint/profile.rb b/lib/travis/api/app/endpoint/profile.rb deleted file mode 100644 index e5b62f3e..00000000 --- a/lib/travis/api/app/endpoint/profile.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'travis/api/app' - -class Travis::Api::App - class Endpoint - class Profile < Endpoint - # Gives information about the currently logged in user. - # - # Example: - # - # { - # "user": { - # "email": "svenfuchs@artweb-design.de", - # "gravatar_id": "402602a60e500e85f2f5dc1ff3648ecb", - # "is_syncing": false, - # "locale": "de", - # "login": "svenfuchs", - # "name": "Sven Fuchs", - # "synced_at": "2012-08-14T22:11:21Z" - # } - # } - get '/', scope: :private do - body service(:user).find_one, type: :user - end - - put '/', scope: :private do - raise NotImplementedError - update_locale if valid_locale? - 'ok' - end - - # TODO: Add implementation and documentation. - post '/sync', scope: :private do - sync_user(current_user) - 204 - end - - private - - def sync_user(user) - unless user.is_syncing? - publisher = Travis::Amqp::Publisher.new('sync.user') - publisher.publish({ user_id: user.id }, type: 'sync') - user.update_column(:is_syncing, true) - end - end - - def locale - params[:user][:locale] - end - - def valid_locale? - I18n.available_locales.include?(locale.to_sym) # ??? - end - - def update_locale - current_user.update_attributes!(:locale => locale.to_s) - session[:locale] = locale # ??? - end - end - end -end diff --git a/lib/travis/api/app/endpoint/users.rb b/lib/travis/api/app/endpoint/users.rb new file mode 100644 index 00000000..60399ca1 --- /dev/null +++ b/lib/travis/api/app/endpoint/users.rb @@ -0,0 +1,43 @@ +require 'travis/api/app' + +class Travis::Api::App + class Endpoint + class Users < Endpoint + # Gives information about the currently logged in user. + # + # Example: + # + # { + # "user": { + # "name": "Sven Fuchs", + # "login": "svenfuchs", + # "email": "svenfuchs@artweb-design.de", + # "gravatar_id": "402602a60e500e85f2f5dc1ff3648ecb", + # "locale": "de", + # "is_syncing": false, + # "synced_at": "2012-08-14T22:11:21Z" + # } + # } + get '/:id?', scope: :private do + body current_user + end + + put '/:id?', scope: :private do + service(:user).update_locale(locale) + 204 + end + + # TODO: Add implementation and documentation. + post '/sync', scope: :private do + service(:user).sync + 204 + end + + private + + def locale + params[:user][:locale] + end + end + end +end diff --git a/lib/travis/api/app/responder.rb b/lib/travis/api/app/responder.rb index db20f7ef..36e3a5c4 100644 --- a/lib/travis/api/app/responder.rb +++ b/lib/travis/api/app/responder.rb @@ -20,6 +20,8 @@ class Travis::Api::App # # Logging is set up by custom middleware disable :protection, :logging, :setup + enable :raise_errors + # disable :dump_errors register :subclass_tracker helpers :json_renderer end diff --git a/spec/endpoint/accounts_spec.rb b/spec/endpoint/accounts_spec.rb new file mode 100644 index 00000000..c3eafdab --- /dev/null +++ b/spec/endpoint/accounts_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Travis::Api::App::Endpoint::Accounts do + include Travis::Testing::Stubs + let(:access_token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) } + + before do + User.stubs(:find_by_github_id).returns(user) + User.stubs(:find).returns(user) + user.stubs(:repositories).returns(stub(administratable: stub(select: [repository]))) + user.stubs(:attributes).returns(:id => user.id, :login => user.login, :name => user.name) + end + + it 'includes accounts' do + get('/accounts', access_token: access_token.to_s).should be_ok + parsed_body['accounts'].should == [{ + 'id' => user.id, + 'login' => user.login, + 'name' => user.name, + 'type' => 'user', + 'reposCount' => nil + }] + end +end diff --git a/spec/endpoint/profile_spec.rb b/spec/endpoint/users_spec.rb similarity index 63% rename from spec/endpoint/profile_spec.rb rename to spec/endpoint/users_spec.rb index 612a8e9c..6c8c7110 100644 --- a/spec/endpoint/profile_spec.rb +++ b/spec/endpoint/users_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Travis::Api::App::Endpoint::Profile do +describe Travis::Api::App::Endpoint::Users do include Travis::Testing::Stubs let(:access_token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) } @@ -11,11 +11,11 @@ describe Travis::Api::App::Endpoint::Profile do end it 'needs to be authenticated' do - get('/profile').should_not be_ok + get('/users').should_not be_ok end it 'replies with the current user' do - get('/profile', access_token: access_token.to_s).should be_ok + get('/users', access_token: access_token.to_s).should be_ok parsed_body['user'].should == { 'id' => user.id, 'login' => user.login, @@ -27,15 +27,4 @@ describe Travis::Api::App::Endpoint::Profile do 'synced_at' => user.synced_at.strftime('%Y-%m-%dT%H:%M:%SZ') } end - - it 'includes accounts' do - get('/profile', access_token: access_token.to_s).should be_ok - parsed_body['accounts'].should == [{ - 'id' => user.id, - 'login' => user.login, - 'name' => user.name, - 'type' => 'user', - 'reposCount' => nil - }] - end end