rewrite all the things
This commit is contained in:
parent
f67e72602b
commit
7baf61054c
7
.travis.yml
Normal file
7
.travis.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- 1.9.3
|
||||
before_script:
|
||||
- 'RAILS_ENV=test rake db:create db:schema:load --trace'
|
||||
notifications:
|
||||
irc: "irc.freenode.org#travis"
|
35
Gemfile
35
Gemfile
|
@ -1,23 +1,34 @@
|
|||
source :rubygems
|
||||
|
||||
ruby '1.9.3' rescue nil
|
||||
|
||||
gem 'travis-core', github: 'travis-ci/travis-core'
|
||||
gem 'travis-support', github: 'travis-ci/travis-support'
|
||||
gem 'travis-core', github: 'travis-ci/travis-core'
|
||||
gem 'hubble', github: 'roidrage/hubble'
|
||||
|
||||
gem 'backports', '~> 2.5'
|
||||
gem 'pg', '~> 0.13.2'
|
||||
gem 'newrelic_rpm', '~> 3.3.0'
|
||||
gem 'thin', '~> 1.4'
|
||||
gem 'sinatra'
|
||||
gem 'sinatra-contrib'
|
||||
# gem 'sinatra-cross_origin', github: 'britg/sinatra-cross_origin'
|
||||
gem 'rack-contrib', github: 'rack/rack-contrib', require: 'rack/contrib'
|
||||
gem 'redcarpet'
|
||||
|
||||
gem 'rake', '~> 0.9.2.2'
|
||||
gem 'versionist', '~> 0.2.0'
|
||||
group :production do
|
||||
gem 'rack-ssl'
|
||||
end
|
||||
|
||||
# db
|
||||
gem 'pg', '~> 0.13.2'
|
||||
gem 'newrelic_rpm', '~> 3.3.0'
|
||||
gem 'hubble', git: 'git://github.com/roidrage/hubble'
|
||||
group :test do
|
||||
gem 'rspec', '~> 2.11'
|
||||
gem 'factory_girl', '~> 2.4.0'
|
||||
end
|
||||
|
||||
# heroku
|
||||
gem 'unicorn', '~> 4.1.1'
|
||||
group :development do
|
||||
gem 'yard-sinatra', github: 'rkh/yard-sinatra'
|
||||
gem 'foreman'
|
||||
gem 'rerun'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem 'rake', '~> 0.9.2'
|
||||
gem 'micro_migrations', git: 'git://gist.github.com/2087829.git'
|
||||
end
|
||||
|
|
86
Gemfile.lock
86
Gemfile.lock
|
@ -1,12 +1,18 @@
|
|||
GIT
|
||||
remote: git://github.com/rack/rack-contrib.git
|
||||
revision: b7e7c38fd02c3b5da91aa57af78b3f571c6ebcd0
|
||||
remote: git://gist.github.com/2087829.git
|
||||
revision: c766c06b0bdbda3bd96c3f4e376249cafcbbfaaa
|
||||
specs:
|
||||
rack-contrib (1.1.0)
|
||||
rack (>= 0.9.1)
|
||||
micro_migrations (0.0.1)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/roidrage/hubble
|
||||
remote: git://github.com/rkh/yard-sinatra.git
|
||||
revision: 3b1064eef407d2d288a5b96d258178a1e67b3b80
|
||||
specs:
|
||||
yard-sinatra (1.0.0)
|
||||
yard (~> 0.7)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/roidrage/hubble.git
|
||||
revision: 5220415d5542a2868d54f7be9f35fc1d66126b8e
|
||||
specs:
|
||||
hubble (0.1.2)
|
||||
|
@ -63,25 +69,29 @@ GEM
|
|||
activesupport (= 3.2.6)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activeresource (3.2.6)
|
||||
activemodel (= 3.2.6)
|
||||
activesupport (= 3.2.6)
|
||||
activesupport (3.2.6)
|
||||
i18n (~> 0.6)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.2.8)
|
||||
addressable (2.3.1)
|
||||
arel (3.0.2)
|
||||
atomic (1.0.1)
|
||||
avl_tree (1.1.3)
|
||||
backports (2.6.1)
|
||||
backports (2.6.2)
|
||||
builder (3.0.0)
|
||||
daemons (1.1.8)
|
||||
data_migrations (0.0.1)
|
||||
activerecord
|
||||
rake
|
||||
diff-lcs (1.1.3)
|
||||
erubis (2.7.0)
|
||||
eventmachine (0.12.10)
|
||||
factory_girl (2.4.2)
|
||||
activesupport
|
||||
faraday (0.8.1)
|
||||
multipart-post (~> 1.1)
|
||||
ffi (1.1.0)
|
||||
foreman (0.53.0)
|
||||
thor (>= 0.13.6)
|
||||
gh (0.7.3)
|
||||
addressable
|
||||
backports (~> 2.3)
|
||||
|
@ -95,7 +105,10 @@ GEM
|
|||
i18n (0.6.0)
|
||||
journey (1.0.4)
|
||||
json (1.6.7)
|
||||
kgio (2.7.4)
|
||||
listen (0.4.7)
|
||||
rb-fchange (~> 0.0.5)
|
||||
rb-fsevent (~> 0.9.1)
|
||||
rb-inotify (~> 0.8.8)
|
||||
mail (2.4.4)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
|
@ -131,14 +144,6 @@ GEM
|
|||
rack
|
||||
rack-test (0.6.1)
|
||||
rack (>= 1.0)
|
||||
rails (3.2.6)
|
||||
actionmailer (= 3.2.6)
|
||||
actionpack (= 3.2.6)
|
||||
activerecord (= 3.2.6)
|
||||
activeresource (= 3.2.6)
|
||||
activesupport (= 3.2.6)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.6)
|
||||
railties (3.2.6)
|
||||
actionpack (= 3.2.6)
|
||||
activesupport (= 3.2.6)
|
||||
|
@ -146,12 +151,27 @@ GEM
|
|||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
raindrops (0.10.0)
|
||||
rake (0.9.2.2)
|
||||
rb-fchange (0.0.5)
|
||||
ffi
|
||||
rb-fsevent (0.9.1)
|
||||
rb-inotify (0.8.8)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12)
|
||||
json (~> 1.4)
|
||||
redcarpet (2.1.1)
|
||||
redis (3.0.1)
|
||||
rerun (0.7.1)
|
||||
listen
|
||||
rollout (1.1.0)
|
||||
rspec (2.11.0)
|
||||
rspec-core (~> 2.11.0)
|
||||
rspec-expectations (~> 2.11.0)
|
||||
rspec-mocks (~> 2.11.0)
|
||||
rspec-core (2.11.1)
|
||||
rspec-expectations (2.11.2)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.11.1)
|
||||
signature (0.1.3)
|
||||
simple_states (0.1.1)
|
||||
activesupport
|
||||
|
@ -171,33 +191,37 @@ GEM
|
|||
hike (~> 1.2)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
thin (1.4.1)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 0.12.6)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
treetop (1.4.10)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.33)
|
||||
unicorn (4.1.1)
|
||||
kgio (~> 2.4)
|
||||
rack
|
||||
raindrops (~> 0.6)
|
||||
versionist (0.2.3)
|
||||
rails (~> 3.0)
|
||||
yard (~> 0.7)
|
||||
yard (0.8.2.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
backports (~> 2.5)
|
||||
factory_girl (~> 2.4.0)
|
||||
foreman
|
||||
hubble!
|
||||
micro_migrations!
|
||||
newrelic_rpm (~> 3.3.0)
|
||||
pg (~> 0.13.2)
|
||||
rack-contrib!
|
||||
rake (~> 0.9.2.2)
|
||||
rack-ssl
|
||||
rake (~> 0.9.2)
|
||||
redcarpet
|
||||
rerun
|
||||
rspec (~> 2.11)
|
||||
sinatra
|
||||
sinatra-contrib
|
||||
thin (~> 1.4)
|
||||
travis-core!
|
||||
travis-support!
|
||||
unicorn (~> 4.1.1)
|
||||
versionist (~> 0.2.0)
|
||||
yard-sinatra!
|
||||
|
|
60
README.md
Normal file
60
README.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# The public Travis API
|
||||
|
||||
This is the app (eventually) running on https://api.travis-ci.org/
|
||||
|
||||
## Installation
|
||||
|
||||
Setup:
|
||||
|
||||
$ bundle install
|
||||
|
||||
Run tests:
|
||||
|
||||
$ RAILS_ENV=test rake db:create db:schema:load
|
||||
$ rake spec
|
||||
|
||||
Run the server:
|
||||
|
||||
$ rake db:create db:schema:load
|
||||
$ foreman start
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
### API documentation
|
||||
|
||||
We use source code comments to add documentation. If the server is running, you
|
||||
can browse an HTML documenation at [`/docs`](http://localhost:5000/docs).
|
||||
|
||||
### Project architecture
|
||||
|
||||
lib
|
||||
`-- travis
|
||||
`-- api
|
||||
`-- app
|
||||
|-- endpoint # API endpoints
|
||||
|-- extensions # Sinatra extensions
|
||||
|-- helpers # Sinatra helpers
|
||||
`-- middleware # Rack middleware
|
||||
|
||||
Classes inheriting from `Endpoint` or `Middleware`, they will automatically be
|
||||
set up properly.
|
||||
|
||||
Each endpoint class gets mapped to a prefix, which defaults to the snake-case
|
||||
class name (i.e. `Travis::Api::App::Profile` will map to `/profile`).
|
||||
It can be overridden by setting `:prefix`:
|
||||
|
||||
``` ruby
|
||||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class MyRouts < Endpoint
|
||||
set :prefix, '/awesome'
|
||||
end
|
||||
end
|
||||
```
|
13
Rakefile
Normal file
13
Rakefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
require 'bundler/setup'
|
||||
ENV['SCHEMA'] = "#{Gem.loaded_specs['travis-core'].full_gem_path}/db/schema.rb"
|
||||
|
||||
require 'micro_migrations'
|
||||
require 'travis'
|
||||
|
||||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new
|
||||
task default: :spec
|
||||
rescue LoadError
|
||||
warn "could not load rspec"
|
||||
end
|
|
@ -1,5 +1,2 @@
|
|||
$:.unshift 'lib'
|
||||
|
||||
require 'travis/api/app'
|
||||
|
||||
run Travis::Api::App
|
||||
run Travis::Api::App.new
|
||||
|
|
|
@ -1,105 +1,64 @@
|
|||
require 'sinatra'
|
||||
require 'sinatra/reloader'
|
||||
require 'travis/api/cors'
|
||||
require 'json'
|
||||
# Make sure we set that before everything
|
||||
ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] || ENV['ENV']
|
||||
ENV['RAILS_ENV'] = ENV['RACK_ENV']
|
||||
|
||||
require 'travis'
|
||||
require 'backports'
|
||||
require 'rack'
|
||||
require 'rack/protection'
|
||||
require 'active_record'
|
||||
|
||||
Travis::Database.connect
|
||||
# Rack class implementing the HTTP API.
|
||||
# Instances respond to #call.
|
||||
#
|
||||
# run Travis::Api::App.new
|
||||
#
|
||||
# Requires TLS in production.
|
||||
class Travis::Api::App
|
||||
autoload :Responder, 'travis/api/app/responder'
|
||||
autoload :Endpoint, 'travis/api/app/endpoint'
|
||||
autoload :Extensions, 'travis/api/app/extensions'
|
||||
autoload :Helpers, 'travis/api/app/helpers'
|
||||
autoload :Middleware, 'travis/api/app/middleware'
|
||||
|
||||
module Travis
|
||||
module Api
|
||||
class App < Sinatra::Application
|
||||
autoload :Service, 'travis/api/app/service'
|
||||
disable :protection
|
||||
Rack.autoload :SSL, 'rack/ssl'
|
||||
|
||||
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
||||
use Travis::API::CORS
|
||||
# Used to track if setup already ran.
|
||||
def self.setup?
|
||||
@setup ||= false
|
||||
end
|
||||
|
||||
error ActiveRecord::RecordNotFound do
|
||||
not_found
|
||||
end
|
||||
# Loads all endpoints and middleware and hooks them up properly.
|
||||
# Calls #setup on any middleware and endpoint.
|
||||
#
|
||||
# This method is not threadsafe, but called when loading
|
||||
# the environment, so no biggy.
|
||||
def self.setup(options = {})
|
||||
return if setup?
|
||||
Travis::Database.connect
|
||||
|
||||
configure :development do
|
||||
register Sinatra::Reloader
|
||||
set :show_exceptions, :after_handler
|
||||
end
|
||||
Responder.set(options) if options
|
||||
Backports.require_relative_dir 'app/middleware'
|
||||
Backports.require_relative_dir 'app/endpoint'
|
||||
Responder.subclasses.each(&:setup)
|
||||
|
||||
before do
|
||||
content_type :json
|
||||
end
|
||||
@setup = true
|
||||
end
|
||||
|
||||
get '/repositories' do
|
||||
respond_with Service::Repos.new(params).collection
|
||||
end
|
||||
attr_accessor :app
|
||||
|
||||
get '/repositories/:id' do
|
||||
respond_with Service::Repos.new(params).item
|
||||
# raise if not params[:format] == 'png'
|
||||
end
|
||||
|
||||
get '/builds' do
|
||||
respond_with Service::Builds.new(params).collection
|
||||
end
|
||||
|
||||
get '/builds/:id' do
|
||||
respond_with Service::Builds.new(params).item
|
||||
end
|
||||
|
||||
get '/branches' do
|
||||
# respond_with Service::Repos.new(params).item, :type => :branches
|
||||
{ branches: [] }.to_json
|
||||
end
|
||||
|
||||
get '/jobs' do
|
||||
respond_with Service::Jobs.new(params).collection, :type => 'jobs'
|
||||
end
|
||||
|
||||
get '/jobs/:id' do
|
||||
respond_with Service::Jobs.new(params).item, :type => 'job'
|
||||
end
|
||||
|
||||
get '/artifacts/:id' do
|
||||
respond_with Service::Artifacts.new(params).item
|
||||
end
|
||||
|
||||
get '/workers' do
|
||||
respond_with Service::Workers.new(params).collection
|
||||
end
|
||||
|
||||
get '/hooks' do
|
||||
authenticate_user!
|
||||
respond_with Service::Hooks.new(user, params).item
|
||||
# rescue_from ActiveRecord::RecordInvalid, :with => Proc.new { head :not_acceptable }
|
||||
end
|
||||
|
||||
put '/hooks/:id' do
|
||||
authenticate_user!
|
||||
respond_with Service::Hooks.new(user, params).update
|
||||
end
|
||||
|
||||
get '/profile' do
|
||||
authenticate_user!
|
||||
respond_with Service::Profile.new(user).update
|
||||
end
|
||||
|
||||
post '/profile/sync' do
|
||||
authenticate_user!
|
||||
respond_with Service::Profile.new(user).sync
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate_user!
|
||||
@user = User.find_by_login('svenfuchs')
|
||||
end
|
||||
|
||||
def respond_with(resource, options = {})
|
||||
Travis::Api.data(resource, { :params => params, :version => version }.merge(options)).to_json
|
||||
end
|
||||
|
||||
def version
|
||||
'v2'
|
||||
end
|
||||
def initialize
|
||||
Travis::Api::App.setup
|
||||
@app = Rack::Builder.app do
|
||||
use Rack::Protection::PathTraversal
|
||||
use Rack::SSL if Endpoint.production?
|
||||
Middleware.subclasses.each { |m| use(m) }
|
||||
Endpoint.subclasses.each { |e| map(e.prefix) { run(e) } }
|
||||
end
|
||||
end
|
||||
|
||||
# Rack protocol
|
||||
def call(env)
|
||||
app.call(env)
|
||||
end
|
||||
end
|
||||
|
|
16
lib/travis/api/app/endpoint.rb
Normal file
16
lib/travis/api/app/endpoint.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
# Superclass for HTTP endpoints. Takes care of prefixing.
|
||||
class Endpoint < Responder
|
||||
set(:prefix) { "/" << name[/[^:]+$/].underscore }
|
||||
before { content_type :json }
|
||||
|
||||
error(ActiveRecord::RecordNotFound, Sinatra::NotFound) { not_found }
|
||||
not_found { content_type =~ /json/ ? { 'file' => 'not found' } : 'file not found' }
|
||||
|
||||
# TODO: Dummy method.
|
||||
def self.scope(name)
|
||||
end
|
||||
end
|
||||
end
|
11
lib/travis/api/app/endpoint/artifacts.rb
Normal file
11
lib/travis/api/app/endpoint/artifacts.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Artifacts < Endpoint
|
||||
# TODO: Add documentation.
|
||||
get('/:id') { |id| Artifact.find(id) }
|
||||
end
|
||||
end
|
||||
end
|
11
lib/travis/api/app/endpoint/branches.rb
Normal file
11
lib/travis/api/app/endpoint/branches.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Branches < Endpoint
|
||||
# TODO: Add better implementation and documentation.
|
||||
get('/') {{ branches: [] }}
|
||||
end
|
||||
end
|
||||
end
|
28
lib/travis/api/app/endpoint/builds.rb
Normal file
28
lib/travis/api/app/endpoint/builds.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Builds < Endpoint
|
||||
# TODO: Add documentation.
|
||||
get '/' do
|
||||
scope = repository.builds.by_event_type(params[:event_type] || 'push')
|
||||
scope = params[:after] ? scope.older_than(params[:after]) : scope.recent
|
||||
scope
|
||||
end
|
||||
|
||||
# TODO: Add documentation.
|
||||
get '/:id' do
|
||||
one = params[:repository_id] ? repository.builds : Build
|
||||
one.includes(:commit, :matrix => [:commit, :log]).find(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository
|
||||
pass if params.empty?
|
||||
Repository.find_by(params) || not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
185
lib/travis/api/app/endpoint/documentation.rb
Normal file
185
lib/travis/api/app/endpoint/documentation.rb
Normal file
|
@ -0,0 +1,185 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# Generated API documentation.
|
||||
class Documentation < Endpoint
|
||||
set prefix: '/docs'
|
||||
enable :inline_templates
|
||||
|
||||
# HTML view for [/endpoints](#/endpoints/).
|
||||
get '/' do
|
||||
content_type :html
|
||||
endpoints = Endpoints.endpoints
|
||||
erb :index, {}, :endpoints => endpoints.keys.sort.map { |k| endpoints[k] }
|
||||
end
|
||||
|
||||
helpers do
|
||||
def icon_for(verb)
|
||||
# GET, POST, PATCH, PUT, DELETE"
|
||||
case verb
|
||||
when 'GET' then 'file'
|
||||
when 'POST' then 'edit'
|
||||
when 'PATCH' then 'wrench'
|
||||
when 'PUT' then 'share'
|
||||
when 'DELETE' then 'trash'
|
||||
else 'question-sign'
|
||||
end
|
||||
end
|
||||
|
||||
def slug_for(route)
|
||||
return route['uri'] if route['verb'] == 'GET'
|
||||
route['verb'] + " " + route['uri']
|
||||
end
|
||||
|
||||
def docs_for(entry)
|
||||
markdown(entry['doc']).
|
||||
gsub('<pre', '<pre class="prettyprint linenums lang-js pre-scrollable"').
|
||||
gsub(/<\/?code>/, '').
|
||||
gsub(/TODO:?/, '<span class="label label-warning">TODO</span>')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
__END__
|
||||
|
||||
@@ index
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Travis API documentation</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- we might wanna change this -->
|
||||
<link href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" rel="stylesheet" />
|
||||
<link href="http://twitter.github.com/bootstrap/assets/css/bootstrap-responsive.css" rel="stylesheet" />
|
||||
<link href="http://twitter.github.com/bootstrap/assets/js/google-code-prettify/prettify.css" rel="stylesheet" />
|
||||
<script src="http://twitter.github.com/bootstrap/assets/js/jquery.js"></script>
|
||||
<script src="http://twitter.github.com/bootstrap/assets/js/google-code-prettify/prettify.js"></script>
|
||||
<script src="http://twitter.github.com/bootstrap/assets/js/bootstrap.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
header {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-top: 36px;
|
||||
}
|
||||
header h1 {
|
||||
text-shadow: 2px 2px 5px #000;
|
||||
margin-bottom: 9px;
|
||||
font-size: 81px;
|
||||
font-weight: bold;
|
||||
letter-spacing: -1px;
|
||||
line-height: 1;
|
||||
}
|
||||
header p {
|
||||
margin-bottom: 18px;
|
||||
font-weight: 300;
|
||||
font-size: 18px;
|
||||
}
|
||||
.page-header {
|
||||
margin-top: 90px;
|
||||
}
|
||||
.route {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
.page-header a {
|
||||
color: black;
|
||||
}
|
||||
.nav-list a {
|
||||
color: inherit !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="prettyPrint()">
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<header class="span12">
|
||||
<h1>The Travis API</h1>
|
||||
<p>All the routes, just waiting for you to build something awesome.</p>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<aside class="span3">
|
||||
<div class="page-header">
|
||||
<h1>Navigation</h1>
|
||||
</div>
|
||||
<div class="well" style="padding: 8px 0;">
|
||||
<ul class="nav nav-list">
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<li class="nav-header"><a href="#<%= endpoint['name'] %>"><%= endpoint['name'] %></a></li>
|
||||
<% endpoint['routes'].each do |route| %>
|
||||
<li>
|
||||
<a href="#<%= slug_for(route) %>">
|
||||
<i class="icon-<%= icon_for route['verb'] %>"></i>
|
||||
<tt><%= route['uri'] %></tt>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<li class="divider"></li>
|
||||
<li class="nav-header">
|
||||
External Links
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://travis-ci.org">
|
||||
<i class="icon-globe"></i>
|
||||
Travis CI
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-api">
|
||||
<i class="icon-cog"></i>
|
||||
Source Code
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-api/issues">
|
||||
<i class="icon-list-alt"></i>
|
||||
API issues
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-ember">
|
||||
<i class="icon-play-circle"></i>
|
||||
Example Client
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="span9">
|
||||
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<div id="<%= endpoint['name'] %>">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<a href="#<%= endpoint['name'] %>"><%= endpoint['name'] %></a>
|
||||
</h1>
|
||||
</div>
|
||||
<%= docs_for endpoint %>
|
||||
<% endpoint['routes'].each do |route| %>
|
||||
<div class="route" id="<%= slug_for(route) %>">
|
||||
<pre><h3><%= route['verb'] %> <%= route['uri'] %></h3></pre>
|
||||
<p>
|
||||
<h5>Required autorization scope: <span class="label"><%= route['scope'] %></span></h5>
|
||||
</p>
|
||||
<%= docs_for route %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
68
lib/travis/api/app/endpoint/endpoints.rb
Normal file
68
lib/travis/api/app/endpoint/endpoints.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'travis/api/app'
|
||||
require 'yard/sinatra'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# Documents all available API endpoints for the currently deployed version.
|
||||
# Text is actually parsed from the source code upon server start.
|
||||
class Endpoints < Endpoint
|
||||
set :endpoints, {}
|
||||
|
||||
set :setup do
|
||||
endpoint_files = Dir.glob(File.expand_path("../*.rb", __FILE__))
|
||||
YARD::Registry.load(endpoint_files, true)
|
||||
|
||||
YARD::Sinatra.routes.each do |route|
|
||||
namespace = route.namespace
|
||||
controller = namespace.to_s.constantize
|
||||
route_info = {
|
||||
'uri' => (controller.prefix + route.http_path[1..-2]).gsub('//', '/'),
|
||||
'verb' => route.http_verb,
|
||||
'doc' => route.docstring,
|
||||
'scope' => /scope\W+(\w+)/.match(route.source).try(:[], 1) || 'public'
|
||||
}
|
||||
endpoint = endpoints[controller.prefix] ||= {
|
||||
'name' => namespace.name,
|
||||
'doc' => namespace.docstring,
|
||||
'prefix' => controller.prefix,
|
||||
'routes' => []
|
||||
}
|
||||
endpoint['routes'] << route_info
|
||||
end
|
||||
|
||||
set :json, endpoints.keys.sort.map { |k| endpoints[k] }.to_json
|
||||
endpoints.each_value { |r| r[:json] = r.to_json if r.respond_to? :to_hash }
|
||||
end
|
||||
|
||||
# Lists all available API endpoints by URI prefix.
|
||||
#
|
||||
# Values in the resulting array correspond to return values of
|
||||
# [`/endpoints/:prefix`](#/endpoints/:prefix).
|
||||
get '/' do
|
||||
settings.json
|
||||
end
|
||||
|
||||
# Infos about a specific controller.
|
||||
#
|
||||
# Example response:
|
||||
#
|
||||
# {
|
||||
# name: "Endpoints",
|
||||
# doc: "Documents all available API endpoints...",
|
||||
# prefix: "/endpoints",
|
||||
# routes: [
|
||||
# {
|
||||
# uri: "/endpoints/:prefix",
|
||||
# verb: "GET",
|
||||
# doc: "Infos about...",
|
||||
# scope: "public"
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
get '/:prefix' do |prefix|
|
||||
pass unless endpoint = settings.endpoints["/#{prefix}"]
|
||||
endpoint[:json]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
15
lib/travis/api/app/endpoint/home.rb
Normal file
15
lib/travis/api/app/endpoint/home.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
class Home < Endpoint
|
||||
set(:prefix, '/')
|
||||
|
||||
# Landing point. Redirects web browsers to [API documentation](#/docs/).
|
||||
get '/' do
|
||||
redirect to('/docs/') if request.preferred_type('application/json', 'text/html') == 'text/html'
|
||||
{ 'hello' => 'world' }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
14
lib/travis/api/app/endpoint/hooks.rb
Normal file
14
lib/travis/api/app/endpoint/hooks.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Hooks < Endpoint
|
||||
# TODO: Add implementation and documentation.
|
||||
get('/', scope: :private) { raise NotImplementedError }
|
||||
|
||||
# TODO: Add implementation and documentation.
|
||||
put('/:id', scope: :admin) { raise NotImplementedError }
|
||||
end
|
||||
end
|
||||
end
|
14
lib/travis/api/app/endpoint/jobs.rb
Normal file
14
lib/travis/api/app/endpoint/jobs.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Jobs < Endpoint
|
||||
# TODO: Add implementation and documentation.
|
||||
get('/') { raise NotImplementedError }
|
||||
|
||||
# TODO: Add implementation and documentation.
|
||||
get('/:id') { raise NotImplementedError }
|
||||
end
|
||||
end
|
||||
end
|
14
lib/travis/api/app/endpoint/profile.rb
Normal file
14
lib/travis/api/app/endpoint/profile.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Profile < Endpoint
|
||||
# TODO: Add implementation and documentation.
|
||||
get('/', scope: :private) { raise NotImplementedError }
|
||||
|
||||
# TODO: Add implementation and documentation.
|
||||
post('/sync', scope: :private) { raise NotImplementedError }
|
||||
end
|
||||
end
|
||||
end
|
20
lib/travis/api/app/endpoint/repositories.rb
Normal file
20
lib/travis/api/app/endpoint/repositories.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Repositories < Endpoint
|
||||
# TODO: Add documentation.
|
||||
get '/' do
|
||||
scope = Repository.timeline.recent
|
||||
scope = scope.by_owner_name(params[:owner_name]) if params[:owner_name]
|
||||
scope = scope.by_slug(params[:slug]) if params[:slug]
|
||||
scope = scope.search(params[:search]) if params[:search].present?
|
||||
scope
|
||||
end
|
||||
|
||||
# TODO: Add documentation.
|
||||
get('/:id') { Repository.find_by(params) }
|
||||
end
|
||||
end
|
||||
end
|
11
lib/travis/api/app/endpoint/workers.rb
Normal file
11
lib/travis/api/app/endpoint/workers.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# TODO: Add documentation.
|
||||
class Workers < Endpoint
|
||||
# TODO: Add implementation and documentation.
|
||||
get('/') { Worker.order(:host, :name) }
|
||||
end
|
||||
end
|
||||
end
|
8
lib/travis/api/app/extensions.rb
Normal file
8
lib/travis/api/app/extensions.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
# Namespace for Sinatra extensions.
|
||||
module Extensions
|
||||
Backports.require_relative_dir 'extensions'
|
||||
end
|
||||
end
|
28
lib/travis/api/app/extensions/smart_constants.rb
Normal file
28
lib/travis/api/app/extensions/smart_constants.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
module Extensions
|
||||
# Allows writing
|
||||
#
|
||||
# helpers :some_helper
|
||||
#
|
||||
# Instead of
|
||||
#
|
||||
# helpers Travis::Api::App::Helpers::SomeHelper
|
||||
module SmartConstants
|
||||
def helpers(*list, &block)
|
||||
super(*resolve_constants(list, Helpers), &block)
|
||||
end
|
||||
|
||||
def register(*list, &block)
|
||||
super(*resolve_constants(list, Extensions), &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resolve_constants(list, namespace)
|
||||
list.map { |e| Symbol === e ? namespace.const_get(e.to_s.camelize) : e }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
lib/travis/api/app/extensions/subclass_tracker.rb
Normal file
25
lib/travis/api/app/extensions/subclass_tracker.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
module Extensions
|
||||
# Keeps track of subclasses. Used for endpoint and middleware detection.
|
||||
# This will prevent garbage collection of subclasses.
|
||||
module SubclassTracker
|
||||
def direct_subclasses
|
||||
@direct_subclasses ||= []
|
||||
end
|
||||
|
||||
# List of "leaf" subclasses (ie subclasses without subclasses).
|
||||
def subclasses
|
||||
return [self] if direct_subclasses.empty?
|
||||
direct_subclasses.map(&:subclasses).flatten.uniq
|
||||
end
|
||||
|
||||
def inherited(subclass)
|
||||
super
|
||||
subclass.set app_file: caller_files.first
|
||||
direct_subclasses << subclass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
8
lib/travis/api/app/helpers.rb
Normal file
8
lib/travis/api/app/helpers.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
# Namespace for helpers.
|
||||
module Helpers
|
||||
Backports.require_relative_dir 'helpers'
|
||||
end
|
||||
end
|
32
lib/travis/api/app/helpers/json_renderer.rb
Normal file
32
lib/travis/api/app/helpers/json_renderer.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
module Helpers
|
||||
# Allows routes to return either hashes or anything Travis::API.data can
|
||||
# convert (in addition to the return values supported by Sinatra, of
|
||||
# course). These values will be encoded in JSON.
|
||||
module JsonRenderer
|
||||
def respond_with(resource, options = {})
|
||||
halt render_json(resource, options)
|
||||
end
|
||||
|
||||
def body(value = nil, &block)
|
||||
value = render_json(value) if value
|
||||
super(value, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_json(resource, options = {})
|
||||
options[:version] ||= 'v2' # TODO: Content negotiation
|
||||
options[:params] ||= params
|
||||
|
||||
builder = Travis::Api.builder(resource, options)
|
||||
resource = builder.new(resource, options[:params]).data.to_json if builder
|
||||
resource = resource.to_json if resource.is_a? Hash
|
||||
|
||||
resource
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
lib/travis/api/app/middleware.rb
Normal file
7
lib/travis/api/app/middleware.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
# Superclass for all middleware.
|
||||
class Middleware < Responder
|
||||
end
|
||||
end
|
9
lib/travis/api/app/middleware/access_token.rb
Normal file
9
lib/travis/api/app/middleware/access_token.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Middleware
|
||||
# Checks access tokens and sets appropriate scopes.
|
||||
class AccessToken < Middleware
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +1,12 @@
|
|||
require 'sinatra/base'
|
||||
|
||||
module Travis
|
||||
module API
|
||||
class CORS < Sinatra::Base
|
||||
disable :protection
|
||||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Middleware
|
||||
# Implements Cross-Origin Resource Sharing. Supported by all major browsers.
|
||||
# See http://www.w3.org/TR/cors/
|
||||
#
|
||||
# TODO: Be smarter about origin.
|
||||
class Cors < Middleware
|
||||
before do
|
||||
headers['Access-Control-Allow-Origin'] = "*"
|
||||
headers['Access-Control-Allow-Credentials'] = "true"
|
||||
|
@ -13,7 +15,7 @@ module Travis
|
|||
|
||||
options // do
|
||||
headers['Access-Control-Allow-Methods'] = "GET, POST, PATCH, PUT, DELETE"
|
||||
headers['Access-Control-Allow-Headers'] = "Content-Type, Authorization"
|
||||
headers['Access-Control-Allow-Headers'] = "Content-Type, Authorization, Accept"
|
||||
end
|
||||
end
|
||||
end
|
15
lib/travis/api/app/middleware/logging.rb
Normal file
15
lib/travis/api/app/middleware/logging.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Middleware
|
||||
# Makes sure we use Travis.logger everywhere.
|
||||
class Logging < Middleware
|
||||
set(:setup) { ActiveRecord::Base.logger = Travis.logger }
|
||||
|
||||
before do
|
||||
env['rack.logger'] = Travis.logger
|
||||
env['rack.errors'] = Travis.logger.instance_variable_get(:@logdev).dev rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
lib/travis/api/app/responder.rb
Normal file
27
lib/travis/api/app/responder.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'travis/api/app'
|
||||
require 'sinatra/base'
|
||||
|
||||
class Travis::Api::App
|
||||
# Superclass for any endpoint and middleware.
|
||||
# Pulls in relevant helpers and extensions.
|
||||
class Responder < Sinatra::Base
|
||||
register Extensions::SmartConstants
|
||||
|
||||
configure do
|
||||
# We pull in certain protection middleware in App.
|
||||
# Being token based makes us invulnerable to common
|
||||
# CSRF attack.
|
||||
#
|
||||
# Logging is set up by custom middleware
|
||||
disable :protection, :logging, :setup
|
||||
register :subclass_tracker
|
||||
helpers :json_renderer
|
||||
end
|
||||
|
||||
configure :development do
|
||||
# We want error pages in development, but only
|
||||
# when we don't have an error handler specified
|
||||
set :show_exceptions, :after_handler
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
autoload :Artifacts, 'travis/api/app/service/artifacts'
|
||||
autoload :Builds, 'travis/api/app/service/builds'
|
||||
autoload :Hooks, 'travis/api/app/service/hooks'
|
||||
autoload :Jobs, 'travis/api/app/service/jobs'
|
||||
autoload :Repos, 'travis/api/app/service/repos'
|
||||
autoload :Workers, 'travis/api/app/service/workers'
|
||||
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Artifacts < Service
|
||||
def item
|
||||
Artifact.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Builds < Service
|
||||
def collection
|
||||
scope = repository.builds.by_event_type(params[:event_type] || 'push')
|
||||
scope = params[:after] ? scope.older_than(params[:after]) : scope.recent
|
||||
scope
|
||||
end
|
||||
|
||||
def item
|
||||
one = params[:repository_id] ? repository.builds : Build
|
||||
one.includes(:commit, :matrix => [:commit, :log]).find(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository
|
||||
Repository.find_by(params) || not_found # TODO needs to return nil if params are empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Hooks < Service
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user, params)
|
||||
super(params)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def collection
|
||||
user.service_hooks
|
||||
end
|
||||
|
||||
def update
|
||||
hook.set(payload[:active], user)
|
||||
hook
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hook
|
||||
repository.service_hook
|
||||
end
|
||||
|
||||
def repository
|
||||
Repository.find_or_create_by_owner_name_and_name(params[:owner_name], params[:name])
|
||||
end
|
||||
|
||||
def payload
|
||||
params[:service_hook] || {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Jobs < Service
|
||||
def collection
|
||||
if params[:ids]
|
||||
Job::Test.where(:id => params[:ids]).includes(:commit, :log)
|
||||
else
|
||||
jobs = Job::Test.queued.includes(:commit, :log)
|
||||
jobs = jobs.where(:queue => params[:queue]) if params[:queue]
|
||||
jobs
|
||||
end
|
||||
end
|
||||
|
||||
def item
|
||||
Job::Test.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Profile < Service
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def item
|
||||
user
|
||||
end
|
||||
|
||||
def sync
|
||||
unless user.is_syncing?
|
||||
publisher.publish({ user_id: user.id }, type: 'sync')
|
||||
user.update_attribute(:is_syncing, true)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def publisher
|
||||
Travis::Amqp::Publisher.new('sync.user')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,27 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Repos
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def collection
|
||||
scope = Repository.timeline.recent
|
||||
scope = scope.by_owner_name(params[:owner_name]) if params[:owner_name]
|
||||
scope = scope.by_slug(params[:slug]) if params[:slug]
|
||||
scope = scope.search(params[:search]) if params[:search].present?
|
||||
scope
|
||||
end
|
||||
|
||||
def item
|
||||
Repository.find_by(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
module Travis
|
||||
module Api
|
||||
class App
|
||||
class Service
|
||||
class Workers < Service
|
||||
def collection
|
||||
Worker.order(:host, :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
8
script/server
Executable file
8
script/server
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
cd "$(dirname "$0")/.."
|
||||
[ $PORT ] || PORT=5000
|
||||
[ $RACK_ENV ] || RACK_ENV=development
|
||||
|
||||
cmd="ruby -I lib -S bundle exec ruby -I lib -S thin start -p $PORT -e $RACK_ENV --threaded"
|
||||
[[ $RACK_ENV == "development" ]] && exec rerun "$cmd -a 127.0.0.1"
|
||||
exec $cmd
|
10
spec/app_spec.rb
Normal file
10
spec/app_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App do
|
||||
describe :setup? do
|
||||
it 'indicates if #setup has been called' do
|
||||
Travis::Api::App.setup
|
||||
Travis::Api::App.should be_setup
|
||||
end
|
||||
end
|
||||
end
|
15
spec/default_spec.rb
Normal file
15
spec/default_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Home do
|
||||
describe 'GET /' do
|
||||
it 'replies with a json response by default' do
|
||||
get('/')["Content-Type"].should include("json")
|
||||
end
|
||||
|
||||
it 'redirects HTML requests to /docs' do
|
||||
get '/', {}, 'HTTP_ACCEPT' => 'text/html'
|
||||
status.should == 302
|
||||
headers['Location'].should end_with('/docs/')
|
||||
end
|
||||
end
|
||||
end
|
12
spec/endpoint/artifacts_spec.rb
Normal file
12
spec/endpoint/artifacts_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Artifacts do
|
||||
let(:artifact) { Factory(:log) }
|
||||
let(:id) { artifact.id }
|
||||
|
||||
describe 'GET /artifacts/:id' do
|
||||
it 'loads the artifact' do
|
||||
get("/artifacts/#{id}").should be_ok
|
||||
end
|
||||
end
|
||||
end
|
5
spec/endpoint/branches_spec.rb
Normal file
5
spec/endpoint/branches_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Branches do
|
||||
it 'has to be implemented'
|
||||
end
|
5
spec/endpoint/builds_spec.rb
Normal file
5
spec/endpoint/builds_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Builds do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/documentation_spec.rb
Normal file
5
spec/endpoint/documentation_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Documentation do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/endpoints_spec.rb
Normal file
5
spec/endpoint/endpoints_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Endpoints do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/hooks_spec.rb
Normal file
5
spec/endpoint/hooks_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Hooks do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/jobs_spec.rb
Normal file
5
spec/endpoint/jobs_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Jobs do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/profile_spec.rb
Normal file
5
spec/endpoint/profile_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Profile do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/repositories_spec.rb
Normal file
5
spec/endpoint/repositories_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Repositories do
|
||||
it 'has to be tested'
|
||||
end
|
5
spec/endpoint/workers_spec.rb
Normal file
5
spec/endpoint/workers_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint::Workers do
|
||||
it 'has to be tested'
|
||||
end
|
18
spec/endpoint_spec.rb
Normal file
18
spec/endpoint_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Endpoint do
|
||||
class MyEndpoint < Travis::Api::App::Endpoint
|
||||
set :prefix, '/my_endpoint'
|
||||
get('/') { 'ok' }
|
||||
end
|
||||
|
||||
it 'sets up endpoints automatically under given prefix' do
|
||||
get('/my_endpoint/').should be_ok
|
||||
body.should == "ok"
|
||||
end
|
||||
|
||||
it 'does not require a trailing slash' do
|
||||
get('/my_endpoint').should be_ok
|
||||
body.should == "ok"
|
||||
end
|
||||
end
|
22
spec/extensions/smart_constants_spec.rb
Normal file
22
spec/extensions/smart_constants_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Extensions::SmartConstants do
|
||||
let(:some_app) do
|
||||
Sinatra.new { register Travis::Api::App::Extensions::SmartConstants }
|
||||
end
|
||||
|
||||
describe :helpers do
|
||||
it 'works' do # :)
|
||||
some_app.helpers :json_renderer
|
||||
some_app.ancestors.should include(Travis::Api::App::Helpers::JsonRenderer)
|
||||
end
|
||||
end
|
||||
|
||||
describe :register do
|
||||
it 'works' do # :)
|
||||
some_app.register :subclass_tracker
|
||||
some_app.should be_a(Travis::Api::App::Extensions::SubclassTracker)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
31
spec/extensions/subclass_tracker_spec.rb
Normal file
31
spec/extensions/subclass_tracker_spec.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Extensions::SubclassTracker do
|
||||
let!(:root) { Sinatra.new { register Travis::Api::App::Extensions::SubclassTracker } }
|
||||
let!(:left) { Class.new(root) }
|
||||
let!(:right) { Class.new(root) }
|
||||
let!(:sub1) { Class.new(right) }
|
||||
let!(:sub2) { Class.new(right) }
|
||||
|
||||
it 'tracks direct subclasses' do
|
||||
classes = root.direct_subclasses
|
||||
classes.size.should == 2
|
||||
classes.should include(left)
|
||||
classes.should include(right)
|
||||
end
|
||||
|
||||
it 'tracks leaf subclasses' do
|
||||
classes = root.subclasses
|
||||
classes.size.should == 3
|
||||
classes.should include(left)
|
||||
classes.should include(sub1)
|
||||
classes.should include(sub2)
|
||||
end
|
||||
|
||||
it 'tracks subclasses of subclasses properly' do
|
||||
classes = right.subclasses
|
||||
classes.size.should == 2
|
||||
classes.should include(sub1)
|
||||
classes.should include(sub2)
|
||||
end
|
||||
end
|
16
spec/helpers/json_renderer_spec.rb
Normal file
16
spec/helpers/json_renderer_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'spec_helper'
|
||||
require 'json'
|
||||
|
||||
describe Travis::Api::App::Helpers::JsonRenderer do
|
||||
before do
|
||||
mock_app do
|
||||
helpers Travis::Api::App::Helpers::JsonRenderer
|
||||
get('/') { {'foo' => 'bar'} }
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders body as json' do
|
||||
get('/').should be_ok
|
||||
JSON.load(body).should == {'foo' => 'bar'}
|
||||
end
|
||||
end
|
16
spec/middleware/access_token_spec.rb
Normal file
16
spec/middleware/access_token_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Middleware::AccessToken do
|
||||
before do
|
||||
mock_app do
|
||||
use Travis::Api::App::Middleware::AccessToken
|
||||
get('/check_cors') { 'ok' }
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets associated scope properly'
|
||||
it 'lets through requests without a token'
|
||||
it 'reject requests with an invalide token'
|
||||
it 'rejects expired tokens'
|
||||
it 'checks that the token corresponds to Origin'
|
||||
end
|
50
spec/middleware/cors_spec.rb
Normal file
50
spec/middleware/cors_spec.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Middleware::Cors do
|
||||
before do
|
||||
mock_app do
|
||||
use Travis::Api::App::Middleware::Cors
|
||||
get('/check_cors') { 'ok' }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'normal request' do
|
||||
before { get('/check_cors').should be_ok }
|
||||
|
||||
it 'sets Access-Control-Allow-Origin' do
|
||||
headers['Access-Control-Allow-Origin'].should == "*"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Allow-Credentials' do
|
||||
headers['Access-Control-Allow-Credentials'].should == "true"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Expose-Headers' do
|
||||
headers['Access-Control-Expose-Headers'].should == "Content-Type"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'OPTIONS requests' do
|
||||
before { options('/').should be_ok }
|
||||
|
||||
it 'sets Access-Control-Allow-Origin' do
|
||||
headers['Access-Control-Allow-Origin'].should == "*"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Allow-Credentials' do
|
||||
headers['Access-Control-Allow-Credentials'].should == "true"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Expose-Headers' do
|
||||
headers['Access-Control-Expose-Headers'].should == "Content-Type"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Allow-Methods' do
|
||||
headers['Access-Control-Allow-Methods'].should == "GET, POST, PATCH, PUT, DELETE"
|
||||
end
|
||||
|
||||
it 'sets Access-Control-Allow-Headers' do
|
||||
headers['Access-Control-Allow-Headers'].should == "Content-Type, Authorization, Accept"
|
||||
end
|
||||
end
|
||||
end
|
19
spec/middleware/logging_spec.rb
Normal file
19
spec/middleware/logging_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Middleware::Logging do
|
||||
it 'configures ActiveRecord' do
|
||||
ActiveRecord::Base.logger.should == Travis.logger
|
||||
end
|
||||
|
||||
it 'sets the logger' do
|
||||
mock_app do
|
||||
use Travis::Api::App::Middleware::Logging
|
||||
get '/check_logger' do
|
||||
logger.should == Travis.logger
|
||||
'ok'
|
||||
end
|
||||
end
|
||||
|
||||
get('/check_logger').should be_ok
|
||||
end
|
||||
end
|
12
spec/middleware_spec.rb
Normal file
12
spec/middleware_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::Middleware do
|
||||
class MyMiddleware < Travis::Api::App::Middleware
|
||||
get('/my_middleware') { 'ok' }
|
||||
end
|
||||
|
||||
it 'sets up middleware automatically' do
|
||||
get('/my_middleware').should be_ok
|
||||
body.should == "ok"
|
||||
end
|
||||
end
|
17
spec/spec_helper.rb
Normal file
17
spec/spec_helper.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = ENV['ENV'] = 'test'
|
||||
|
||||
require 'rspec'
|
||||
require 'travis/api/app'
|
||||
require 'sinatra/test_helpers'
|
||||
require 'logger'
|
||||
|
||||
Travis.logger = Logger.new(StringIO.new)
|
||||
Travis::Api::App.setup
|
||||
|
||||
Backports.require_relative_dir 'support'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.expect_with :rspec, :stdlib
|
||||
config.include Sinatra::TestHelpers
|
||||
config.before(:each) { set_app Travis::Api::App.new }
|
||||
end
|
99
spec/support/factories.rb
Normal file
99
spec/support/factories.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
require 'factory_girl'
|
||||
|
||||
FactoryGirl.define do
|
||||
factory :build do
|
||||
repository { Repository.first || Factory(:repository) }
|
||||
association :request
|
||||
association :commit
|
||||
end
|
||||
|
||||
factory :commit do
|
||||
repository { Repository.first || Factory(:repository) }
|
||||
commit '62aae5f70ceee39123ef'
|
||||
branch 'master'
|
||||
message 'the commit message'
|
||||
committed_at '2011-11-11T11:11:11Z'
|
||||
committer_name 'Sven Fuchs'
|
||||
committer_email 'svenfuchs@artweb-design.de'
|
||||
author_name 'Sven Fuchs'
|
||||
author_email 'svenfuchs@artweb-design.de'
|
||||
compare_url 'https://github.com/svenfuchs/minimal/compare/master...develop'
|
||||
end
|
||||
|
||||
factory :test, :class => 'Job::Test' do
|
||||
repository { Repository.first || Factory(:repository) }
|
||||
commit { Factory(:commit) }
|
||||
source { Factory(:build) }
|
||||
log { Factory(:log) }
|
||||
queue "ruby"
|
||||
end
|
||||
|
||||
factory :log, :class => 'Artifact::Log' do
|
||||
content '$ bundle install --pa'
|
||||
end
|
||||
|
||||
factory :request do
|
||||
repository { Repository.first || Factory(:repository) }
|
||||
association :commit
|
||||
token 'the-token'
|
||||
end
|
||||
|
||||
factory :repository do
|
||||
name 'minimal'
|
||||
owner_name 'svenfuchs'
|
||||
owner_email 'svenfuchs@artweb-design.de'
|
||||
url { |r| "http://github.com/#{r.owner_name}/#{r.name}" }
|
||||
last_duration 60
|
||||
created_at { |r| Time.utc(2011, 01, 30, 5, 25) }
|
||||
updated_at { |r| r.created_at + 5.minutes }
|
||||
end
|
||||
|
||||
factory :minimal, :parent => :repository do
|
||||
end
|
||||
|
||||
factory :enginex, :class => Repository do
|
||||
name 'enginex'
|
||||
owner_name 'josevalim'
|
||||
last_duration 30
|
||||
end
|
||||
|
||||
factory :running_build, :parent => :build do
|
||||
repository { Factory(:repository, :name => 'running_build') }
|
||||
state 'started'
|
||||
end
|
||||
|
||||
factory :successful_build, :parent => :build do
|
||||
repository { Factory(:repository, :name => 'successful_build', :last_build_result => 0) }
|
||||
result 0
|
||||
state 'finished'
|
||||
started_at { Time.now.utc }
|
||||
finished_at { Time.now.utc }
|
||||
end
|
||||
|
||||
factory :broken_build, :parent => :build do
|
||||
repository { Factory(:repository, :name => 'broken_build', :last_build_result => 1) }
|
||||
result 1
|
||||
state 'finished'
|
||||
started_at { Time.now.utc }
|
||||
finished_at { Time.now.utc }
|
||||
end
|
||||
|
||||
factory :user do
|
||||
name 'Sven Fuchs'
|
||||
login 'svenfuchs'
|
||||
email 'sven@fuchs.com'
|
||||
tokens { [Token.new] }
|
||||
end
|
||||
|
||||
factory :worker do
|
||||
name 'worker-1'
|
||||
host 'ruby-1.workers.travis-ci.org'
|
||||
state :working
|
||||
last_seen_at { Time.now.utc }
|
||||
end
|
||||
|
||||
factory :ssl_key do
|
||||
private_key "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDGed1uxl9szL0PVE/B6v9PDso+xRHs9e9YDB8Dm+QYFDyddud1\nn1134ZY39Dxg6zNhXDGKYilHP4E9boIuvgfSADN12eD1clogX46M4oBGgUAhtr5Q\nvGLn9TEW4IbeI+nDshMJLTLethCmB6Hwm5Ld9QnRVT6U/AztOTv9eJ/xKQIDAQAB\nAoGABQ3zcq/AnF+2bN6DzXdzmwrQYbrZEwTMXJyqaYgdzfMt/ACcMmWllrj6/1/L\n7dfvjgowBMstK/BVFUBsNk6GmmoCDHFAU+BgeyyqUxyeb63+0dIDwVYx9LHTL4dr\n9a8cVyeefqc3mqB13B9NUlS40Ij4kuK6EOGP3DZwC1FQVwECQQDtBQFqgRuNdfbV\naGIcXnuMnD4BGrnFHm0IBdLYsK4ULL85gFbhEew6DTYGYlGqX1dXbXYue8F18D8i\nzqL6HOBhAkEA1l6zvLdC2t3J9UnwpkwU0jSPX4BpHH7IkrCoGRggjwtbSxJFcCKB\nRrbPFDNAwchsa2/ldXSBrFg6Y7GlwF3lyQJAaJk+6LuVZzZZ+hAYzCA+Me15x479\n0Kn+v/2h8RL3n9ungD7NGIKKV4wg/WxCUgfFScX608S1udCObFP4xJwdwQJBALtl\nwEQqBGSmXCV0xM3rVoxH7En1TG3fm2E400pUoCnMKLugtlkHoPF7X91tzJ9aoQTu\npa2e8rkBy9FY++gFbZkCQAJ46lGEXZJqcACvLX0t/+RrvmqWMxCydLFG50kOnD8b\nVNILVyUn1lYasTs4aMYr6BRtVZoCxqV5/+rkMhb1eOM=\n-----END RSA PRIVATE KEY-----\n"
|
||||
public_key "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAMZ53W7GX2zMvQ9UT8Hq/08Oyj7FEez171gMHwOb5BgUPJ1253WfXXfh\nljf0PGDrM2FcMYpiKUc/gT1ugi6+B9IAM3XZ4PVyWiBfjozigEaBQCG2vlC8Yuf1\nMRbght4j6cOyEwktMt62EKYHofCbkt31CdFVPpT8DO05O/14n/EpAgMBAAE=\n-----END RSA PUBLIC KEY-----\n"
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'travis-api'
|
||||
s.version = '0.0.1'
|
||||
s.require_path = 'lib'
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user