-
1
require 'travis'
-
1
require 'travis/model'
-
1
require 'travis/support/amqp'
-
1
require 'travis/states_cache'
-
1
require 'rack'
-
1
require 'rack/protection'
-
1
require 'rack/contrib'
-
1
require 'dalli'
-
1
require 'memcachier'
-
1
require 'rack/cache'
-
1
require 'rack/attack'
-
1
require 'active_record'
-
1
require 'redis'
-
1
require 'gh'
-
1
require 'raven'
-
1
require 'sidekiq'
-
1
require 'metriks/reporter/logger'
-
1
require 'metriks/librato_metrics_reporter'
-
1
require 'travis/support/log_subscriber/active_record_metrics'
-
1
require 'fileutils'
-
1
require 'travis/api/v2/http'
-
1
require 'travis/api/v3'
-
-
# Rack class implementing the HTTP API.
-
# Instances respond to #call.
-
#
-
# run Travis::Api::App.new
-
#
-
# Requires TLS in production.
-
1
module Travis::Api
-
1
class App
-
1
autoload :AccessToken, 'travis/api/app/access_token'
-
1
autoload :Base, 'travis/api/app/base'
-
1
autoload :Endpoint, 'travis/api/app/endpoint'
-
1
autoload :Extensions, 'travis/api/app/extensions'
-
1
autoload :Helpers, 'travis/api/app/helpers'
-
1
autoload :Middleware, 'travis/api/app/middleware'
-
1
autoload :Responders, 'travis/api/app/responders'
-
1
autoload :Cors, 'travis/api/app/cors'
-
-
1
Rack.autoload :SSL, 'rack/ssl'
-
-
1
ERROR_RESPONSE = JSON.generate(error: 'Travis encountered an error, sorry :(')
-
-
# Used to track if setup already ran.
-
1
def self.setup?
-
28
@setup ||= false
-
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.
-
1
def self.setup(options = {})
-
28
setup! unless setup?
-
28
Endpoint.set(options) if options
-
28
FileUtils.touch('/tmp/app-initialized') if ENV['DYNO'] # Heroku
-
end
-
-
1
def self.new(options = {})
-
27
setup(options)
-
27
super()
-
end
-
-
1
def self.deploy_sha
-
@deploy_sha ||= File.exist?(deploy_sha_path) ? File.read(deploy_sha_path)[0..7] : 'deploy-sha'
-
end
-
-
1
def self.deploy_sha_path
-
File.expand_path('../../../../.deploy-sha', __FILE__)
-
end
-
-
1
attr_accessor :app
-
-
1
def initialize
-
27
@app = Rack::Builder.app do
-
50
use(Rack::Config) { |env| env['metriks.request.start'] ||= Time.now.utc }
-
-
27
Rack::Utils::HTTP_STATUS_CODES[420] = "Enhance Your Calm"
-
27
use Rack::Attack
-
27
Rack::Attack.blacklist('block client requesting ruby builds') do |req|
-
23
req.ip == "130.15.4.210"
-
end
-
-
27
Rack::Attack.blacklisted_response = lambda do |env|
-
[ 420, {}, ['Enhance Your Calm']]
-
end
-
-
27
use Travis::Api::App::Cors # if Travis.env == 'development' ???
-
27
use Raven::Rack if Endpoint.production? && Travis.config.sentry.dsn
-
27
use Rack::Protection::PathTraversal
-
27
use Rack::SSL if Endpoint.production?
-
27
use ActiveRecord::ConnectionAdapters::ConnectionManagement
-
27
use ActiveRecord::QueryCache
-
-
27
memcache_servers = ENV['MEMCACHIER_SERVERS']
-
27
if Travis::Features.feature_active?(:use_rack_cache) && memcache_servers
-
use Rack::Cache,
-
metastore: "memcached://#{memcache_servers}/meta-#{Travis::Api::App.deploy_sha}",
-
entitystore: "memcached://#{memcache_servers}/body-#{Travis::Api::App.deploy_sha}"
-
end
-
-
27
use Rack::Deflater
-
27
use Rack::PostBodyContentTypeParser
-
27
use Rack::JSONP
-
-
27
use Rack::Config do |env|
-
23
env['SCRIPT_NAME'] = env['HTTP_X_SCRIPT_NAME'].to_s + env['SCRIPT_NAME'].to_s
-
23
env['travis.global_prefix'] = env['SCRIPT_NAME']
-
end
-
-
-
27
use Travis::Api::App::Middleware::Logging
-
27
use Travis::Api::App::Middleware::ScopeCheck
-
27
use Travis::Api::App::Middleware::UserAgentTracker
-
27
use Travis::Api::App::Middleware::Metriks
-
-
# if this is a v3 API request, ignore everything after
-
27
use Travis::API::V3::OptIn
-
-
# rewrite should come after V3 hook
-
27
use Travis::Api::App::Middleware::Rewrite
-
-
27
SettingsEndpoint.subclass :env_vars
-
27
if Travis.config.endpoints.ssh_key
-
27
SingletonSettingsEndpoint.subclass :ssh_key
-
end
-
-
27
Endpoint.subclasses.each do |e|
-
486
next if e == SettingsEndpoint # TODO: add something like abstract? method to check if
-
# class should be registered
-
972
map(e.prefix) { run(e.new) }
-
end
-
end
-
end
-
-
# Rack protocol
-
1
def call(env)
-
23
app.call(env)
-
rescue
-
if Endpoint.production?
-
[500, {'Content-Type' => 'application/json'}, [ERROR_RESPONSE]]
-
else
-
raise
-
end
-
end
-
-
1
private
-
-
1
def self.console?
-
defined? Travis::Console
-
end
-
-
1
def self.setup!
-
1
setup_travis
-
1
load_endpoints
-
1
setup_endpoints
-
1
@setup = true
-
end
-
-
1
def self.setup_travis
-
1
Travis::Async.enabled = true
-
1
Travis::Amqp.config = Travis.config.amqp
-
-
1
setup_database_connections
-
-
1
if Travis.env == 'production' || Travis.env == 'staging'
-
Sidekiq.configure_client do |config|
-
config.redis = Travis.config.redis.merge(size: 1, namespace: Travis.config.sidekiq.namespace)
-
end
-
end
-
-
1
if (Travis.env == 'production' || Travis.env == 'staging') and not console?
-
setup_monitoring
-
end
-
end
-
-
1
def self.setup_database_connections
-
1
Travis::Database.connect
-
-
1
if Travis.config.logs_database
-
Log.establish_connection 'logs_database'
-
Log::Part.establish_connection 'logs_database'
-
end
-
end
-
-
1
def self.setup_monitoring
-
Raven.configure do |config|
-
config.dsn = Travis.config.sentry.dsn
-
end if Travis.config.sentry.dsn
-
-
Travis::LogSubscriber::ActiveRecordMetrics.attach
-
Travis::Notification.setup(instrumentation: false)
-
Travis::Metrics.setup
-
end
-
-
1
def self.load_endpoints
-
6
Dir.glob("#{__dir__}/app/middleware/*.rb").each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] }
-
20
Dir.glob("#{__dir__}/app/endpoint/*.rb").each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] }
-
end
-
-
1
def self.setup_endpoints
-
1
Base.subclasses.each(&:setup)
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'securerandom'
-
-
1
class Travis::Api::App
-
1
class AccessToken
-
1
DEFAULT_SCOPES = [:public, :private]
-
1
attr_reader :token, :scopes, :user_id, :app_id, :expires_in, :extra
-
-
1
def self.create(options = {})
-
4
new(options).tap(&:save)
-
end
-
-
1
def self.for_travis_token(travis_token, options = {})
-
travis_token = Token.find_by_token(travis_token) unless travis_token.respond_to? :user
-
new(scope: :travis_token, app_id: 1, user: travis_token.user).tap(&:save) if travis_token
-
end
-
-
1
def self.find_by_token(token)
-
8
return token if token.is_a? self
-
8
user_id, app_id, *scopes = redis.lrange(key(token), 0, -1)
-
8
extra = decode_json(scopes.pop) if scopes.last && scopes.last =~ /^json:/
-
8
new(token: token, scopes: scopes, user_id: user_id, app_id: app_id, extra: extra) if user_id
-
end
-
-
1
def initialize(options = {})
-
12
raise ArgumentError, 'must supply either user_id or user' unless options.key?(:user) ^ options.key?(:user_id)
-
12
raise ArgumentError, 'must supply app_id' unless options.key?(:app_id)
-
-
12
begin
-
12
@expires_in = Integer(options[:expires_in]) if options[:expires_in]
-
rescue ArgumentError
-
raise ArgumentError, 'expires_in must be of integer type'
-
end
-
-
12
@app_id = Integer(options[:app_id])
-
12
@scopes = Array(options[:scopes] || options[:scope] || DEFAULT_SCOPES).map(&:to_sym)
-
12
@user = options[:user]
-
12
@user_id = Integer(options[:user_id] || @user.id)
-
12
@token = options[:token] || reuse_token || SecureRandom.urlsafe_base64(16)
-
12
@extra = options[:extra]
-
end
-
-
1
def save
-
4
key = key(token)
-
4
redis.del(key)
-
4
data = [user_id, app_id, *scopes]
-
4
data << encode_json(extra) if extra
-
4
redis.rpush(key, data.map(&:to_s))
-
4
redis.set(reuse_key, token)
-
-
4
if expires_in
-
redis.expire(reuse_key, expires_in)
-
redis.expire(key, expires_in)
-
end
-
end
-
-
1
def user
-
4
@user ||= User.find(user_id) if user_id
-
end
-
-
1
def user?
-
!!user
-
end
-
-
1
def to_s
-
4
token
-
end
-
-
1
module Helpers
-
1
private
-
1
def redis
-
24
Thread.current[:redis] ||= ::Redis.connect(url: Travis.config.redis.url)
-
end
-
-
1
def key(token)
-
12
"t:#{token}"
-
end
-
-
1
def encode_json(hash)
-
'json:' + Base64.encode64(hash.to_json)
-
end
-
-
1
def decode_json(json)
-
JSON.parse(Base64.decode64(json.gsub(/^json:/, '')))
-
end
-
end
-
-
1
include Helpers
-
1
extend Helpers
-
-
1
private
-
-
1
def reuse_token
-
4
redis.get(reuse_key) unless expires_in
-
end
-
-
1
def reuse_key
-
@reuse_key ||= begin
-
4
keys = ["r", user_id, app_id]
-
4
keys.append(scopes.map(&:to_s).sort) if scopes != DEFAULT_SCOPES
-
4
keys.join(':')
-
8
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'sinatra/base'
-
1
require 'mustermann'
-
-
1
class Travis::Api::App
-
# Superclass for any endpoint and middleware.
-
# Pulls in relevant helpers and extensions.
-
1
class Base < Sinatra::Base
-
1
register Extensions::SmartConstants
-
1
register Mustermann
-
-
1
error NotImplementedError do
-
content_type :txt
-
status 501
-
"This feature has not yet been implemented. Sorry :(\n\nPull Requests welcome!"
-
end
-
-
1
error JSON::ParserError do
-
status 400
-
"Invalid JSON in request body"
-
end
-
-
# hotfix??
-
1
def route_missing
-
135
@app ? forward : halt(404)
-
end
-
-
1
def call(env)
-
135
super
-
rescue Sinatra::NotFound
-
[404, {'Content-Type' => 'text/plain'}, ['Tell Konstantin to fix this!']]
-
end
-
-
1
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
-
1
disable :protection, :logging, :setup
-
1
enable :raise_errors
-
# disable :dump_errors
-
1
register :subclass_tracker, :expose_pattern
-
1
helpers :respond_with, :mime_types
-
end
-
-
1
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
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
# Implements Cross-Origin Resource Sharing. Supported by all major browsers.
-
# See http://www.w3.org/TR/cors/
-
#
-
# TODO: Be smarter about origin.
-
1
class Cors < Base
-
1
before do
-
23
headers['Access-Control-Allow-Origin'] = "*"
-
23
headers['Access-Control-Allow-Credentials'] = "true"
-
23
headers['Access-Control-Expose-Headers'] = "Content-Type, Cache-Control, Expires, Etag, Last-Modified"
-
end
-
-
1
options // do
-
headers['Access-Control-Allow-Methods'] = "HEAD, GET, POST, PATCH, PUT, DELETE"
-
headers['Access-Control-Allow-Headers'] = "Content-Type, Authorization, Accept, If-None-Match, If-Modified-Since, X-User-Agent"
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'addressable/uri'
-
1
require 'active_record/base'
-
-
1
class Travis::Api::App
-
# Superclass for HTTP endpoints. Takes care of prefixing.
-
1
class Endpoint < Base
-
1
include Travis::Services::Helpers
-
-
471
set(:prefix) { "/" << name[/[^:]+$/].underscore }
-
1
set disable_root_endpoint: false
-
1
register :scoping
-
1
helpers :current_user, :flash, :db_follower
-
-
# TODO hmmm?
-
11
before { flash.clear }
-
11
after { content_type :json unless content_type }
-
-
1
error(ActiveRecord::RecordNotFound, Sinatra::NotFound) { not_found }
-
1
not_found {
-
10
if content_type =~ /json/
-
10
if body && !body.empty?
-
body
-
else
-
10
{ 'file' => 'not found' }
-
end
-
else
-
'file not found'
-
end
-
}
-
-
1
private
-
-
1
def redis
-
Thread.current[:redis] ||= ::Redis.connect(url: Travis.config.redis.url)
-
end
-
-
1
def endpoint(link, query_values = {})
-
link = url(File.join(env['travis.global_prefix'], link), true, false)
-
uri = Addressable::URI.parse(link)
-
query_values = query_values.merge(uri.query_values) if uri.query_values
-
uri.query_values = query_values
-
uri.to_s
-
end
-
-
1
def safe_redirect(url)
-
redirect(endpoint('/redirect', to: url), 301)
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Accounts < Endpoint
-
1
get '/', scope: :private do
-
respond_with service(:find_user_accounts, params), type: :accounts
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'addressable/uri'
-
1
require 'faraday'
-
1
require 'securerandom'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
# You need to get hold of an access token in order to reach any
-
# endpoint requiring authorization.
-
# There are three ways to get hold of such a token: OAuth2, via a GitHub
-
# token you may already have or with Cross-Origin Window Messages.
-
#
-
# ## OAuth2
-
#
-
# API authorization is done via a subset of OAuth2 and is largely compatible
-
# with the [GitHub process](http://developer.github.com/v3/oauth/).
-
# Be aware that Travis CI will in turn use OAuth2 to authenticate (and
-
# authorize) against GitHub.
-
#
-
# This is the recommended way for third-party web apps.
-
# The entry point is [/auth/authorize](#/auth/authorize).
-
#
-
# ## GitHub Token
-
#
-
# If you already have a GitHub token with the same or greater scope than
-
# the tokens used by Travis CI, you can easily exchange it for a access
-
# token. Travis will not store the GitHub token and only use it for a single
-
# request to resolve the associated user and scopes.
-
#
-
# This is the recommended way for GitHub applications that also want Travis
-
# integration.
-
#
-
# The entry point is [/auth/github](#POST /auth/github).
-
#
-
# ## Cross-Origin Window Messages
-
#
-
# This is the recommended way for the official client. We might improve the
-
# authorization flow to support third-party clients in the future, too.
-
#
-
# The entry point is [/auth/post_message](#/auth/post_message).
-
1
class Authorization < Endpoint
-
1
enable :inline_templates
-
1
set prefix: '/auth'
-
-
# Endpoint for retrieving an authorization code, which in turn can be used
-
# to generate an access token.
-
#
-
# NOTE: This endpoint is not yet implemented.
-
#
-
# Parameters:
-
#
-
# * **client_id**: your App's client id (required)
-
# * **redirect_uri**: URL to redirect to
-
# * **scope**: requested access scope
-
# * **state**: should be random string to prevent CSRF attacks
-
1
get '/authorize' do
-
raise NotImplementedError
-
end
-
-
# Endpoint for generating an access token from an authorization code.
-
#
-
# NOTE: This endpoint is not yet implemented.
-
#
-
# Parameters:
-
#
-
# * **client_id**: your App's client id (required)
-
# * **client_secret**: your App's client secret (required)
-
# * **code**: code retrieved from redirect from [/auth/authorize](#/auth/authorize) (required)
-
# * **redirect_uri**: URL to redirect to
-
# * **state**: same value sent to [/auth/authorize](#/auth/authorize)
-
1
post '/access_token' do
-
raise NotImplementedError
-
end
-
-
# Endpoint for generating an access token from a GitHub access token.
-
#
-
# Parameters:
-
#
-
# * **github_token**: GitHub token for checking authorization (required)
-
1
post '/github' do
-
unless params[:github_token]
-
halt 422, { "error" => "Must pass 'github_token' parameter" }
-
end
-
-
{ 'access_token' => github_to_travis(params[:github_token], app_id: 1, drop_token: true) }
-
end
-
-
# Endpoint for making sure user authorized Travis CI to access GitHub.
-
# There are no restrictions on where to redirect to after handshake.
-
# However, no information whatsoever is being sent with the redirect.
-
#
-
# Parameters:
-
#
-
# * **redirect_uri**: URI to redirect to after handshake.
-
1
get '/handshake' do
-
handshake do |user, token, redirect_uri|
-
if target_ok? redirect_uri
-
content_type :html
-
data = { user: user, token: token, uri: redirect_uri }
-
erb(:post_payload, locals: data)
-
else
-
safe_redirect redirect_uri
-
end
-
end
-
end
-
-
# This endpoint is meant to be embedded in an iframe, popup window or
-
# similar. It will perform the handshake and, once done, will send an
-
# access token and user payload to the parent window via postMessage.
-
#
-
# However, the endpoint to send the payload to has to be explicitely
-
# whitelisted in production, as this is endpoint is only meant to be used
-
# with the official Travis CI client at the moment.
-
#
-
# Example usage:
-
#
-
# window.addEventListener("message", function(event) {
-
# console.log("received token: " + event.data.token);
-
# });
-
#
-
# var iframe = $('<iframe />').hide();
-
# iframe.appendTo('body');
-
# iframe.attr('src', "https://api.travis-ci.org/auth/post_message");
-
#
-
# Note that embedding it in an iframe will only work for users that are
-
# logged in at GitHub and already authorized Travis CI. It is therefore
-
# recommended to redirect to [/auth/handshake](#/auth/handshake) if no
-
# token is being received.
-
1
get '/post_message', scope: :public do
-
content_type :html
-
data = { check_third_party_cookies: !Travis.config.auth.disable_third_party_cookies_check }
-
erb(:container, locals: data)
-
end
-
-
1
get '/post_message/iframe', scope: :public do
-
handshake do |user, token, target_origin|
-
halt 403, invalid_target(target_origin) unless target_ok? target_origin
-
post_message(token: token, user: user, target_origin: target_origin)
-
end
-
end
-
-
1
error Faraday::Error::ClientError do
-
halt 401, 'could not resolve github token'
-
end
-
-
1
private
-
-
1
def serialize_user(user)
-
rendered = Travis::Api.data(user, version: :v2)
-
rendered['user'].merge('token' => user.tokens.first.try(:token).to_s)
-
end
-
-
1
def oauth_endpoint
-
proxy = Travis.config.oauth2.proxy
-
proxy ? File.join(proxy, request.fullpath) : url
-
end
-
-
1
def handshake
-
config = Travis.config.oauth2
-
endpoint = Addressable::URI.parse(config.authorization_server)
-
values = {
-
client_id: config.client_id,
-
scope: config.scope,
-
redirect_uri: oauth_endpoint
-
}
-
-
if params[:code]
-
halt 400, 'state mismatch' unless state_ok?(params[:state])
-
endpoint.path = config.access_token_path
-
values[:state] = params[:state]
-
values[:code] = params[:code]
-
values[:client_secret] = config.client_secret
-
github_token = get_token(endpoint.to_s, values)
-
user = user_for_github_token(github_token)
-
token = generate_token(user: user, app_id: 0)
-
payload = params[:state].split(":::", 2)[1]
-
yield serialize_user(user), token, payload
-
else
-
values[:state] = create_state
-
endpoint.path = config.authorize_path
-
endpoint.query_values = values
-
redirect to(endpoint.to_s)
-
end
-
end
-
-
1
def create_state
-
state = SecureRandom.urlsafe_base64(16)
-
redis.sadd('github:states', state)
-
redis.expire('github:states', 1800)
-
payload = params[:origin] || params[:redirect_uri]
-
state << ":::" << payload if payload
-
response.set_cookie('travis.state', state)
-
state
-
end
-
-
1
def state_ok?(state)
-
cookie_state = request.cookies['travis.state']
-
state == cookie_state and redis.srem('github:states', state.to_s.split(":::", 1))
-
end
-
-
1
def github_to_travis(token, options = {})
-
drop_token = options.delete(:drop_token)
-
generate_token options.merge(user: user_for_github_token(token, drop_token))
-
end
-
-
1
class UserManager < Struct.new(:data, :token, :drop_token)
-
1
include User::Renaming
-
-
1
attr_accessor :user
-
-
1
def initialize(*)
-
super
-
-
@user = ::User.find_by_github_id(data['id'])
-
end
-
-
1
def info(attributes = {})
-
info = data.to_hash.slice('name', 'login', 'gravatar_id')
-
info.merge! attributes.stringify_keys
-
if Travis::Features.feature_active?(:education_data_sync) ||
-
(user && Travis::Features.owner_active?(:education_data_sync, user))
-
info['education'] = education
-
end
-
info['github_id'] ||= data['id']
-
info
-
end
-
-
1
def user_exists?
-
user
-
end
-
-
1
def education
-
Travis::Github::Education.new(token.to_s).student?
-
end
-
-
1
def fetch
-
retried ||= false
-
info = drop_token ? self.info : self.info(github_oauth_token: token)
-
-
ActiveRecord::Base.transaction do
-
if user
-
rename_repos_owner(user.login, info['login'])
-
user.update_attributes info
-
else
-
self.user = ::User.create! info
-
end
-
-
nullify_logins(user.github_id, user.login)
-
end
-
-
user
-
rescue ActiveRecord::RecordNotUnique
-
unless retried
-
retried = true
-
retry
-
end
-
end
-
end
-
-
1
def user_for_github_token(token, drop_token = false)
-
data = GH.with(token: token.to_s, client_id: nil) { GH['user'] }
-
scopes = parse_scopes data.headers['x-oauth-scopes']
-
manager = UserManager.new(data, token, drop_token)
-
-
unless acceptable?(scopes, drop_token)
-
# TODO: we should probably only redirect if this is a web
-
# oauth request, are there any other possibilities to
-
# consider?
-
url = Travis.config.oauth2.insufficient_access_redirect_url
-
url += "#existing-user" if manager.user_exists?
-
redirect to(url)
-
end
-
-
user = manager.fetch
-
halt 403, 'not a Travis user' if user.nil?
-
user
-
end
-
-
1
def get_token(endpoint, values)
-
response = Faraday.new(ssl: Travis.config.github.ssl).post(endpoint, values)
-
parameters = Addressable::URI.form_unencode(response.body)
-
token_info = parameters.assoc("access_token")
-
halt 401, 'could not resolve github token' unless token_info
-
token_info.last
-
end
-
-
1
def parse_scopes(data)
-
data.gsub(/\s/,'').split(',') if data
-
end
-
-
1
def generate_token(options)
-
AccessToken.create(options).token
-
end
-
-
1
def acceptable?(scopes, lossy = false)
-
User::Oauth.wanted_scopes.all? do |scope|
-
acceptable_scopes_for(scope, lossy).any? { |s| scopes.include? s }
-
end
-
end
-
-
1
def acceptable_scopes_for(scope, lossy = false)
-
scopes = case scope = scope.to_s
-
when /^(.+):/ then [$1, scope]
-
when 'public_repo' then [scope, 'repo']
-
else [scope]
-
end
-
-
if lossy
-
scopes << 'repo'
-
scopes << 'public_repo' if lossy and scope != 'repo'
-
end
-
-
scopes
-
end
-
-
1
def post_message(payload)
-
content_type :html
-
erb(:post_message, locals: payload)
-
end
-
-
1
def invalid_target(target_origin)
-
content_type :html
-
erb(:invalid_target, {}, target_origin: target_origin)
-
end
-
-
1
def target_ok?(target_origin)
-
return unless uri = Addressable::URI.parse(target_origin)
-
if allowed_https_targets.include?(uri.host)
-
uri.scheme == 'https'
-
elsif uri.host =~ /\A(.+\.)?travis-ci\.(com|org)\Z/
-
uri.scheme == 'https'
-
elsif uri.host =~ /\A(.+\.)?travis-lite\.com\Z/
-
uri.scheme == 'https'
-
elsif uri.host == 'localhost' or uri.host == '127.0.0.1'
-
uri.port > 1023
-
end
-
end
-
-
1
def allowed_https_targets
-
@allowed_https_targets ||= Travis.config.auth.target_origin.to_s.split(',')
-
end
-
end
-
end
-
end
-
-
__END__
-
-
@@ invalid_target
-
<script>
-
console.log('refusing to send a token to <%= target_origin.inspect %>, not whitelisted!');
-
</script>
-
-
@@ common
-
function tellEveryone(msg, win) {
-
if(win == undefined) win = window;
-
win.postMessage(msg, '*');
-
if(win.parent != win) tellEveryone(msg, win.parent);
-
if(win.opener) tellEveryone(msg, win.opener);
-
}
-
-
@@ container
-
<!DOCTYPE html>
-
<html><body><script>
-
// === THE FLOW ===
-
-
// every serious program has a main function
-
function main() {
-
doYouHave(thirdPartyCookies,
-
yesIndeed("third party cookies enabled, creating iframe",
-
doYouHave(iframe(after(5)),
-
yesIndeed("iframe succeeded", done),
-
nopeSorry("iframe taking too long, creating pop-up",
-
doYouHave(popup(after(5)),
-
yesIndeed("pop-up succeeded", done),
-
nopeSorry("pop-up failed, redirecting", redirect))))),
-
nopeSorry("third party cookies disabled, creating pop-up",
-
doYouHave(popup(after(8)),
-
yesIndeed("popup succeeded", done),
-
nopeSorry("popup failed", redirect))))();
-
}
-
-
// === THE LOGIC ===
-
var url = window.location.pathname + '/iframe' + window.location.search;
-
-
function thirdPartyCookies(yes, no) {
-
<%= "return no();" unless check_third_party_cookies %>
-
window.cookiesCheckCallback = function(enabled) { enabled ? yes() : no() };
-
var img = document.createElement('img');
-
img.src = "https://third-party-cookies.herokuapp.com/set";
-
img.onload = function() {
-
var script = document.createElement('script');
-
script.src = "https://third-party-cookies.herokuapp.com/check";
-
window.document.body.appendChild(script);
-
}
-
}
-
-
function iframe(time) {
-
return function(yes, no) {
-
var iframe = document.createElement('iframe');
-
iframe.src = url;
-
timeout(time, yes, no);
-
window.document.body.appendChild(iframe);
-
}
-
}
-
-
function popup(time) {
-
return function(yes, no) {
-
if(popupWindow) {
-
timeout(time, yes, function() {
-
if(popupWindow.closed || popupWindow.innerHeight < 1) {
-
no()
-
} else {
-
try {
-
popupWindow.focus();
-
popupWindow.resizeTo(900, 500);
-
} catch(err) {
-
no()
-
}
-
}
-
});
-
} else {
-
no()
-
}
-
}
-
}
-
-
function done() {
-
if(popupWindow && !popupWindow.closed) popupWindow.close();
-
}
-
-
function redirect() {
-
tellEveryone('redirect');
-
}
-
-
function createPopup() {
-
if(!popupWindow) popupWindow = window.open(url, 'Signing in...', 'height=50,width=50');
-
}
-
-
// === THE PLUMBING ===
-
<%= erb :common %>
-
-
function timeout(time, yes, no) {
-
var timeout = setTimeout(no, time);
-
onSuccess(function() {
-
clearTimeout(timeout);
-
yes()
-
});
-
}
-
-
function onSuccess(callback) {
-
succeeded ? callback() : callbacks.push(callback)
-
}
-
-
function doYouHave(feature, yes, no) {
-
return function() { feature(yes, no) };
-
}
-
-
function yesIndeed(msg, callback) {
-
return function() {
-
if(console && console.log) console.log(msg);
-
return callback();
-
}
-
}
-
-
function after(value) {
-
return value*1000;
-
}
-
-
var nopeSorry = yesIndeed;
-
var timeoutes = [];
-
var callbacks = [];
-
var seconds = 1000;
-
var succeeded = false;
-
var popupWindow;
-
-
window.addEventListener("message", function(event) {
-
if(event.data === "done") {
-
succeeded = true
-
for(var i = 0; i < callbacks.length; i++) {
-
(callbacks[i])();
-
}
-
}
-
});
-
-
// === READY? GO! ===
-
main();
-
</script>
-
</body>
-
</html>
-
-
@@ post_message
-
<script>
-
<%= erb :common %>
-
function uberParent(win) {
-
return win.parent === win ? win : uberParent(win.parent);
-
}
-
-
function sendPayload(win) {
-
var payload = {
-
'user': <%= user.to_json %>,
-
'token': <%= token.inspect %>
-
};
-
uberParent(win).postMessage(payload, <%= target_origin.inspect %>);
-
}
-
-
if(window.parent == window) {
-
sendPayload(window.opener);
-
window.close();
-
} else {
-
tellEveryone('done');
-
sendPayload(window.parent);
-
}
-
</script>
-
-
@@ post_payload
-
<body onload='document.forms[0].submit()'>
-
<form action="<%= uri %>" method='post'>
-
<input type='hidden' name='token' value='<%= token %>'>
-
<input type='hidden' name='user' value="<%= user.to_json.gsub('"', '"') %>">
-
<input type='hidden' name='storage' value='localStorage'>
-
</form>
-
</body>
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Branches < Endpoint
-
1
get '/' do
-
respond_with service(:find_branches, params), type: :branches
-
end
-
-
# get '/:owner_name/:name/branches' do # v1
-
# get '/repos/:owner_name/:name/branches' do # v2
-
# respond_with service(:branches, :find_all, params), type: :branches
-
# end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Broadcasts < Endpoint
-
1
get '/', scope: :private do
-
respond_with service(:find_user_broadcasts, params), type: :broadcasts
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Builds < Endpoint
-
1
get '/' do
-
prefer_follower do
-
name = params[:branches] ? :find_branches : :find_builds
-
params['ids'] = params['ids'].split(',') if params['ids'].respond_to?(:split)
-
respond_with service(name, params)
-
end
-
end
-
-
1
get '/:id' do
-
respond_with service(:find_build, params)
-
end
-
-
1
post '/:id/cancel' do
-
Metriks.meter("api.request.cancel_build").mark
-
-
service = self.service(:cancel_build, params.merge(source: 'api'))
-
if !service.authorized?
-
json = { error: {
-
message: "You don't have access to cancel build(#{params[:id]})"
-
} }
-
-
Metriks.meter("api.request.cancel_build.unauthorized").mark
-
status 403
-
respond_with json
-
elsif !service.can_cancel?
-
json = { error: {
-
message: "The build(#{params[:id]}) can't be canceled",
-
code: 'cant_cancel'
-
} }
-
-
Metriks.meter("api.request.cancel_build.cant_cancel").mark
-
status 422
-
respond_with json
-
else
-
service.run
-
-
Metriks.meter("api.request.cancel_build.success").mark
-
status 204
-
end
-
end
-
-
1
post '/:id/restart' do
-
Metriks.meter("api.request.restart_build").mark
-
respond_with service(:reset_model, build_id: params[:id])
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Documentation < Endpoint
-
1
set prefix: '/docs'
-
-
1
get '/' do
-
redirect "http://docs.travis-ci.com/api"
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'yard/sinatra'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
# Documents all available API endpoints for the currently deployed version.
-
# Text is actually parsed from the source code upon server start.
-
1
class Endpoints < Endpoint
-
1
set :endpoints, {}
-
-
1
set :setup do
-
1
endpoint_files = Dir.glob(File.expand_path("../*.rb", __FILE__))
-
1
YARD::Registry.load(endpoint_files, true)
-
-
1
YARD::Sinatra.routes.each do |route|
-
68
namespace = route.namespace
-
68
controller = namespace.to_s.constantize
-
68
route_info = {
-
68
'uri' => (controller.prefix + route.http_path).gsub('//', '/'),
-
'verb' => route.http_verb,
-
'doc' => route.docstring,
-
'scope' => /scope\W+(\w+)/.match(route.source).try(:[], 1) || controller.default_scope.to_s
-
}
-
68
endpoint = endpoints[controller.prefix] ||= {
-
'name' => namespace.name,
-
'doc' => namespace.docstring,
-
'prefix' => controller.prefix,
-
'routes' => []
-
}
-
68
endpoint['routes'] << route_info
-
end
-
-
18
set :json, endpoints.keys.sort.map { |k| endpoints[k] }.to_json
-
18
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).
-
1
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"
-
# }
-
# ]
-
# }
-
1
get '/:prefix' do |prefix|
-
pass unless endpoint = settings.endpoints["/#{prefix}"]
-
endpoint[:json]
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'travis/api/app/endpoint/setting_endpoint'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class EnvVars < SettingsEndpoint
-
1
define_method(:name) { :env_vars }
-
1
define_routes!
-
-
1
def update
-
data = JSON.parse(request.body.read)[singular_name]
-
previously_public = record.public?
-
record.update(data)
-
-
# if we update from private to public reset value
-
if !previously_public && record.public? && data['value'].nil?
-
record.value = nil
-
end
-
-
if record.valid?
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
else
-
status 422
-
respond_with(record, type: :validation_error, version: :v2)
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Home < Endpoint
-
1
host = Travis.config.client_domain || Travis.config.host
-
1
fail "Travis.config.client_domain is not set" unless host or test?
-
-
1
set :prefix, '/'
-
1
set :client_config,
-
host: host,
-
shorten_host: Travis.config.shorten_host,
-
assets: Travis.config.assets,
-
1
pusher: (Travis.config.pusher_ws || Travis.config.pusher || {}).to_hash.slice(:scheme, :host, :port, :path, :key),
-
github: { api_url: GH.current.api_host.to_s, scopes: Travis.config.oauth2.try(:scope).to_s.split(?,) }
-
-
# Landing point. Redirects web browsers to [API documentation](#/docs/).
-
1
get '/' do
-
pass if settings.disable_root_endpoint?
-
redirect to('/docs/') if request.preferred_type('application/json', 'application/json-home', 'text/html') == 'text/html'
-
{ 'hello' => 'world' }
-
end
-
-
# Simple endpoints that redirects somewhere else, to make sure we don't
-
# send a referrer.
-
#
-
# Parameters:
-
#
-
# * **to**: URI to redirect to after handshake.
-
1
get '/redirect' do
-
halt 400 unless params[:to] =~ %r{^https?://}
-
redirect params[:to]
-
end
-
-
# Provides you with system info:
-
#
-
# {
-
# config: {
-
# host: "travis-ci.org",
-
# shorten_host: "trvs.io",
-
# pusher: { key: "dd3f11c013317df48b50" },
-
# assets: {
-
# host: "localhost:3000",
-
# version: "asset-id",
-
# interval: 15
-
# }
-
# }
-
# }
-
1
get '/config' do
-
{ config: settings.client_config }
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Hooks < Endpoint
-
1
get '/', scope: :private do
-
respond_with service(:find_hooks, params), type: :hooks
-
end
-
-
1
put '/:id?', scope: :private do
-
respond_with service(:update_hook, id: params[:id] || params[:hook][:id], active: params[:hook][:active])
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Jobs < Endpoint
-
1
include Helpers::Accept
-
-
1
get '/' do
-
prefer_follower do
-
respond_with service(:find_jobs, params)
-
end
-
end
-
-
1
get '/:id' do
-
job = service(:find_job, params).run
-
if job && job.repository
-
respond_with job
-
else
-
json = { error: { message: "The job(#{params[:id]}) couldn't be found" } }
-
status 404
-
respond_with json
-
end
-
end
-
-
1
post '/:id/cancel' do
-
Metriks.meter("api.request.cancel_job").mark
-
-
service = self.service(:cancel_job, params.merge(source: 'api'))
-
if !service.authorized?
-
json = { error: {
-
message: "You don't have access to cancel job(#{params[:id]})"
-
} }
-
-
Metriks.meter("api.request.cancel_job.unauthorized").mark
-
status 403
-
respond_with json
-
elsif !service.can_cancel?
-
json = { error: {
-
message: "The job(#{params[:id]}) can't be canceled",
-
code: 'cant_cancel'
-
} }
-
-
Metriks.meter("api.request.cancel_job.cant_cancel").mark
-
status 422
-
respond_with json
-
else
-
service.run
-
-
Metriks.meter("api.request.cancel_job.success").mark
-
status 204
-
end
-
end
-
-
1
post '/:id/restart' do
-
Metriks.meter("api.request.restart_job").mark
-
respond_with service(:reset_model, job_id: params[:id])
-
end
-
-
1
get '/:job_id/log' do
-
resource = service(:find_log, params).run
-
if (!resource || resource.archived?)
-
# the way we use responders makes it hard to validate proper format
-
# automatically here, so we need to check it explicitly
-
if accepts?('text/plain')
-
archived_log_path = archive_url("/jobs/#{params[:job_id]}/log.txt")
-
-
if params[:cors_hax]
-
status 204
-
headers['Access-Control-Expose-Headers'] = 'Location'
-
headers['Location'] = archived_log_path
-
else
-
redirect archived_log_path, 307
-
end
-
else
-
status 406
-
end
-
else
-
respond_with resource
-
end
-
end
-
-
1
patch '/:id/log', scope: :private do |id|
-
begin
-
self.service(:remove_log, params).run
-
rescue Travis::AuthorizationDenied => ade
-
status 401
-
{ error: { message: ade.message } }
-
rescue Travis::JobUnfinished, Travis::LogAlreadyRemoved => e
-
status 409
-
{ error: { message: e.message } }
-
rescue => e
-
status 500
-
{ error: { message: "Unexpected error occurred: #{e.message}" } }
-
end
-
end
-
-
1
get "/:job_id/annotations" do
-
respond_with service(:find_annotations, params)
-
end
-
-
1
post "/:job_id/annotations" do
-
if params[:status] && params[:description]
-
annotation = service(:update_annotation, params).run
-
-
status annotation ? 204 : 401
-
else
-
status 422
-
-
{ "error" => "Must include status and description" }
-
end
-
end
-
-
1
def archive_url(path)
-
"https://s3.amazonaws.com/#{hostname('archive')}#{path}"
-
end
-
-
1
def hostname(name)
-
"#{name}#{'-staging' if Travis.env == 'staging'}.#{Travis.config.host.split('.')[-2, 2].join('.')}"
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'travis/yaml'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Lint < Endpoint
-
1
def lint
-
request.body.rewind
-
content = params[:content] || request.body.read
-
parsed = Travis::Yaml.parse(content)
-
warnings = parsed.nested_warnings.map { |k, m| { key: k, message: m } }
-
{ lint: { warnings: warnings } }.to_json
-
end
-
-
1
post('/', scope: :public) { lint }
-
1
put('/', scope: :public) { lint }
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
# Logs are generated by builds.
-
1
class Logs < Endpoint
-
# Fetches a log by its *id*.
-
1
get '/:id' do |id|
-
respond_with service(:find_log, params)
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Repos < Endpoint
-
1
set :pattern, capture: { id: /\d+/ }
-
-
# Endpoint for getting all repositories.
-
#
-
# You can filter the repositories by adding parameters to the request. For example, you can get all repositories
-
# owned by johndoe by adding `owner_name=johndoe`, or all repositories that johndoe has access to by adding
-
# `member=johndoe`. The parameter names correspond to the keys of the response hash.
-
#
-
# ### Response
-
#
-
# json(:repositories)
-
1
get '/' do
-
prefer_follower do
-
respond_with service(:find_repos, params)
-
end
-
end
-
-
# Gets the repository with the given id.
-
#
-
# ### Response
-
#
-
# json(:repository)
-
1
get '/:id' do
-
prefer_follower do
-
respond_with service(:find_repo, params)
-
end
-
end
-
-
# Retrieves repositories for a given owner.
-
1
get '/:owner_name' do
-
prefer_follower do
-
respond_with service(:find_repos, params)
-
end
-
end
-
-
1
get '/:id/cc' do
-
respond_with service(:find_repo, params.merge(schema: 'cc'))
-
end
-
-
# Get settings for a given repository
-
#
-
1
get '/:id/settings', scope: :private do
-
settings = service(:find_repo_settings, params).run
-
if settings
-
respond_with({ settings: settings.simple_attributes }, version: :v2)
-
else
-
status 404
-
end
-
end
-
-
1
patch '/:id/settings', scope: :private do
-
payload = JSON.parse request.body.read
-
-
if payload['settings'].blank? || !payload['settings'].is_a?(Hash)
-
halt 422, { "error" => "Settings must be passed with a request" }
-
end
-
-
settings = service(:find_repo_settings, params).run
-
if settings
-
settings.merge(payload['settings'])
-
# TODO: I would like to have better API here, but leaving this
-
# for testing to not waste too much time before I can play with it
-
if settings.save
-
respond_with({ settings: settings.simple_attributes }, version: :v2)
-
else
-
status 422
-
respond_with(settings, type: :validation_error, version: :v2)
-
end
-
else
-
status 404
-
end
-
end
-
-
# Get the public key for the repository with the given id.
-
#
-
# This can be used to encrypt secure variables in the build configuration. See
-
# [the encryption keys](http://docs.travis-ci.com/user/encryption-keys/) documentation page for more
-
# information.
-
#
-
# ### Response
-
#
-
# json(:repository_key)
-
1
get '/:id/key' do
-
respond_with service(:find_repo_key, params), version: :v2
-
end
-
-
1
post '/:id/key' do
-
respond_with service(:regenerate_repo_key, params), version: :v2
-
end
-
-
# Gets list of branches
-
1
get '/:repository_id/branches' do
-
respond_with service(:find_branches, params), type: :branches, version: :v2
-
end
-
-
# Gets lastest build on a branch branches
-
1
get '/:repository_id/branches/:branch' do
-
respond_with service(:find_branch, params), type: :branch, version: :v2
-
end
-
-
# List caches for a given repo. Can be filtered with `branch` and `match` query parameter.
-
1
get '/:repository_id/caches', scope: :private do
-
respond_with service(:find_caches, params), type: :caches, version: :v2
-
end
-
-
# Delete caches for a given repo. Can be filtered with `branch` and `match` query parameter.
-
1
delete '/:repository_id/caches', scope: :private do
-
respond_with service(:delete_caches, params), type: :caches, version: :v2
-
end
-
-
# Gets the repository with the given name.
-
#
-
# ### Response
-
#
-
# json(:repository)
-
1
get '/:owner_name/:name' do
-
prefer_follower do
-
respond_with service(:find_repo, params)
-
end
-
end
-
-
# Gets the builds for the repository with the given name.
-
#
-
# ### Response
-
#
-
# json(:builds)
-
1
get '/:owner_name/:name/builds' do
-
name = params[:branches] ? :find_branches : :find_builds
-
params['ids'] = params['ids'].split(',') if params['ids'].respond_to?(:split)
-
respond_with service(:find_builds, params)
-
end
-
-
# Get a build with the given id in the repository with the given name.
-
#
-
# ### Response
-
#
-
# json(:build)
-
1
get '/:owner_name/:name/builds/:id' do
-
respond_with service(:find_build, params)
-
end
-
-
1
get '/:owner_name/:name/cc' do
-
respond_with service(:find_repo, params.merge(schema: 'cc'))
-
end
-
-
# Get the public key for a given repository.
-
#
-
# This can be used to encrypt secure variables in the build configuration. See
-
# [the encryption keys](http://docs.travis-ci.com/user/encryption-keys/) documentation page for more
-
# information.
-
#
-
# ### Response
-
#
-
# json(:repository_key)
-
1
get '/:owner_name/:name/key' do
-
respond_with service(:find_repo_key, params), version: :v2
-
end
-
-
1
post '/:owner_name/:name/key' do
-
respond_with service(:regenerate_repo_key, params), version: :v2
-
end
-
-
# Gets list of branches
-
1
get '/:owner_name/:name/branches' do
-
respond_with service(:find_branches, params), type: :branches, version: :v2
-
end
-
-
# Gets lastest build on a branch branches
-
1
get '/:owner_name/:name/branches/:branch' do
-
respond_with service(:find_branch, params), type: :branch, version: :v2
-
end
-
-
# List caches for a given repo. Can be filtered with `branch` and `match` query parameter.
-
1
get '/:owner_name/:name/caches', scope: :private do
-
respond_with service(:find_caches, params), type: :caches, version: :v2
-
end
-
-
# Delete caches for a given repo. Can be filtered with `branch` and `match` query parameter.
-
1
delete '/:owner_name/:name/caches', scope: :private do
-
respond_with service(:delete_caches, params), type: :caches, version: :v2
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'travis/api/app/services/schedule_request'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Requests < Endpoint
-
1
post '/', scope: :private do
-
if params[:request] && params[:request][:repository]
-
respond_with service(:schedule_request, params[:request])
-
else
-
# DEPRECATED: this will be removed by 1st of December
-
Metriks.meter("api.request.restart").mark
-
respond_with service(:reset_model, params)
-
end
-
end
-
-
1
get '/' do
-
begin
-
respond_with(service(:find_requests, params).run)
-
rescue Travis::RepositoryNotFoundError => e
-
status 404
-
{ "error" => "Repository could not be found" }
-
end
-
end
-
-
1
get '/:id' do
-
respond_with service(:find_request, params)
-
end
-
end
-
end
-
end
-
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class SettingsEndpoint < Endpoint
-
66
set(:prefix) { "/settings/" << name[/[^:]+$/].underscore }
-
-
1
class << self
-
# This method checks if class based on a given name exists or creates
-
# a new SettingsEndpoint subclass, which will be then used as an endpoint
-
1
def subclass(name)
-
54
class_name = name.to_s.camelize
-
54
if Travis::Api::App::Endpoint.const_defined?(class_name)
-
53
Travis::Api::App::Endpoint.const_get(class_name)
-
else
-
1
klass = create_settings_class(name)
-
1
Travis::Api::App::Endpoint.const_set(class_name, klass)
-
1
klass
-
end
-
end
-
-
1
def create_settings_class(name)
-
klass = Class.new(self) do
-
define_method(:name) { name }
-
define_routes!
-
end
-
end
-
-
1
def define_routes!
-
1
get("/", scope: :private) do index end
-
1
get("/:id", scope: :private) do show end
-
1
post("/", scope: :private) do create end
-
1
patch("/:id", scope: :private) do update end
-
1
delete("/:id", scope: :private) do destroy end
-
end
-
end
-
-
# Rails style methods for easy overriding
-
1
def index
-
respond_with(collection, type: name, version: :v2)
-
end
-
-
1
def show
-
respond_with(record, type: singular_name, version: :v2)
-
end
-
-
1
def update
-
record.update(JSON.parse(request.body.read)[singular_name])
-
if record.valid?
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
else
-
status 422
-
respond_with(record, type: :validation_error, version: :v2)
-
end
-
end
-
-
1
def create
-
record = collection.create(JSON.parse(request.body.read)[singular_name])
-
if record.valid?
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
else
-
status 422
-
respond_with(record, type: :validation_error, version: :v2)
-
end
-
end
-
-
1
def destroy
-
record = collection.destroy(params[:id]) || record_not_found
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
end
-
-
1
def singular_name
-
name.to_s.singularize
-
end
-
-
1
def collection
-
@collection ||= repo_settings.send(name)
-
end
-
-
# This method can't be called "settings" because it clashes with
-
# Sinatra's method
-
1
def repo_settings
-
@settings ||= begin
-
service(:find_repo_settings, id: params['repository_id'].to_i).run
-
end || halt(404, error: "Couldn't find repository")
-
end
-
-
1
def record
-
collection.find(params[:id]) || record_not_found
-
end
-
-
1
def record_not_found
-
halt(404, { error: "Could not find a requested setting" })
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'travis/api/app/endpoint/setting_endpoint'
-
-
1
class Travis::Api::App
-
1
class SingletonSettingsEndpoint < SettingsEndpoint
-
1
class << self
-
1
def create_settings_class(name)
-
1
klass = Class.new(self) do
-
1
define_method(:name) { name }
-
1
get("/:repository_id", scope: :private) do show end
-
1
patch("/:repository_id", scope: :private) do update end
-
1
delete("/:repository_id", scope: :private) do destroy end
-
end
-
end
-
end
-
-
1
def update
-
record = parent.update(name, JSON.parse(request.body.read)[singular_name])
-
if record.valid?
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
else
-
status 422
-
respond_with(record, type: :validation_error, version: :v2)
-
end
-
end
-
-
1
def destroy
-
record = parent.delete(name)
-
repo_settings.save
-
respond_with(record, type: singular_name, version: :v2)
-
end
-
-
1
def record
-
parent.get(name) || record_not_found
-
end
-
-
1
def parent
-
repo_settings
-
end
-
-
1
def record_not_found
-
halt(404, { error: "Could not find a requested setting" })
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
1
class Uptime < Endpoint
-
1
get '/' do
-
begin
-
ActiveRecord::Base.connection.execute('select 1')
-
[200, "OK"]
-
rescue Exception => e
-
return [500, "Error: #{e.message}"]
-
end
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Endpoint
-
# TODO should this be /profile?
-
1
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"
-
# }
-
# }
-
1
get '/', scope: :private do
-
respond_with current_user
-
end
-
-
1
get '/permissions', scope: :private do
-
respond_with service(:find_user_permissions), type: :permissions
-
end
-
-
# TODO fix url/key generation in ember-data
-
# get '/accounts', scope: :private do
-
# respond_with service(:users, :find_accounts), type: :accounts
-
# end
-
-
# TODO fix url/key generation in ember-data
-
# get '/broadcasts', scope: :private do
-
# respond_with service(:users, :find_broadcasts), type: :broadcasts
-
# end
-
-
1
get '/:id', scope: :private do
-
pass unless current_user.id.to_s == params[:id]
-
respond_with current_user
-
end
-
-
1
put '/:id?', scope: :private do
-
respond_with service(:update_user, params[:user])
-
end
-
-
1
post '/sync', scope: :private do
-
if current_user.syncing?
-
status 409
-
{ 'message' => "Sync already in progress. Try again later." }
-
else
-
respond_with service(:sync_user)
-
end
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
# Namespace for Sinatra extensions.
-
1
module Extensions
-
5
Dir.glob("#{__dir__}/extensions/*.rb").each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] }
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Extensions
-
1
module ExposePattern
-
1
def route(verb, path, *)
-
119
condition { headers('X-Endpoint' => settings.name.to_s, 'X-Pattern' => path.to_s) }
-
119
super
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Extensions
-
1
module Scoping
-
1
module Helpers
-
1
def scope
-
env['travis.scope'].to_sym
-
end
-
-
1
def public?
-
scope == :public
-
end
-
-
1
def required_params_match?
-
return true unless token = env['travis.access_token']
-
-
if token.extra && (required_params = token.extra['required_params'])
-
required_params.all? { |name, value| params[name] == value }
-
else
-
true
-
end
-
end
-
end
-
-
1
def self.registered(app)
-
1
app.set default_scope: :public, anonymous_scopes: [:public]
-
1
app.helpers(Helpers)
-
end
-
-
1
def scope(*names)
-
118
condition do
-
names = [settings.default_scope] if names == [:default]
-
scopes = env['travis.access_token'].try(:scopes) || settings.anonymous_scopes
-
-
result = names.any? do |name|
-
if scopes.include?(name) && required_params_match?
-
headers['X-OAuth-Scopes'] = scopes.map(&:to_s).join(',')
-
headers['X-Accepted-OAuth-Scopes'] = name.to_s
-
-
env['travis.scope'] = name
-
headers['Vary'] = 'Accept'
-
headers['Vary'] << ', Authorization' unless public?
-
true
-
end
-
end
-
-
if !result
-
headers['X-OAuth-Scopes'] = scopes.map(&:to_s).join(',')
-
headers['X-Accepted-OAuth-Scopes'] = names.first.to_s
-
-
if env['travis.access_token']
-
pass { halt 403, "insufficient access" }
-
else
-
pass { halt 401, "no access token supplied" }
-
end
-
end
-
-
result
-
end
-
end
-
-
1
def route(verb, path, options = {}, &block)
-
118
options[:scope] ||= :default
-
118
super(verb, path, options, &block)
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Extensions
-
# Allows writing
-
#
-
# helpers :some_helper
-
#
-
# Instead of
-
#
-
# helpers Travis::Api::App::Helpers::SomeHelper
-
1
module SmartConstants
-
1
def helpers(*list, &block)
-
4
super(*resolve_constants(list, Helpers), &block)
-
end
-
-
1
def register(*list, &block)
-
4
super(*resolve_constants(list, Extensions), &block)
-
end
-
-
1
private
-
-
1
def resolve_constants(list, namespace)
-
20
list.map { |e| Symbol === e ? namespace.const_get(e.to_s.camelize) : e }
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Extensions
-
# Keeps track of subclasses. Used for endpoint and middleware detection.
-
# This will prevent garbage collection of subclasses.
-
1
module SubclassTracker
-
1
def direct_subclasses
-
707
@direct_subclasses ||= []
-
end
-
-
# List of "leaf" subclasses (ie subclasses without subclasses).
-
1
def subclasses
-
594
return [self] if direct_subclasses.empty?
-
85
direct_subclasses.map(&:subclasses).flatten.uniq
-
end
-
-
1
def inherited(subclass)
-
28
super
-
28
subclass.set app_file: caller_files.first
-
28
direct_subclasses << subclass
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
# Namespace for helpers.
-
1
module Helpers
-
7
Dir.glob("#{__dir__}/helpers/*.rb").each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] }
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Helpers
-
1
module Accept
-
1
HEADER_FORMAT = /vnd\.travis-ci\.(\d+)\+(\w+)/
-
1
DEFAULT_VERSION = 'v1'
-
1
DEFAULT_FORMAT = 'json'
-
-
1
class Entry
-
1
SEPARATORS = Regexp.escape("()<>@,;:\/[]?={}\t ")
-
1
TOKEN = /[^#{SEPARATORS}]+/
-
1
attr_reader :type, :subtype, :quality, :version, :params
-
1
def initialize(accept_string)
-
@type, @subtype, @quality, @version, @params = parse(accept_string)
-
end
-
-
1
def <=>(other)
-
[1 - quality, mime_type.count('*'), 1 - params.size] <=>
-
[1 - other.quality, other.mime_type.count('*'), 1 - other.params.size]
-
end
-
-
1
def mime_type
-
"#{type}/#{subtype}"
-
end
-
-
1
def version
-
version = @version || params['version']
-
version ? "v#{version}" : nil
-
end
-
-
1
def accepts?(mime_type)
-
return true if self.mime_type == '*/*'
-
-
type, subtype = mime_type.scan(%r{(#{TOKEN})/(#{TOKEN})}).flatten
-
type == self.type && (self.subtype == '*' || subtype == self.subtype)
-
end
-
-
1
def to_s
-
str = "#{mime_type}; q=#{quality}"
-
str << "; #{params.map { |k,v| "#{k}=#{v}" }.join('; ')}" if params.length > 0
-
str
-
end
-
-
1
private
-
1
def parse(str)
-
# this handles only subset of what Accept header can
-
# contain, only the simplest cases, no quoted strings etc.
-
type, subtype, params = str.scan(%r{(#{TOKEN})/(#{TOKEN})(.*)}).flatten
-
quality = 1
-
-
version = nil
-
if params
-
params = Hash[*params.split(';').map { |p| p.scan /(#{TOKEN})=(#{TOKEN})/ }.flatten]
-
quality = params.delete('q').to_f if params['q']
-
end
-
-
if subtype =~ HEADER_FORMAT
-
subtype = $2
-
version = $1
-
end
-
-
[type, subtype, quality, version, params]
-
end
-
end
-
-
1
def accepts?(mime_type)
-
accept_entries.any? { |e| e.accepts?(mime_type) }
-
end
-
-
1
def accept_entries
-
entries = env['HTTP_ACCEPT'].to_s.delete(' ').to_s.split(',').map { |e| Entry.new(e) }
-
entries.empty? ? [Entry.new('*/*')] : entries.sort
-
end
-
-
1
def acceptable_formats
-
if format = env['travis.format_from_path']
-
[Entry.new(Rack::Mime.mime_type(".#{format}"))]
-
else
-
accept_entries
-
end
-
end
-
-
1
def accept_version
-
43
@accept_version ||= request.accept.join =~ HEADER_FORMAT && "v#{$1}" || DEFAULT_VERSION
-
end
-
-
1
def accept_format
-
10
@accept_format ||= request.accept.join =~ HEADER_FORMAT && $2 || DEFAULT_FORMAT
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Helpers
-
1
module CurrentUser
-
1
def current_user
-
access_token.user if signed_in?
-
end
-
-
1
def access_token
-
env['travis.access_token']
-
end
-
-
1
def signed_in?
-
!!access_token
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Helpers
-
1
module DbFollower
-
1
def prefer_follower
-
yield
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Helpers
-
1
module Flash
-
1
def flash
-
10
@flash ||= []
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
module Helpers
-
1
module MimeTypes
-
1
def html?
-
request.accept =~ %r(text/html)
-
end
-
-
1
def json?
-
request.accept =~ %r(application/json)
-
end
-
-
1
def xml?
-
request.accept =~ %r(application/xml)
-
end
-
-
1
def png?
-
request.accept =~ %r(image/png)
-
end
-
-
1
def atom?
-
request.accept =~ %r(application/atom+xml)
-
end
-
end
-
end
-
end
-
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
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.
-
1
module RespondWith
-
1
include Accept
-
-
1
STATUS = {
-
success: 200,
-
not_found: 404
-
}
-
-
1
def respond_with(resource, options = {})
-
result = respond(resource, options)
-
if result && response.content_type =~ /application\/json/
-
status STATUS[result[:result]] if result.is_a?(Hash) && result[:result].is_a?(Symbol)
-
result = prettify_result? ? JSON.pretty_generate(result) : result.to_json
-
end
-
halt result || 404
-
end
-
-
1
def body(value = nil, options = {}, &block)
-
40
value = value.to_json if value.is_a?(Hash)
-
40
super(value, &block)
-
end
-
-
1
private
-
-
1
def respond(resource, options)
-
resource = apply_service_responder(resource, options)
-
-
response = nil
-
acceptable_formats.find do |accept|
-
responders(resource, options).find do |const|
-
responder = const.new(self, resource, options.dup.merge(accept: accept))
-
response = responder.apply if responder.apply?
-
end
-
end
-
-
if responders = options[:responders]
-
responders.each do |klass|
-
responder = klass.new(self, response, options)
-
response = responder.apply if responder.apply?
-
end
-
end
-
-
response || (resource ? error(406) : error(404))
-
end
-
-
1
def prettify_result?
-
!params[:pretty].nil? && (params[:pretty].downcase == 'true' || params[:pretty].to_i > 0)
-
end
-
-
1
def apply_service_responder(resource, options)
-
responder = Responders::Service.new(self, resource, options)
-
resource = responder.apply if responder.apply?
-
resource
-
end
-
-
1
def responders(resource, options)
-
[:Json, :Atom, :Image, :Xml, :Plain, :Badge].map do |name|
-
Responders.const_get(name)
-
end
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
# Superclass for all middleware.
-
1
class Middleware < Base
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Middleware
-
# Makes sure we use Travis.logger everywhere.
-
1
class Logging < Middleware
-
2
set(:setup) { ActiveRecord::Base.logger = Travis.logger }
-
-
1
before do
-
23
env['rack.logger'] = Travis.logger
-
23
env['rack.errors'] = Travis.logger.instance_variable_get(:@logdev).dev rescue nil
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'metriks'
-
-
1
class Travis::Api::App
-
1
class Middleware
-
1
class Metriks < Middleware
-
1
include Helpers::Accept
-
-
1
before do
-
23
env['metriks.request.start'] ||= Time.now.utc
-
end
-
-
1
after do
-
23
if queue_start = time(env['HTTP_X_QUEUE_START']) || time(env['HTTP_X_REQUEST_START'])
-
time = env['metriks.request.start'] - queue_start
-
::Metriks.timer('api.request_queue').update(time)
-
end
-
-
23
if response.status < 400
-
13
time = Time.now.utc - env['metriks.request.start']
-
13
if headers['X-Pattern'].present? and headers['X-Endpoint'].present?
-
name = "#{(headers['X-Endpoint'].split("::", 5).last.gsub(/::/, ".")).downcase}#{headers['X-Pattern'].gsub(/[\/]/, '.').gsub(/[:\?\*]/, "_")}"
-
metric = "api.request.endpoint.#{name}"
-
::Metriks.timer(metric).update(time)
-
::Metriks.timer('api.requests').update(time)
-
end
-
13
if content_type.present?
-
13
type = content_type.split(';', 2).first.to_s.gsub(/\s/,'').gsub(/[^A-z\/]+/, '_').gsub('/', '.')
-
13
::Metriks.timer("api.request.content_type.#{type}").update(time)
-
else
-
::Metriks.timer("api.request.content_type.none").update(time)
-
end
-
13
::Metriks.meter("api.request.#{request.request_method.downcase}").mark
-
end
-
-
23
::Metriks.meter("api.request.status.#{response.status.to_s[0]}").mark
-
23
::Metriks.meter("api.request.version.#{accept_version}").mark
-
end
-
-
1
def time(value)
-
46
value = value.to_f
-
46
start = env['metriks.request.start'].to_f
-
46
value /= 1000 while value > start
-
46
Time.at(value) if value > 946684800
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Middleware
-
1
class Rewrite < Middleware
-
1
FORMAT = %r(\.(json|xml|png|txt|atom|svg)$)
-
1
V1_REPO_URL = %r(^(/[^/]+/[^/]+(?:/builds(?:/[\d]+)?|/cc)?)$)
-
-
1
helpers :accept
-
-
2
set(:setup) { ActiveRecord::Base.logger = Travis.logger }
-
-
1
before do
-
10
extract_format
-
10
rewrite_v1_repo_segment if v1? || xml?
-
10
rewrite_v1_named_repo_image_path if png? || svg?
-
end
-
-
1
after do
-
10
redirect_v1_named_repo_path if (v1? || xml?) && not_found?
-
end
-
-
1
private
-
-
1
def extract_format
-
10
env['PATH_INFO'].sub!(FORMAT, '')
-
10
env['travis.format_from_path'] = $1
-
10
env['travis.format'] = $1 || accept_format
-
end
-
-
1
def rewrite_v1_repo_segment
-
10
env['PATH_INFO'].sub!(%r(^/repositories), '/repos')
-
end
-
-
1
def rewrite_v1_named_repo_image_path
-
env['PATH_INFO'].sub!(V1_REPO_URL) { "/repos#{$1}" }
-
end
-
-
1
def redirect_v1_named_repo_path
-
10
if request.path =~ V1_REPO_URL
-
force_redirect("/repositories#{$1}.#{env['travis.format']}")
-
end
-
end
-
-
1
def force_redirect(path)
-
path += "?#{request.query_string}" unless request.query_string.empty?
-
response.body = ''
-
response['Content-Length'] = '0'
-
response['Content-Type'] = ''
-
redirect(path)
-
end
-
-
1
def png?
-
10
env['travis.format'] == 'png'
-
end
-
-
1
def svg?
-
10
env['travis.format'] == 'svg'
-
end
-
-
1
def xml?
-
env['travis.format'] == 'xml'
-
end
-
-
1
def atom?
-
env['travis.format'] == 'atom'
-
end
-
-
1
def v1?
-
20
accept_version == 'v1'
-
end
-
end
-
end
-
end
-
-
1
require 'travis/api/app'
-
-
1
class Travis::Api::App
-
1
class Middleware
-
# Checks access tokens and sets appropriate scopes.
-
1
class ScopeCheck < Middleware
-
1
before do
-
23
next unless token
-
4
access_token = AccessToken.find_by_token(token)
-
4
halt 403, 'access denied' unless access_token
-
4
env['travis.access_token'] = access_token
-
end
-
-
1
after do
-
23
headers['X-OAuth-Scopes'] ||= begin
-
23
scopes = Array(env['travis.access_token'].try(:scopes))
-
23
scopes.map(&:to_s).join(',')
-
end
-
end
-
-
1
def token
-
27
@token ||= header_token || query_token || travis_token
-
end
-
-
1
private
-
-
1
def travis_token
-
19
return unless token = params[:token]
-
AccessToken.for_travis_token(token) || ""
-
end
-
-
1
def query_token
-
19
params[:access_token] if params[:access_token] and not params[:access_token].empty?
-
end
-
-
1
def header_token
-
23
type, payload = env['HTTP_AUTHORIZATION'].to_s.split(" ", 2)
-
23
return if payload.nil? or payload.empty?
-
-
10
case type.downcase
-
when 'basic' then payload.unpack("m").first.split(':', 2).first
-
4
when 'token' then payload.gsub(/^"(.+)"$/, '\1')
-
end
-
end
-
end
-
end
-
end
-
1
require 'travis/api/app'
-
1
require 'useragent'
-
-
1
class Travis::Api::App
-
1
class Middleware
-
1
class UserAgentTracker < Middleware
-
1
WEB_BROWSERS = [
-
"Internet Explorer",
-
"Webkit", "Chrome", "Safari", "Android",
-
"Firefox", "Camino", "Iceweasel", "Seamonkey", "Android",
-
"Opera", "Mozilla"
-
]
-
-
1
before(agent: /^$/) do
-
23
::Metriks.meter("api.user_agent.missing").mark
-
23
halt(400, "error" => "missing User-Agent header") if Travis::Features.feature_active?(:require_user_agent)
-
end
-
-
1
before(agent: /^.+$/) do
-
agent = UserAgent.parse(request.user_agent)
-
case agent.browser
-
when *WEB_BROWSERS then mark_browser
-
when "curl", "Wget" then mark(:console, agent.browser)
-
when "travis-api-wrapper" then mark(:script, :node_js, agent.browser)
-
when "TravisPy" then mark(:script, :python, agent.browser)
-
when "Ruby", "PHP", "Perl", "Python" then mark(:script, agent.browser, :vanilla)
-
when "Faraday" then mark(:script, :ruby, :vanilla)
-
when "Travis" then mark_travis(agent)
-
else mark_unknown
-
end
-
end
-
-
1
def mark_browser
-
# allows a JavaScript Client to set X-User-Agent, for instance to "travis-web" in travis-web
-
x_agent = UserAgent.parse(env['HTTP_X_USER_AGENT'] || 'unknown').browser
-
mark(:browser, x_agent)
-
end
-
-
1
def mark_travis(agent)
-
command = agent.application.comment.detect { |c| c.start_with? "command " }
-
-
if command
-
mark(:cli, :version, agent.version)
-
mark(:cli, command.sub(' ', '.'))
-
else
-
# only track ruby version and library version for non-cli usage
-
mark(:script, :ruby, :travis, :version, agent.version)
-
end
-
end
-
-
1
def mark_unknown
-
logger.warn "[user-agent-tracker] Unknown User-Agent: %p" % request.user_agent
-
mark(:unknown)
-
end
-
-
1
def mark(*keys)
-
key = "api.user_agent." << keys.map { |k| k.to_s.downcase.gsub(/[^a-z0-9\-\.]+/, '_') }.join('.')
-
::Metriks.meter(key).mark
-
end
-
end
-
end
-
end
-
1
require 'multi_json'
-
1
require 'travis/sidekiq/build_request'
-
1
require 'travis/services/base'
-
-
1
class Travis::Api::App
-
1
module Services
-
1
class ScheduleRequest < Travis::Services::Base
-
1
register :schedule_request
-
-
1
def run
-
repo && active? ? schedule_request : not_found
-
end
-
-
1
def messages
-
@messages ||= []
-
end
-
-
1
private
-
-
1
def schedule_request
-
Metriks.meter('api.request.create').mark
-
Travis::Sidekiq::BuildRequest.perform_async(type: 'api', payload: payload, credentials: {})
-
messages << { notice: 'Build request scheduled.' }
-
:success
-
end
-
-
1
def not_found
-
messages << { error: "Repository #{slug} not found." }
-
:not_found
-
end
-
-
1
def active?
-
Travis::Features.owner_active?(:request_create, repo.owner)
-
end
-
-
1
def payload
-
data = params.merge(user: { id: current_user.id })
-
data[:repository][:id] = repo.github_id
-
MultiJson.encode(data)
-
end
-
-
1
def repo
-
@repo ||= Repository.by_slug(slug).first
-
end
-
-
1
def slug
-
repo = params[:repository] || {}
-
repo.values_at(:owner_name, :name).join('/')
-
end
-
end
-
end
-
end
-
1
require 'active_model_serializers'
-
-
1
module Travis
-
1
module Api
-
1
class Serializer < ActiveModel::Serializer
-
1
def data
-
as_json
-
end
-
end
-
-
1
class ArraySerializer < ActiveModel::ArraySerializer
-
1
def data
-
as_json
-
end
-
-
1
def initialize(resource, options)
-
options[:each_serializer] ||= Travis::Api::V2::Http.const_get(options[:root].to_s.singularize.camelize)
-
super(resource, options)
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
require 'travis/api/v2/http'
-
end
-
end
-
end
-
-
1
require 'travis/api/serializer'
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
require 'travis/api/v2/http/accounts'
-
1
require 'travis/api/v2/http/annotations'
-
1
require 'travis/api/v2/http/broadcasts'
-
1
require 'travis/api/v2/http/branch'
-
1
require 'travis/api/v2/http/branches'
-
1
require 'travis/api/v2/http/build'
-
1
require 'travis/api/v2/http/builds'
-
1
require 'travis/api/v2/http/caches'
-
1
require 'travis/api/v2/http/hooks'
-
1
require 'travis/api/v2/http/job'
-
1
require 'travis/api/v2/http/jobs'
-
1
require 'travis/api/v2/http/log'
-
1
require 'travis/api/v2/http/permissions'
-
1
require 'travis/api/v2/http/repositories'
-
1
require 'travis/api/v2/http/repository'
-
1
require 'travis/api/v2/http/requests'
-
1
require 'travis/api/v2/http/request'
-
1
require 'travis/api/v2/http/ssl_key'
-
1
require 'travis/api/v2/http/env_var'
-
1
require 'travis/api/v2/http/env_vars'
-
1
require 'travis/api/v2/http/user'
-
1
require 'travis/api/v2/http/validation_error'
-
1
require 'travis/api/v2/http/ssh_key'
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Accounts
-
1
include Formats
-
-
1
attr_reader :accounts, :options
-
-
1
def initialize(accounts, options = {})
-
@accounts = accounts
-
@options = options
-
end
-
-
1
def data
-
{
-
:accounts => accounts.map { |account| account_data(account) }
-
}
-
end
-
-
1
private
-
-
1
def account_data(account)
-
{
-
'id' => account.id,
-
'name' => account.name,
-
'login' => account.login,
-
'type' => account.type.underscore,
-
'repos_count' => account.repos_count
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Annotations
-
1
include Formats
-
-
1
def initialize(annotations, options = {})
-
@annotations = annotations
-
end
-
-
1
def data
-
{
-
"annotations" => @annotations.map { |annotation| build_annotation(annotation) },
-
}
-
end
-
-
1
private
-
-
1
def build_annotation(annotation)
-
{
-
"id" => annotation.id,
-
"job_id" => annotation.job_id,
-
"description" => annotation.description,
-
"url" => annotation.url,
-
"status" => annotation.status,
-
"provider_name" => annotation.annotation_provider.name,
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'travis/api/v2/http/branches'
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Branch < Branches
-
1
include Formats
-
-
1
attr_reader :build, :commit, :options
-
-
1
def initialize(build, options = {})
-
@build = build
-
@commit = build.commit
-
@options = options
-
end
-
-
1
def data
-
{
-
'branch' => build_data(build),
-
'commit' => commit_data(commit)
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Branches
-
1
include Formats
-
-
1
attr_reader :builds, :commits, :options
-
-
1
def initialize(builds, options = {})
-
builds = builds.last_finished_builds_by_branches if builds.is_a?(Repository) # TODO remove, bc
-
@builds = builds
-
@commits = builds.map(&:commit)
-
@options = options
-
end
-
-
1
def data
-
{
-
'branches' => builds.map { |build| build_data(build) },
-
'commits' => commits.map { |commit| commit_data(commit) }
-
}
-
end
-
-
1
private
-
-
1
def build_data(build)
-
{
-
'id' => build.id,
-
'repository_id' => build.repository_id,
-
'commit_id' => build.commit_id,
-
'number' => build.number,
-
'config' => build.obfuscated_config.stringify_keys,
-
'state' => build.state.to_s,
-
'started_at' => format_date(build.started_at),
-
'finished_at' => format_date(build.finished_at),
-
'duration' => build.duration,
-
'job_ids' => build.matrix.map { |job| job.id },
-
'pull_request' => build.pull_request?
-
}
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Broadcasts
-
1
attr_reader :broadcasts, :options
-
-
1
def initialize(broadcasts, options = {})
-
@broadcasts = broadcasts
-
@options = options
-
end
-
-
1
def data
-
{
-
'broadcasts' => broadcasts.map { |broadcast| broadcast_data(broadcast) }
-
}
-
end
-
-
1
private
-
-
1
def broadcast_data(broadcast)
-
{
-
'id' => broadcast.id,
-
'message' => broadcast.message
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Build
-
1
include Formats
-
-
1
attr_reader :build, :options
-
-
1
def initialize(build, options = {})
-
options[:include_jobs] = true unless options.key?(:include_jobs)
-
-
@build = build
-
@options = options
-
end
-
-
1
def data
-
{
-
'build' => build_data(build),
-
'commit' => commit_data(build.commit),
-
'jobs' => options[:include_jobs] ? build.matrix.map { |job| job_data(job) } : [],
-
'annotations' => options[:include_jobs] ? Annotations.new(annotations(build), @options).data["annotations"] : [],
-
}
-
end
-
-
1
private
-
-
1
def build_data(build)
-
{
-
'id' => build.id,
-
'repository_id' => build.repository_id,
-
'commit_id' => build.commit_id,
-
'number' => build.number,
-
'pull_request' => build.pull_request?,
-
'pull_request_title' => build.pull_request_title,
-
'pull_request_number' => build.pull_request_number,
-
'config' => build.obfuscated_config.stringify_keys,
-
'state' => build.state.to_s,
-
'started_at' => format_date(build.started_at),
-
'finished_at' => format_date(build.finished_at),
-
'duration' => build.duration,
-
'job_ids' => build.matrix_ids
-
}
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
}
-
end
-
-
1
def job_data(job)
-
{
-
'id' => job.id,
-
'repository_id' => job.repository_id,
-
'build_id' => job.source_id,
-
'commit_id' => job.commit_id,
-
'log_id' => job.log_id,
-
'state' => job.state.to_s,
-
'number' => job.number,
-
'config' => job.obfuscated_config.stringify_keys,
-
'started_at' => format_date(job.started_at),
-
'finished_at' => format_date(job.finished_at),
-
'queue' => job.queue,
-
'allow_failure' => job.allow_failure,
-
'tags' => job.tags,
-
'annotation_ids' => job.annotation_ids,
-
}
-
end
-
-
1
def annotations(build)
-
build.matrix.map(&:annotations).flatten
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Builds
-
1
include Formats
-
-
1
attr_reader :builds, :commits, :options
-
-
1
def initialize(builds, options = {})
-
@builds = builds
-
@commits = builds.map(&:commit)
-
@options = options
-
end
-
-
1
def data
-
{
-
'builds' => builds.map { |build| build_data(build) },
-
'commits' => commits.map { |commit| commit_data(commit) }
-
}
-
end
-
-
1
private
-
-
1
def build_data(build)
-
{
-
'id' => build.id,
-
'repository_id' => build.repository_id,
-
'commit_id' => build.commit_id,
-
'number' => build.number,
-
'pull_request' => build.pull_request?,
-
'pull_request_title' => build.pull_request_title,
-
'pull_request_number' => build.pull_request_number,
-
'config' => build.obfuscated_config.stringify_keys,
-
'state' => build.state.to_s,
-
'started_at' => format_date(build.started_at),
-
'finished_at' => format_date(build.finished_at),
-
'duration' => build.duration,
-
'job_ids' => matrix_ids(build)
-
}
-
end
-
-
1
def matrix_ids(build)
-
build.cached_matrix_ids || build.matrix_ids
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
'pull_request_number' => commit.pull_request_number
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Caches
-
1
include Formats
-
1
attr_reader :caches, :options
-
-
1
def initialize(caches, options = {})
-
@caches = caches
-
@options = options
-
end
-
-
1
def data
-
{ 'caches' => caches.map { |cache| cache_data(cache) } }
-
end
-
-
1
private
-
-
1
def cache_data(cache)
-
{
-
'repository_id' => cache.repository.id,
-
'size' => cache.size,
-
'slug' => cache.slug,
-
'branch' => cache.branch,
-
'last_modified' => format_date(cache.last_modified)
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class EnvVar < Travis::Api::Serializer
-
1
attributes :id, :name, :value, :public, :repository_id
-
-
1
def value
-
if object.public?
-
object.value.decrypt
-
end
-
end
-
-
1
def serializable_hash
-
hash = super
-
hash.delete :value unless object.public?
-
hash
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Travis::Api::V2::Http::EnvVars < Travis::Api::ArraySerializer
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Hooks
-
1
attr_reader :hooks, :options
-
-
1
def initialize(hooks, options = {})
-
@hooks = hooks
-
@options = options
-
end
-
-
1
def data
-
{
-
'hooks' => hooks.map { |hook| hook_data(hook) },
-
}
-
end
-
-
1
private
-
-
1
def hook_data(hook)
-
{
-
'id' => hook.id,
-
'name' => hook.name,
-
'owner_name' => hook.owner_name,
-
'description' => hook.description,
-
'active' => hook.active,
-
'private' => hook.private,
-
'admin' => hook.admin?
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Job
-
1
include Formats
-
-
1
attr_reader :job, :options
-
-
1
def initialize(job, options = {})
-
@job = job
-
@options = options
-
end
-
-
1
def data
-
{
-
'job' => job_data(job),
-
'commit' => commit_data(job.commit),
-
'annotations' => Annotations.new(job.annotations, @options).data["annotations"],
-
}
-
end
-
-
1
private
-
-
1
def job_data(job)
-
{
-
'id' => job.id,
-
'repository_id' => job.repository_id,
-
'repository_slug' => job.repository.slug,
-
'build_id' => job.source_id,
-
'commit_id' => job.commit_id,
-
'log_id' => job.log_id,
-
'number' => job.number,
-
'config' => job.obfuscated_config.stringify_keys,
-
'state' => job.state.to_s,
-
'started_at' => format_date(job.started_at),
-
'finished_at' => format_date(job.finished_at),
-
'queue' => job.queue,
-
'allow_failure' => job.allow_failure,
-
'tags' => job.tags,
-
'annotation_ids' => job.annotation_ids,
-
}
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Jobs
-
1
include Formats
-
-
1
attr_reader :jobs, :options
-
-
1
def initialize(jobs, options = {})
-
@jobs = jobs
-
@options = options
-
end
-
-
1
def data
-
{
-
'jobs' => jobs.map { |job| job_data(job) },
-
'commits' => jobs.map { |job| commit_data(job.commit) }
-
}
-
end
-
-
1
private
-
-
1
def job_data(job)
-
{
-
'id' => job.id,
-
'repository_id' => job.repository_id,
-
'repository_slug' => job.repository.slug,
-
'build_id' => job.source_id,
-
'commit_id' => job.commit_id,
-
'log_id' => job.log_id,
-
'number' => job.number,
-
'config' => job.obfuscated_config.stringify_keys,
-
'state' => job.state.to_s,
-
'started_at' => format_date(job.started_at),
-
'finished_at' => format_date(job.finished_at),
-
'queue' => job.queue,
-
'allow_failure' => job.allow_failure,
-
'tags' => job.tags
-
}
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Log
-
1
attr_reader :log, :options
-
-
1
def initialize(log, options = {})
-
@log = log
-
@options = options
-
end
-
-
1
def data
-
{
-
'log' => options[:chunked] ? chunked_log_data : log_data,
-
}
-
end
-
-
1
private
-
-
1
def log_data
-
{
-
'id' => log.id,
-
'job_id' => log.job_id,
-
'type' => log.class.name.demodulize,
-
'body' => log.content
-
}
-
end
-
-
1
def chunked_log_data
-
{
-
'id' => log.id,
-
'job_id' => log.job_id,
-
'type' => log.class.name.demodulize,
-
'parts' => log_parts
-
}
-
end
-
-
1
def log_parts
-
parts = log.parts
-
parts = parts.where(number: part_numbers) if part_numbers
-
parts = parts.where(["number > ?", after]) if after
-
parts.sort_by(&:number).map do |part|
-
{
-
'id' => part.id,
-
'number' => part.number,
-
'content' => part.content,
-
'final' => part.final
-
}
-
end
-
end
-
-
1
def after
-
after = options['after'].to_i
-
after == 0 ? nil : after
-
end
-
-
1
def part_numbers
-
if numbers = options['part_numbers']
-
numbers.is_a?(String) ? numbers.split(',').map(&:to_i) : numbers
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Permissions
-
1
attr_reader :permissions, :options
-
-
1
def initialize(permissions, options = {})
-
@permissions = permissions
-
@options = options
-
end
-
-
1
def data
-
{
-
'permissions' => repo_ids,
-
'admin' => admin_ids,
-
'pull' => pull_ids,
-
'push' => push_ids
-
}
-
end
-
-
1
private
-
1
def filtered_ids(perm = nil)
-
if perm
-
permissions.find_all { |p| p.send("#{perm}?") }.map { |permission| permission.repository_id }
-
else
-
permissions.map { |permission| permission.repository_id }
-
end
-
end
-
-
1
def repo_ids
-
filtered_ids
-
end
-
-
1
def admin_ids
-
filtered_ids(:admin)
-
end
-
-
1
def pull_ids
-
filtered_ids(:pull)
-
end
-
-
1
def push_ids
-
filtered_ids(:push)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Repositories
-
1
include Formats
-
-
1
attr_reader :repositories, :options
-
-
1
def initialize(repositories, options = {})
-
@repositories = repositories
-
@options = options
-
end
-
-
1
def data
-
{
-
'repos' => repositories.map { |repository| repository_data(repository) }
-
}
-
end
-
-
1
private
-
-
1
def repository_data(repository)
-
{
-
'id' => repository.id,
-
'slug' => repository.slug,
-
'description' => repository.description,
-
'last_build_id' => repository.last_build_id,
-
'last_build_number' => repository.last_build_number,
-
'last_build_state' => repository.last_build_state.to_s,
-
'last_build_duration' => repository.last_build_duration,
-
'last_build_language' => nil,
-
'last_build_started_at' => format_date(repository.last_build_started_at),
-
'last_build_finished_at' => format_date(repository.last_build_finished_at),
-
'active' => repository.active,
-
'github_language' => repository.github_language
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Repository
-
1
include Formats
-
-
1
attr_reader :repository, :options
-
-
1
def initialize(repository, options = {})
-
@repository = repository
-
end
-
-
1
def data
-
{
-
'repo' => repository_data(repository)
-
}
-
end
-
-
1
private
-
-
# TODO why does this not include the last build? (i.e. 'builds' => { last build here })
-
1
def repository_data(repository)
-
{
-
'id' => repository.id,
-
'slug' => repository.slug,
-
'description' => repository.description,
-
'last_build_id' => repository.last_build_id,
-
'last_build_number' => repository.last_build_number,
-
'last_build_state' => repository.last_build_state.to_s,
-
'last_build_duration' => repository.last_build_duration,
-
'last_build_language' => nil,
-
'last_build_started_at' => format_date(repository.last_build_started_at),
-
'last_build_finished_at' => format_date(repository.last_build_finished_at),
-
'github_language' => repository.github_language
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Request
-
1
include Formats
-
1
attr_reader :request, :commit, :options
-
-
1
def initialize(request, options = {})
-
@request = request
-
@commit = request.commit
-
@options = options
-
end
-
-
1
def data
-
data = {
-
'request' => request_data
-
}
-
if commit
-
data['commit'] = commit_data
-
end
-
data
-
end
-
-
1
private
-
-
1
def request_data
-
data = {
-
'id' => request.id,
-
'repository_id' => request.repository_id,
-
'commit_id' => request.commit_id,
-
'created_at' => format_date(request.created_at),
-
'owner_id' => request.owner_id,
-
'owner_type' => request.owner_type,
-
'event_type' => request.event_type,
-
'base_commit' => request.base_commit,
-
'head_commit' => request.head_commit,
-
'result' => request.result,
-
'message' => request.message,
-
'pull_request' => request.pull_request?,
-
'pull_request_number' => request.pull_request_number,
-
'pull_request_title' => request.pull_request_title,
-
'branch' => request.branch_name,
-
'tag' => request.tag_name
-
}
-
-
data['build_id'] = request.builds.first.id if request.builds.present?
-
-
data
-
end
-
-
1
def commit_data
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
'pull_request_number' => commit.pull_request_number
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class Requests
-
1
include Formats
-
1
attr_reader :requests, :commits, :options
-
-
1
def initialize(requests, options = {})
-
@requests = requests
-
@commits = requests.map(&:commit)
-
@options = options
-
end
-
-
1
def data
-
{
-
'requests' => requests.map { |request| request_data(request) },
-
'commits' => commits.compact.map { |commit| commit_data(commit) }
-
}
-
end
-
-
1
private
-
-
1
def request_data(request)
-
data = {
-
'id' => request.id,
-
'repository_id' => request.repository_id,
-
'commit_id' => request.commit_id,
-
'created_at' => format_date(request.created_at),
-
'owner_id' => request.owner_id,
-
'owner_type' => request.owner_type,
-
'event_type' => request.event_type,
-
'base_commit' => request.base_commit,
-
'head_commit' => request.head_commit,
-
'result' => request.result,
-
'message' => request.message,
-
'pull_request' => request.pull_request?,
-
'pull_request_number' => request.pull_request_number,
-
'pull_request_title' => request.pull_request_title,
-
'branch' => request.branch_name,
-
'tag' => request.tag_name
-
}
-
-
data['build_id'] = request.builds.first.id if request.builds.present?
-
-
data
-
end
-
-
1
def commit_data(commit)
-
{
-
'id' => commit.id,
-
'sha' => commit.commit,
-
'branch' => commit.branch,
-
'message' => commit.message,
-
'committed_at' => format_date(commit.committed_at),
-
'author_name' => commit.author_name,
-
'author_email' => commit.author_email,
-
'committer_name' => commit.committer_name,
-
'committer_email' => commit.committer_email,
-
'compare_url' => commit.compare_url,
-
'pull_request_number' => commit.pull_request_number
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'travis/private_key'
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class SshKey < Travis::Api::Serializer
-
1
attributes :id, :description, :fingerprint
-
-
1
def id
-
object.repository_id
-
end
-
-
1
def fingerprint
-
value = object.value.decrypt
-
return unless value
-
PrivateKey.new(value).fingerprint
-
rescue OpenSSL::PKey::RSAError
-
nil
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class SslKey
-
1
attr_reader :key
-
-
1
def initialize(key, options = {})
-
@key = key
-
end
-
-
1
def fingerprint
-
PrivateKey.new(key.private_key).fingerprint
-
end
-
-
1
def data
-
{
-
'key' => key.public_key,
-
'fingerprint' => fingerprint
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class User
-
1
include Formats
-
-
1
attr_reader :user, :options
-
-
1
def initialize(user, options = {})
-
@user = user
-
@options = options
-
end
-
-
1
def data
-
{
-
'user' => user_data,
-
}
-
end
-
-
1
private
-
-
1
def user_data
-
{
-
'id' => user.id,
-
'name' => user.name,
-
'login' => user.login,
-
'email' => user.email,
-
'gravatar_id' => user.email ? Digest::MD5.hexdigest(user.email) : "",
-
'locale' => user.locale,
-
'is_syncing' => user.syncing?,
-
'synced_at' => format_date(user.synced_at),
-
'correct_scopes' => user.correct_scopes?,
-
'created_at' => format_date(user.created_at),
-
}
-
end
-
end
-
end
-
end
-
end
-
end
-
-
-
1
module Travis
-
1
module Api
-
1
module V2
-
1
module Http
-
1
class ValidationError
-
1
attr_reader :resource
-
-
1
def initialize(resource, options = {})
-
@resource = resource
-
end
-
-
1
def data
-
response = {
-
message: 'Validation failed'
-
}
-
resource.errors.to_hash.each do |name, errors|
-
response['errors'] ||= []
-
errors.each do |error_code|
-
response['errors'] << { field: name, code: code(error_code) }
-
end
-
end
-
-
response
-
end
-
-
1
def code(error_code)
-
case error_code
-
when :blank
-
'missing_field'
-
else
-
error_code.to_s
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
module Travis
-
1
module API
-
1
module V3
-
1
V3 = self
-
-
1
def load_dir(dir, recursive: true)
-
49
Dir.glob("#{dir}/*.rb").each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] }
-
53
Dir.glob("#{dir}/*").each { |dir| load_dir(dir) } if recursive
-
end
-
-
1
def response(payload, headers = {}, content_type: 'application/json'.freeze, status: 200)
-
22
payload = JSON.pretty_generate(payload) unless payload.is_a? String
-
22
headers = { 'Content-Type'.freeze => content_type, 'Content-Length'.freeze => payload.bytesize.to_s }.merge!(headers)
-
22
[status, headers, [payload] ]
-
end
-
-
1
extend self
-
1
load_dir("#{__dir__}/v3")
-
-
1
ClientError = Error .create(status: 400)
-
1
NotFound = ClientError .create(:resource, status: 404, template: '%s not found (or insufficient access)')
-
1
EnitityMissing = NotFound .create(type: 'not_found')
-
1
WrongCredentials = ClientError .create('access denied', status: 403)
-
1
WrongParams = ClientError .create('wrong parameters')
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module AccessControl
-
1
REGISTER = {}
-
-
1
def self.new(env)
-
23
type, payload = env['HTTP_AUTHORIZATION'.freeze].to_s.split(" ", 2)
-
23
payload &&= payload.unpack(?m.freeze).first if type == 'basic'.freeze
-
23
payload &&= type == 'token'.freeze ? payload.gsub(/^"(.+)"$/, '\1'.freeze) : payload.split(?:.freeze)
-
23
modes = REGISTER.fetch(type, [])
-
46
access_control = modes.inject(nil) { |current, mode| current || mode.for_request(type, payload, env) }
-
23
raise WrongCredentials unless access_control
-
23
access_control
-
end
-
end
-
end
-
1
require 'travis/api/v3/access_control/generic'
-
-
1
module Travis::API::V3
-
1
class AccessControl::Anonymous < AccessControl::Generic
-
# use when Authorization header is not set
-
1
auth_type(nil)
-
-
1
def self.for_request(*)
-
13
new
-
end
-
end
-
end
-
1
require 'travis/api/v3/access_control/generic'
-
-
1
module Travis::API::V3
-
1
class AccessControl::Application < AccessControl::Generic
-
1
attr_reader :application_name, :config, :user
-
-
1
def initialize(application_name, user: nil)
-
6
@application_name = application_name
-
6
@config = Travis.config.applications[application_name]
-
6
@user = user
-
6
raise ArgumentError, 'unknown application %p' % application_name unless config
-
6
raise ArgumentError, 'cannot use %p without a user' % application_name if config.requires_user and not user
-
end
-
-
1
protected
-
-
1
def logged_in?
-
!!user
-
end
-
-
1
def full_access?
-
4
config.full_access
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class AccessControl::Generic
-
1
def self.for_request(type, payload, env)
-
end
-
-
1
def self.auth_type(*list)
-
7
list.each { |e| (AccessControl::REGISTER[e] ||= []) << self }
-
end
-
-
1
def visible?(object)
-
18
full_access? or dispatch(object)
-
end
-
-
1
protected
-
-
1
def repository_visible?(repository)
-
14
return true if unrestricted_api? and not repository.private?
-
12
private_repository_visible?(repository)
-
end
-
-
1
def private_repository_visible?(repository)
-
4
false
-
end
-
-
1
def full_access?
-
28
false
-
end
-
-
1
def logged_in?
-
14
false
-
end
-
-
1
def public_api?
-
14
!Travis.config.private_api
-
end
-
-
1
def unrestricted_api?
-
14
full_access? or logged_in? or public_api?
-
end
-
-
1
private
-
-
1
def dispatch(object, method = caller_locations.first.base_label)
-
14
method = object.class.name.underscore + ?_.freeze + method
-
14
send(method, object) if respond_to?(method, true)
-
end
-
end
-
end
-
1
require 'travis/api/app/access_token'
-
1
require 'travis/api/v3/access_control/user'
-
-
1
module Travis::API::V3
-
# Using v2 API tokens to access v3 API.
-
# Allows us to later introduce a new way of storing tokens with more capabilities without API users having to know.
-
1
class AccessControl::LegacyToken < AccessControl::User
-
1
auth_type('token', 'basic')
-
-
1
def self.for_request(type, payload, env)
-
4
payload = paylad.first if payload.is_a? Array
-
4
token = Travis::Api::App::AccessToken.find_by_token(payload)
-
4
new(token) if token
-
end
-
-
1
def initialize(token)
-
4
@token = token
-
4
super(token.user)
-
end
-
-
1
protected
-
-
1
def permission?(action, id)
-
4
super if @token.scopes.include? :private
-
end
-
end
-
end
-
1
require 'travis/api/v3/access_control/generic'
-
-
1
module Travis::API::V3
-
1
class AccessControl::Scoped < AccessControl::Generic
-
1
attr_accessor :unscoped, :owner_name, :name
-
-
1
def initialize(scope, unscoped)
-
4
@owner_name, @name = scope.split(?/.freeze, 2)
-
4
@unscoped = unscoped
-
end
-
-
1
protected
-
-
1
def private_repository_visible?(repository)
-
4
return false if name and repository.name != name
-
4
unscoped.visible?(repository) if repository.owner_name == owner_name
-
end
-
end
-
end
-
1
require 'travis/api/v3/access_control/generic'
-
1
require 'travis/api/app/access_token'
-
1
require 'digest/sha1'
-
1
require 'openssl'
-
-
1
module Travis::API::V3
-
# Support signed requests to not expose the secret to an untrusted environment.
-
1
class AccessControl::Signature < AccessControl::Generic
-
1
auth_type('signature')
-
-
1
def self.for_request(type, payload, env)
-
6
*args, signature = payload
-
16
options = Hash[args.map { |a| a.split(?=.freeze, 2) }]
-
6
challenge = ""
-
-
6
if github_id = options[?u.freeze]
-
return unless user = ::User.find_by_github_id(github_id)
-
end
-
-
6
if application = options[?a.freeze]
-
6
return unless Travis.config.applications and app_config = Travis.config.applications[application]
-
end
-
-
6
if c = options[?c.freeze]
-
challenge << env['REQUEST_METHOD'.freeze] << "\n".freeze if c.include?(?m.freeze)
-
challenge << env['SCRIPT_NAME'.freeze] << env['PATH_INFO'.freeze] << "\n" if c.include?(?p.freeze)
-
end
-
-
6
challenge << app_config[:secret] if app_config and user
-
6
challenge << args.join(?:.freeze)
-
-
6
if app_config
-
6
control = AccessControl::Application.new(application, user: user)
-
6
secrets = user ? secrets_for(user) : [app_config[:secret]]
-
else
-
control = AccessControl::User.new(user)
-
secrets = secrets_for(user)
-
end
-
-
6
if scope = options[?s.freeze]
-
4
control &&= AccessControl::Scoped.new(scope, control)
-
end
-
-
12
control if secrets.any? { |secret| signed(challenge, secret) == signature }
-
end
-
-
1
def self.secrets_for(user)
-
[
-
Travis::Api::App::AccessToken.new(user: user, app_id: 1), # generated from github token
-
Travis::Api::App::AccessToken.new(user: user, app_id: 0) # used by web
-
]
-
end
-
-
1
def self.signed(challenge, secret)
-
6
OpenSSL::HMAC.hexdigest('sha256'.freeze, secret, challenge)
-
end
-
end
-
end
-
1
require 'travis/api/v3/access_control/generic'
-
-
1
module Travis::API::V3
-
1
class AccessControl::User < AccessControl::Generic
-
1
attr_reader :user, :permissions
-
-
1
def initialize(user)
-
4
@user = user
-
4
@permissions = user.permissions.where(user_id: user.id)
-
4
super()
-
end
-
-
1
protected
-
-
1
def private_repository_visible?(repository)
-
4
permission?(:pull, repository)
-
end
-
-
1
def permission?(type, id)
-
4
id = id.id if id.is_a? ::Repository
-
4
permissions.where(type => true, :repository_id => id).any?
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Travis::API::V3
-
1
class Error < StandardError
-
1
def self.create(default_message = nil, **options)
-
5
options[:default_message] = default_message if default_message
-
38
Class.new(self) { options.each { |key, value| define_singleton_method(key) { value } } }
-
end
-
-
1
def self.status
-
500
-
end
-
-
1
def self.type
-
10
@type ||= name[/[^:]+$/].underscore
-
end
-
-
1
def self.template
-
'%s'.freeze
-
end
-
-
1
def self.default_message
-
@default_message ||= Rack::Utils::HTTP_STATUS_CODES.fetch(status, 'unknown error'.freeze).downcase
-
end
-
-
1
attr_accessor :status, :type, :payload
-
-
1
def initialize(message = self.class.default_message, status: self.class.status, type: self.class.type, **payload)
-
10
if message.is_a? Symbol
-
10
payload[:resource_type] ||= message
-
10
message = self.class.template % message
-
end
-
-
10
self.status, self.type, self.payload = status, type, payload
-
10
super(message)
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class OptIn
-
1
attr_reader :legacy_stack, :prefix, :router, :accept, :version_header
-
-
1
def initialize(legacy_stack, prefix: '/v3', router: Router.new, accept: 'application/vnd.travis-ci.3+', version_header: 'Travis-API-Version')
-
27
@legacy_stack = legacy_stack
-
27
@prefix = prefix
-
27
@router = router
-
27
@accept = accept
-
27
@version_header = "HTTP_#{version_header.upcase.gsub(/\W/, '_')}"
-
end
-
-
1
def call(env)
-
23
return redirect(env) if redirect?(env)
-
-
23
if matched = matching_env(env)
-
23
result = @router.call(matched)
-
23
result, missing = nil, result if cascade?(*result)
-
end
-
-
23
result = result || legacy_stack.call(env)
-
23
pick(result, missing)
-
end
-
-
1
def pick(result, missing)
-
23
return result if missing.nil?
-
10
return result if result[0] != 404
-
10
missing
-
end
-
-
1
def redirect?(env)
-
23
env['PATH_INFO'.freeze] == prefix
-
end
-
-
1
def redirect(env)
-
[307, {'Location'.freeze => env['SCRIPT_NAME'.freeze] + prefix + ?/.freeze, 'Conent-Type'.freeze => 'text/plain'.freeze}, []]
-
end
-
-
1
def cascade?(status, headers, body)
-
23
status % 100 == 4 and headers['X-Cascade'.freeze] == 'pass'.freeze
-
end
-
-
1
def matching_env(env)
-
23
for_v3 = from_prefix(env) || from_accept(env) || from_version_header(env)
-
23
for_v3 == true ? env : for_v3
-
end
-
-
1
def from_prefix(env)
-
23
return unless prefix and env['PATH_INFO'.freeze].start_with?(prefix + ?/.freeze)
-
20
env.merge({
-
'SCRIPT_NAME'.freeze => env['SCRIPT_NAME'.freeze] + prefix,
-
'PATH_INFO'.freeze => env['PATH_INFO'.freeze][prefix.size..-1]
-
})
-
end
-
-
1
def from_accept(env)
-
3
env['HTTP_ACCEPT'.freeze].include?(accept) if accept and env.include?('HTTP_ACCEPT'.freeze)
-
end
-
-
1
def from_version_header(env)
-
2
env[version_header] == '3'.freeze if version_header and env.include?(version_header)
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module Renderer
-
1
extend self
-
-
1
def [](key)
-
18
return key if key.respond_to? :render
-
18
const_get(key.to_s.camelize)
-
end
-
-
1
def format_date(date)
-
16
date && date.strftime('%Y-%m-%dT%H:%M:%SZ')
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module Renderer::Error
-
1
extend self
-
-
1
def render(error)
-
{
-
:@type => 'error'.freeze,
-
:error_type => error.type,
-
:error_message => error.message,
-
**error.payload
-
10
}
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module Renderer::Repository
-
1
DIRECT_ATTRIBUTES = %i[id name slug description github_language private]
-
1
extend self
-
-
1
def render(repository)
-
8
{ :@type => 'repository'.freeze, **direct_attributes(repository), **nested_resources(repository) }
-
end
-
-
1
def direct_attributes(repository)
-
56
DIRECT_ATTRIBUTES.map { |a| [a, repository.public_send(a)] }.to_h
-
end
-
-
1
def nested_resources(repository)
-
{
-
owner: {
-
:@type => repository.owner_type.downcase,
-
:id => repository.owner_id,
-
:login => repository.owner_name
-
},
-
last_build: {
-
:@type => 'build'.freeze,
-
:id => repository.last_build_id,
-
:number => repository.last_build_number,
-
:state => repository.last_build_state.to_s,
-
:duration => repository.last_build_duration,
-
:started_at => Renderer.format_date(repository.last_build_started_at),
-
:finished_at => Renderer.format_date(repository.last_build_finished_at),
-
}
-
8
}
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class Result
-
1
attr_accessor :type, :resource
-
-
1
def initialize(type, resource = [])
-
22
@type, @resource = type, resource
-
end
-
-
1
def respond_to_missing?(method, *)
-
super or method.to_sym == type.to_sym
-
end
-
-
1
def <<(value)
-
1
resource << value
-
1
self
-
end
-
-
1
def render
-
18
Renderer[type].render(resource)
-
end
-
-
1
def method_missing(method, *args)
-
2
return super unless method.to_sym == type.to_sym
-
2
raise ArgumentError, 'wrong number of arguments (1 for 0)'.freeze if args.any?
-
2
resource
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class Router
-
1
include Travis::API::V3
-
1
attr_accessor :routes
-
-
1
def initialize(routes = Routes)
-
27
@routes = routes
-
27
routes.draw_routes
-
end
-
-
1
def call(env)
-
23
return service_index(env) if env['PATH_INFO'.freeze] == ?/.freeze
-
18
access_control = AccessControl.new(env)
-
18
factory, params = routes.factory_for(env['REQUEST_METHOD'.freeze], env['PATH_INFO'.freeze])
-
18
env_params = params(env)
-
-
18
raise NotFound unless factory
-
-
18
service = factory.new(access_control, env_params.merge(params))
-
18
result = service.run
-
8
render(result, env_params)
-
rescue Error => error
-
10
result = Result.new(:error, error)
-
10
V3.response(result.render, 'X-Cascade'.freeze => 'pass'.freeze, status: error.status)
-
end
-
-
1
def render(result, env_params)
-
8
V3.response(result.render)
-
end
-
-
1
def service_index(env)
-
5
ServiceIndex.for(env, routes).render(env)
-
end
-
-
1
def params(env)
-
18
request = Rack::Request.new(env)
-
18
params = request.params
-
18
media_type = request.media_type
-
-
18
if media_type == 'application/json'.freeze or media_type == 'text/json'.freeze
-
request.body.rewind
-
json_params = env['travis.input.json'.freeze] ||= JSON.load(request.body)
-
params.merge! json_params if json_params.is_a? Hash
-
end
-
-
18
params
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module Routes
-
1
require 'travis/api/v3/routes/dsl'
-
1
extend DSL
-
-
1
resource :repository do
-
1
route '/repo/:id'
-
1
get :find_repository
-
end
-
end
-
end
-
1
require 'travis/api/v3/routes/resource'
-
-
1
module Travis::API::V3
-
1
module Routes::DSL
-
1
def routes
-
126
@routes ||= {}
-
end
-
-
1
def resources
-
32
@resources ||= []
-
end
-
-
1
def current_resource
-
3
@current_resource ||= nil
-
end
-
-
1
def resource(type, &block)
-
1
resource = Routes::Resource.new(type)
-
1
with_resource(resource, &block)
-
1
resources << resource
-
end
-
-
1
def with_resource(resource)
-
1
resource_was, @current_resource = current_resource, resource
-
1
yield
-
ensure
-
1
@current_resource = resource_was
-
end
-
-
1
def route(value)
-
1
current_resource.route = value
-
end
-
-
1
def get(*args)
-
1
current_resource.add_service('GET'.freeze, *args)
-
end
-
-
1
def post(*args)
-
current_resource.add_service('POST'.freeze, *args)
-
end
-
-
1
def draw_routes
-
27
resources.each do |resource|
-
27
prefix = resource.route
-
27
resource.services.each do |(request_method, sub_route), service|
-
27
route = sub_route ? prefix + sub_route : prefix
-
27
routes[route] ||= {}
-
27
routes[route][request_method] = Services[service]
-
end
-
end
-
27
self.routes.replace(routes)
-
end
-
-
1
def factory_for(request_method, path)
-
18
routes.each do |route, method_map|
-
18
next unless params = route.params(path)
-
18
raise MethodNotAllowed unless factory = method_map[request_method]
-
18
return [factory, params]
-
end
-
nil # nothing matched
-
end
-
end
-
end
-
1
require 'mustermann'
-
-
1
module Travis::API::V3
-
1
class Routes::Resource
-
1
attr_accessor :identifier, :route, :services
-
-
1
def initialize(identifier)
-
1
@identifier = identifier
-
1
@services = {}
-
end
-
-
1
def add_service(request_method, service, sub_route = nil)
-
1
sub_route &&= Mustermann.new(sub_route)
-
1
services[[request_method, sub_route]] = service
-
end
-
-
1
def route=(value)
-
1
@route = value ? Mustermann.new(value) : value
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class Service
-
1
def self.required_params
-
@required_params ||= []
-
end
-
-
1
def self.params(*list, optional: false)
-
1
@params ||= []
-
1
list.each do |param|
-
3
param = param.to_s
-
39
define_method(param) { params[param] }
-
3
required_params << param unless optional
-
3
@params << param
-
end
-
1
@params
-
end
-
-
1
attr_accessor :access_control, :params
-
-
1
def initialize(access_control, params)
-
18
@access_control = access_control
-
18
@params = params
-
end
-
-
1
def required_params?
-
required_params.all? { |param| params.include? param }
-
end
-
-
1
def required_params
-
self.class.required_params
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class ServiceIndex
-
1
ALLOW_POST = ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data']
-
1
@index_cache = {}
-
-
1
def self.for(env, routes)
-
5
access_factory = AccessControl.new(env).class
-
5
prefix = env['SCRIPT_NAME'.freeze]
-
5
@index_cache[[access_factory, routes, prefix]] ||= new(access_factory, routes, prefix)
-
end
-
-
1
attr_reader :access_factory, :routes, :json_home_response, :json_response, :prefix
-
-
1
def initialize(access_factory, routes, prefix)
-
2
@prefix = prefix || ''
-
2
@access_factory, @routes = access_factory, routes
-
2
@json_response = V3.response(render_json, content_type: 'application/json'.freeze)
-
2
@json_home_response = V3.response(render_json_home, content_type: 'application/json-home'.freeze)
-
end
-
-
1
def render(env)
-
5
json_home?(env) ? json_home_response : json_response
-
end
-
-
1
def render_json
-
2
resources = { }
-
2
routes.resources.each do |resource|
-
2
resources[resource.identifier] ||= {}
-
2
resource.services.each do |(request_method, sub_route), service|
-
2
service &&= service.to_s.sub(/_#{resource.identifier}$/, ''.freeze)
-
2
list = resources[resource.identifier][service] ||= []
-
2
pattern = sub_route ? resource.route + sub_route : resource.route
-
2
pattern.to_templates.each do |template|
-
2
list << { 'request-method'.freeze => request_method, 'uri-template'.freeze => prefix + template }
-
end
-
end
-
end
-
2
{ resources: resources }
-
end
-
-
1
def render_json_home
-
2
relations = {}
-
-
2
routes.resources.each do |resource|
-
2
resource.services.each do |(request_method, sub_route), service|
-
2
service &&= service.to_s.sub(/_#{resource.identifier}$/, ''.freeze)
-
2
pattern = sub_route ? resource.route + sub_route : resource.route
-
2
relation = "http://schema.travis-ci.com/rel/#{resource.identifier}/#{service}"
-
2
pattern.to_templates.each do |template|
-
2
relations[relation] ||= {}
-
2
relations[relation][template] ||= { allow: [], vars: template.scan(/{\+?([^}]+)}/).flatten }
-
2
relations[relation][template][:allow] << request_method
-
end
-
end
-
end
-
-
2
nested_relations = {}
-
2
relations.delete_if do |relation, request_map|
-
2
next if request_map.size < 2
-
common_vars = request_map.values.map { |e| e[:vars] }.inject(:&)
-
request_map.each do |template, payload|
-
special_vars = payload[:vars] - common_vars
-
schema = special_vars.any? ? "#{relation}/by_#{special_vars.join(?_)}" : relation
-
nested_relations[schema] = { template => payload }
-
end
-
end
-
2
relations.merge! nested_relations
-
-
2
resources = relations.map do |relation, payload|
-
2
template, payload = payload.first
-
2
hints = { 'allow' => payload[:allow] }
-
2
hints['accept-post'] = ALLOW_POST if payload[:allow].include? 'POST'
-
2
hints['accept-patch'] = ALLOW_POST if payload[:allow].include? 'PATCH'
-
2
hints['accept-put'] = ALLOW_POST if payload[:allow].include? 'PUT'
-
2
hints['representations'] = ['application/json', 'application/vnd.travis-ci.3+json']
-
2
[relation, {
-
'href-template' => prefix + template,
-
2
'href-vars' => Hash[payload[:vars].map { |var| [var, "http://schema.travis-ci.com/param/#{var}"] }],
-
'hints' => hints
-
}]
-
end
-
-
2
{ resources: Hash[resources] }
-
end
-
-
1
def json_home?(env)
-
5
env['HTTP_ACCEPT'.freeze] == 'application/json-home'.freeze
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
module Services
-
1
def self.[](key)
-
27
return key if key.respond_to? :new
-
27
const_get(key.to_s.camelize)
-
end
-
end
-
end
-
1
module Travis::API::V3
-
1
class Services::FindRepository < Service
-
1
params :id, :github_id, :slug, optional: true
-
-
1
def run
-
18
raise NotFound, :repository unless repository and access_control.visible? repository
-
8
Result.new(:repository, repository)
-
end
-
-
1
def repository
-
42
raise EntityMissing, :repository if defined?(@repository) and @repository.nil?
-
42
@repository ||= find_repository
-
end
-
-
1
def find_repository
-
18
return ::Repository.find_by_id(id) if id
-
return ::Repository.find_by_github_id(github_id) if github_id
-
return ::Repository.by_slug(slug).first if slug
-
raise WrongParams
-
end
-
end
-
end
-
1
class PrivateKey
-
1
attr_reader :key
-
1
def initialize(key)
-
@key = key
-
end
-
-
1
def fingerprint
-
rsa_key = OpenSSL::PKey::RSA.new(key)
-
public_ssh_rsa = "\x00\x00\x00\x07ssh-rsa" + rsa_key.e.to_s(0) + rsa_key.n.to_s(0)
-
OpenSSL::Digest::MD5.new(public_ssh_rsa).hexdigest.scan(/../).join(':')
-
end
-
-
1
def inspect
-
"<PrivateKey #{fingerprint}>"
-
end
-
end