Merge pull request #117 from travis-ci/ps-ssh-keys
Add ssh keys to settings API
This commit is contained in:
commit
c42335a286
2
Gemfile
2
Gemfile
|
@ -9,6 +9,7 @@ gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs', require: nil, ref: '
|
|||
gem 'sinatra'
|
||||
gem 'sinatra-contrib', require: nil #github: 'sinatra/sinatra-contrib', require: nil
|
||||
|
||||
gem 'active_model_serializers'
|
||||
gem 'unicorn'
|
||||
gem 'sentry-raven', github: 'getsentry/raven-ruby'
|
||||
gem 'yard-sinatra', github: 'rkh/yard-sinatra'
|
||||
|
@ -33,7 +34,6 @@ end
|
|||
group :development do
|
||||
gem 'foreman'
|
||||
gem 'rerun'
|
||||
# gem 'debugger'
|
||||
gem 'rb-fsevent', '~> 0.9.1'
|
||||
end
|
||||
|
||||
|
|
19
Gemfile.lock
19
Gemfile.lock
|
@ -31,12 +31,13 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-core.git
|
||||
revision: dd94b9d19ac4cc33f383f740194ba1b3c6a73d3a
|
||||
revision: d2a19ee99f543eb2db376390847b682caa960b7f
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.12)
|
||||
activerecord (~> 3.2.12)
|
||||
coder (~> 0.4.0)
|
||||
coercible (~> 1.0.0)
|
||||
data_migrations (~> 0.0.1)
|
||||
gh
|
||||
hashr (~> 0.0.19)
|
||||
|
@ -103,6 +104,8 @@ GEM
|
|||
rack-cache (~> 1.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.2.1)
|
||||
active_model_serializers (0.8.1)
|
||||
activemodel (>= 3.0)
|
||||
activemodel (3.2.17)
|
||||
activesupport (= 3.2.17)
|
||||
builder (~> 3.0.0)
|
||||
|
@ -114,9 +117,9 @@ GEM
|
|||
activesupport (3.2.17)
|
||||
i18n (~> 0.6, >= 0.6.4)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.5)
|
||||
addressable (2.3.6)
|
||||
arel (3.0.3)
|
||||
atomic (1.1.15)
|
||||
atomic (1.1.16)
|
||||
avl_tree (1.1.3)
|
||||
backports (2.8.2)
|
||||
builder (3.0.4)
|
||||
|
@ -126,6 +129,8 @@ GEM
|
|||
timers (>= 1.0.0)
|
||||
coder (0.4.0)
|
||||
coderay (1.1.0)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
connection_pool (0.9.3)
|
||||
daemons (1.1.9)
|
||||
dalli (2.6.4)
|
||||
|
@ -133,6 +138,8 @@ GEM
|
|||
activerecord
|
||||
rake
|
||||
database_cleaner (0.8.0)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
diff-lcs (1.2.5)
|
||||
dotenv (0.9.0)
|
||||
erubis (2.7.0)
|
||||
|
@ -178,7 +185,7 @@ GEM
|
|||
mime-types (1.25.1)
|
||||
mocha (0.14.0)
|
||||
metaclass (~> 0.0.1)
|
||||
multi_json (1.9.0)
|
||||
multi_json (1.9.2)
|
||||
multipart-post (2.0.0)
|
||||
net-http-persistent (2.9.4)
|
||||
net-http-pipeline (1.0.1)
|
||||
|
@ -199,7 +206,7 @@ GEM
|
|||
rack (>= 0.4)
|
||||
rack-protection (1.5.1)
|
||||
rack
|
||||
rack-ssl (1.3.3)
|
||||
rack-ssl (1.3.4)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
|
@ -268,6 +275,7 @@ GEM
|
|||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.14.6)
|
||||
thread_safe (0.3.3)
|
||||
tilt (1.4.1)
|
||||
timers (1.1.0)
|
||||
treetop (1.4.15)
|
||||
|
@ -285,6 +293,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_model_serializers
|
||||
bunny (~> 0.8.0)
|
||||
dalli
|
||||
database_cleaner (~> 0.8.0)
|
||||
|
|
|
@ -18,6 +18,7 @@ require 'sidekiq'
|
|||
require 'metriks/reporter/logger'
|
||||
require 'travis/support/log_subscriber/active_record_metrics'
|
||||
require 'fileutils'
|
||||
require 'travis/api/v2/http'
|
||||
|
||||
# Rack class implementing the HTTP API.
|
||||
# Instances respond to #call.
|
||||
|
@ -113,7 +114,13 @@ module Travis::Api
|
|||
use Travis::Api::App::Middleware::Metriks
|
||||
use Travis::Api::App::Middleware::Rewrite
|
||||
|
||||
Endpoint.subclasses.each { |e| map(e.prefix) { run(e.new) } }
|
||||
SettingsEndpoint.subclass :ssh_keys
|
||||
|
||||
Endpoint.subclasses.each do |e|
|
||||
next if e == SettingsEndpoint # TODO: add something like abstract? method to check if
|
||||
# class should be registered
|
||||
map(e.prefix) { run(e.new) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class Travis::Api::App
|
|||
|
||||
# Get settings for a given repository
|
||||
#
|
||||
get '/:id/settings' do
|
||||
get '/:id/settings', scope: :private do
|
||||
settings = service(:find_repo_settings, params).run
|
||||
if settings
|
||||
respond_with({ settings: settings.obfuscated }, version: :v2)
|
||||
|
@ -52,7 +52,7 @@ class Travis::Api::App
|
|||
end
|
||||
end
|
||||
|
||||
patch '/:id/settings' do
|
||||
patch '/:id/settings', scope: :private do
|
||||
payload = JSON.parse request.body.read
|
||||
|
||||
if payload['settings'].blank? || !payload['settings'].is_a?(Hash)
|
||||
|
|
94
lib/travis/api/app/endpoint/setting_endpoint.rb
Normal file
94
lib/travis/api/app/endpoint/setting_endpoint.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class SettingsEndpoint < Endpoint
|
||||
set(:prefix) { "/settings/" << name[/[^:]+$/].underscore }
|
||||
|
||||
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
|
||||
def subclass(name)
|
||||
class_name = name.to_s.camelize
|
||||
if Travis::Api::App.const_defined?(class_name)
|
||||
Travis::Api::App.const_get(class_name)
|
||||
else
|
||||
klass = create_settings_class(name)
|
||||
Travis::Api::App.const_set(class_name, klass)
|
||||
klass
|
||||
end
|
||||
end
|
||||
|
||||
def create_settings_class(name)
|
||||
klass = Class.new(self) do
|
||||
define_method(:name) { name }
|
||||
get("/", scope: :private) do index end
|
||||
get("/:id", scope: :private) do show end
|
||||
post("/", scope: :private) do create end
|
||||
patch("/:id", scope: :private) do update end
|
||||
delete("/:id", scope: :private) do destroy end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Rails style methods for easy overriding
|
||||
def index
|
||||
respond_with(collection, type: name, version: :v2)
|
||||
end
|
||||
|
||||
def show
|
||||
respond_with(record, type: singular_name, version: :v2)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def destroy
|
||||
record = collection.destroy(params[:id]) || record_not_found
|
||||
repo_settings.save
|
||||
respond_with(record, type: singular_name, version: :v2)
|
||||
end
|
||||
|
||||
def singular_name
|
||||
name.to_s.singularize
|
||||
end
|
||||
|
||||
def collection
|
||||
@collection ||= repo_settings.send(name)
|
||||
end
|
||||
|
||||
# This method can't be called "settings" because it clashes with
|
||||
# Sinatra's method
|
||||
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
|
||||
|
||||
def record
|
||||
collection.find(params[:id]) || record_not_found
|
||||
end
|
||||
|
||||
def record_not_found
|
||||
halt(404, { error: "Could not find a requested setting" })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -37,7 +37,9 @@ class Travis::Api::App
|
|||
end
|
||||
|
||||
def redirect_v1_named_repo_path
|
||||
force_redirect("/repositories#{$1}.#{env['travis.format']}") if request.path =~ V1_REPO_URL
|
||||
if request.path =~ V1_REPO_URL
|
||||
force_redirect("/repositories#{$1}.#{env['travis.format']}")
|
||||
end
|
||||
end
|
||||
|
||||
def force_redirect(path)
|
||||
|
|
|
@ -27,7 +27,13 @@ class Travis::Api::App
|
|||
end
|
||||
|
||||
def result
|
||||
builder ? builder.new(resource, params).data : basic_type_resource
|
||||
if builder
|
||||
p = params
|
||||
p[:root] = options[:type] if options[:type]
|
||||
builder.new(resource, p).data
|
||||
else
|
||||
basic_type_resource
|
||||
end
|
||||
end
|
||||
|
||||
def builder
|
||||
|
|
23
lib/travis/api/serializer.rb
Normal file
23
lib/travis/api/serializer.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
require 'active_model_serializers'
|
||||
|
||||
module Travis
|
||||
module Api
|
||||
class Serializer < ActiveModel::Serializer
|
||||
def data
|
||||
as_json
|
||||
end
|
||||
end
|
||||
|
||||
class ArraySerializer < ActiveModel::ArraySerializer
|
||||
def data
|
||||
as_json
|
||||
end
|
||||
|
||||
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,3 +1,5 @@
|
|||
require 'travis/api/serializer'
|
||||
|
||||
module Travis
|
||||
module Api
|
||||
module V2
|
||||
|
@ -20,7 +22,10 @@ module Travis
|
|||
require 'travis/api/v2/http/requests'
|
||||
require 'travis/api/v2/http/request'
|
||||
require 'travis/api/v2/http/ssl_key'
|
||||
require 'travis/api/v2/http/ssh_key'
|
||||
require 'travis/api/v2/http/ssh_keys'
|
||||
require 'travis/api/v2/http/user'
|
||||
require 'travis/api/v2/http/validation_error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
1
lib/travis/api/v2/http/error.rb
Normal file
1
lib/travis/api/v2/http/error.rb
Normal file
|
@ -0,0 +1 @@
|
|||
|
11
lib/travis/api/v2/http/ssh_key.rb
Normal file
11
lib/travis/api/v2/http/ssh_key.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module Travis
|
||||
module Api
|
||||
module V2
|
||||
module Http
|
||||
class SshKey < Travis::Api::Serializer
|
||||
attributes :id, :name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
2
lib/travis/api/v2/http/ssh_keys.rb
Normal file
2
lib/travis/api/v2/http/ssh_keys.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
class Travis::Api::V2::Http::SshKeys < Travis::Api::ArraySerializer
|
||||
end
|
39
lib/travis/api/v2/http/validation_error.rb
Normal file
39
lib/travis/api/v2/http/validation_error.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
module Travis
|
||||
module Api
|
||||
module V2
|
||||
module Http
|
||||
class ValidationError
|
||||
attr_reader :resource
|
||||
|
||||
def initialize(resource, options = {})
|
||||
@resource = resource
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def code(error_code)
|
||||
case error_code
|
||||
when :blank
|
||||
'missing_field'
|
||||
else
|
||||
error_code.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
177
spec/integration/settings_endpoint_spec.rb
Normal file
177
spec/integration/settings_endpoint_spec.rb
Normal file
|
@ -0,0 +1,177 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::SettingsEndpoint do
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
before do
|
||||
model_class = Class.new(Repository::Settings::Model) do
|
||||
field :name
|
||||
field :secret, encrypted: true
|
||||
|
||||
validates :name, presence: true
|
||||
validates :secret, presence: true
|
||||
end
|
||||
collection_class = Class.new(Repository::Settings::Collection) do
|
||||
model model_class
|
||||
end
|
||||
Repository::Settings.class_eval do
|
||||
register :items, collection_class
|
||||
end
|
||||
serializer_class = Class.new(Travis::Api::Serializer) do
|
||||
attributes :id, :name
|
||||
end
|
||||
Travis::Api::V2::Http.const_set(:Item, serializer_class)
|
||||
Travis::Api::V2::Http.const_set(:Items, Travis::Api::ArraySerializer)
|
||||
|
||||
add_settings_endpoint :items
|
||||
end
|
||||
|
||||
after do
|
||||
Travis::Api::App.send :remove_const, :Items
|
||||
Travis::Api::V2::Http.send :remove_const, :Items
|
||||
Travis::Api::V2::Http.send :remove_const, :Item
|
||||
end
|
||||
|
||||
describe 'with authenticated user' do
|
||||
let(:user) { User.where(login: 'svenfuchs').first }
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json', 'HTTP_AUTHORIZATION' => "token #{token}" } }
|
||||
|
||||
before { user.permissions.create!(:repository_id => repo.id, :admin => true, :push => true) }
|
||||
|
||||
describe 'GET /items/:id' do
|
||||
it 'returns an item' do
|
||||
settings = repo.settings
|
||||
item = settings.items.create(name: 'an item', secret: 'TEH SECRET')
|
||||
settings.save
|
||||
|
||||
response = get '/settings/items/' + item.id, { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['item']['name'].should == 'an item'
|
||||
json['item']['id'].should == item.id
|
||||
json['item'].should_not have_key('secret')
|
||||
end
|
||||
|
||||
it 'returns 404 if item can\'t be found' do
|
||||
response = get '/settings/items/123', { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['error'].should == "Could not find a requested setting"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /items' do
|
||||
it 'returns items list' do
|
||||
settings = repo.settings
|
||||
settings.items.create(name: 'an item', secret: 'TEH SECRET')
|
||||
settings.save
|
||||
|
||||
response = get '/settings/items', { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['items'].should have(1).items
|
||||
item = json['items'].first
|
||||
item['name'].should == 'an item'
|
||||
item['id'].should_not be_nil
|
||||
item.should_not have_key('secret')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /items' do
|
||||
it 'creates a new item' do
|
||||
body = { item: { name: 'foo', secret: 'TEH SECRET' } }.to_json
|
||||
response = post "/settings/items?repository_id=#{repo.id}", body, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['item']['name'].should == 'foo'
|
||||
json['item']['id'].should_not be_nil
|
||||
json['item'].should_not have_key('secret')
|
||||
|
||||
item = repo.reload.settings.items.first
|
||||
item.id.should_not be_nil
|
||||
item.name.should == 'foo'
|
||||
item.secret.decrypt.should == 'TEH SECRET'
|
||||
end
|
||||
|
||||
it 'returns error message if item is invalid' do
|
||||
response = post "/settings/items?repository_id=#{repo.id}", '{}', headers
|
||||
response.status.should == 422
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
json['message'].should == 'Validation failed'
|
||||
json['errors'].should == [{
|
||||
'field' => 'name',
|
||||
'code' => 'missing_field'
|
||||
}, {
|
||||
'field' => 'secret',
|
||||
'code' => 'missing_field'
|
||||
}]
|
||||
|
||||
repo.reload.settings.items.length.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /items/:id' do
|
||||
it 'should update an item' do
|
||||
settings = repo.settings
|
||||
item = settings.items.create(name: 'an item', secret: 'TEH SECRET')
|
||||
settings.save
|
||||
|
||||
body = { item: { name: 'a new name', secret: 'a new secret' } }.to_json
|
||||
response = patch "/settings/items/#{item.id}?repository_id=#{repo.id}", body, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['item']['name'].should == 'a new name'
|
||||
json['item']['id'].should == item.id
|
||||
json['item'].should_not have_key('secret')
|
||||
|
||||
updated_item = repo.reload.settings.items.find(item.id)
|
||||
updated_item.id.should == item.id
|
||||
updated_item.name.should == 'a new name'
|
||||
updated_item.secret.decrypt.should == 'a new secret'
|
||||
end
|
||||
|
||||
it 'returns an error message if item is invalid' do
|
||||
settings = repo.settings
|
||||
item = settings.items.create(name: 'an item', secret: 'TEH SECRET')
|
||||
settings.save
|
||||
|
||||
body = { item: { name: '' } }.to_json
|
||||
response = patch "/settings/items/#{item.id}?repository_id=#{repo.id}", body, headers
|
||||
response.status.should == 422
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
json['message'].should == 'Validation failed'
|
||||
json['errors'].should == [{
|
||||
'field' => 'name',
|
||||
'code' => 'missing_field'
|
||||
}]
|
||||
|
||||
updated_item = repo.reload.settings.items.find(item.id)
|
||||
updated_item.id.should == item.id
|
||||
updated_item.name.should == 'an item'
|
||||
updated_item.secret.decrypt.should == 'TEH SECRET'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /items/:id' do
|
||||
it 'should delete an item' do
|
||||
settings = repo.settings
|
||||
item = settings.items.create(name: 'an item', secret: 'TEH SECRET')
|
||||
settings.save
|
||||
|
||||
params = { repository_id: repo.id }
|
||||
response = delete '/settings/items/' + item.id, params, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['item']['name'].should == 'an item'
|
||||
json['item']['id'].should == item.id
|
||||
json['item'].should_not have_key('secret')
|
||||
|
||||
repo.reload.settings.items.should have(0).items
|
||||
end
|
||||
|
||||
it 'returns 404 if item can\'t be found' do
|
||||
response = delete '/settings/items/123', { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['error'].should == "Could not find a requested setting"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,21 +25,18 @@ describe 'Repos' do
|
|||
end
|
||||
|
||||
it 'allows to update settings' do
|
||||
json = { 'settings' => { 'a-new-setting' => 'value' } }.to_json
|
||||
json = { 'settings' => { 'build_pushes' => false } }.to_json
|
||||
response = patch "repos/#{repo.id}/settings", json, headers
|
||||
|
||||
repo.reload.settings['a-new-setting'].should == 'value'
|
||||
repo.reload.settings['build_pushes'].should == false
|
||||
|
||||
body = JSON.parse(response.body)
|
||||
body['settings']['a-new-setting'].should == 'value'
|
||||
body['settings']['build_pushes'].should == false
|
||||
end
|
||||
|
||||
it 'allows to get settings' do
|
||||
repo.settings.replace('foo' => { 'type' => 'password', 'value' => 'abc123' })
|
||||
repo.save
|
||||
|
||||
response = get "repos/#{repo.id}/settings", {}, headers
|
||||
settings = Repository::Settings.defaults.deep_merge({ 'foo' => { 'type' => 'password', 'value' => '∗∗∗∗∗∗' } })
|
||||
settings = Repository::Settings.defaults
|
||||
JSON.parse(response.body).should == { 'settings' => settings }
|
||||
end
|
||||
end
|
||||
|
|
146
spec/integration/v2/settings/ssh_keys_spec.rb
Normal file
146
spec/integration/v2/settings/ssh_keys_spec.rb
Normal file
|
@ -0,0 +1,146 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::SettingsEndpoint do
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
describe 'with authenticated user' do
|
||||
let(:user) { User.where(login: 'svenfuchs').first }
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json', 'HTTP_AUTHORIZATION' => "token #{token}" } }
|
||||
|
||||
before { user.permissions.create!(:repository_id => repo.id, :admin => true, :push => true) }
|
||||
|
||||
describe 'GET /ssh_keys/:id' do
|
||||
it 'returns an item' do
|
||||
settings = repo.settings
|
||||
record = settings.ssh_keys.create(name: 'key for my repo', content: 'the key')
|
||||
settings.save
|
||||
|
||||
response = get '/settings/ssh_keys/' + record.id, { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['ssh_key']['name'].should == 'key for my repo'
|
||||
json['ssh_key']['id'].should == record.id
|
||||
json['ssh_key'].should_not have_key('content')
|
||||
end
|
||||
|
||||
it 'returns 404 if ssh_key can\'t be found' do
|
||||
response = get '/settings/ssh_keys/123', { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['error'].should == "Could not find a requested setting"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /settings/ssh_keys' do
|
||||
it 'returns a list of ssh_keys' do
|
||||
settings = repo.settings
|
||||
record = settings.ssh_keys.create(name: 'key for my repo', content: 'the key')
|
||||
settings.save
|
||||
|
||||
response = get '/settings/ssh_keys', { repository_id: repo.id }, headers
|
||||
response.should be_successful
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
key = json['ssh_keys'].first
|
||||
key['name'].should == 'key for my repo'
|
||||
key['id'].should == record.id
|
||||
key.should_not have_key('content')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /settings/ssh_keys' do
|
||||
it 'creates a new key' do
|
||||
body = { ssh_key: { name: 'foo', content: 'content' } }.to_json
|
||||
response = post "/settings/ssh_keys?repository_id=#{repo.id}", body, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['ssh_key']['name'].should == 'foo'
|
||||
json['ssh_key']['id'].should_not be_nil
|
||||
json['ssh_key'].should_not have_key('content')
|
||||
|
||||
ssh_key = repo.reload.settings.ssh_keys.first
|
||||
ssh_key.id.should_not be_nil
|
||||
ssh_key.name.should == 'foo'
|
||||
ssh_key.content.decrypt.should == 'content'
|
||||
end
|
||||
|
||||
it 'returns error message if a key is invalid' do
|
||||
response = post "/settings/ssh_keys?repository_id=#{repo.id}", '{}', headers
|
||||
response.status.should == 422
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
json['message'].should == 'Validation failed'
|
||||
json['errors'].should == [{
|
||||
'field' => 'name',
|
||||
'code' => 'missing_field'
|
||||
}]
|
||||
|
||||
repo.reload.settings.ssh_keys.length.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /settings/ssh_keys/:id' do
|
||||
it 'should update a key' do
|
||||
settings = repo.settings
|
||||
ssh_key = settings.ssh_keys.create(name: 'foo', content: 'content')
|
||||
settings.save
|
||||
|
||||
body = { ssh_key: { name: 'bar', content: 'a new content' } }.to_json
|
||||
response = patch "/settings/ssh_keys/#{ssh_key.id}?repository_id=#{repo.id}", body, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['ssh_key']['name'].should == 'bar'
|
||||
json['ssh_key']['id'].should == ssh_key.id
|
||||
json['ssh_key'].should_not have_key('content')
|
||||
|
||||
updated_ssh_key = repo.reload.settings.ssh_keys.find(ssh_key.id)
|
||||
updated_ssh_key.id.should == ssh_key.id
|
||||
updated_ssh_key.name.should == 'bar'
|
||||
updated_ssh_key.content.decrypt.should == 'a new content'
|
||||
end
|
||||
|
||||
it 'returns an error message if ssh_key is invalid' do
|
||||
settings = repo.settings
|
||||
ssh_key = settings.ssh_keys.create(name: 'foo', content: 'content')
|
||||
settings.save
|
||||
|
||||
body = { ssh_key: { name: '' } }.to_json
|
||||
response = patch "/settings/ssh_keys/#{ssh_key.id}?repository_id=#{repo.id}", body, headers
|
||||
response.status.should == 422
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
json['message'].should == 'Validation failed'
|
||||
json['errors'].should == [{
|
||||
'field' => 'name',
|
||||
'code' => 'missing_field'
|
||||
}]
|
||||
|
||||
updated_ssh_key = repo.reload.settings.ssh_keys.find(ssh_key.id)
|
||||
updated_ssh_key.id.should == ssh_key.id
|
||||
updated_ssh_key.name.should == 'foo'
|
||||
updated_ssh_key.content.decrypt.should == 'content'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /ssh_keys/:id' do
|
||||
it 'should delete an ssh_key' do
|
||||
settings = repo.settings
|
||||
ssh_key = settings.ssh_keys.create(name: 'foo', content: 'content')
|
||||
settings.save
|
||||
|
||||
params = { repository_id: repo.id }
|
||||
response = delete '/settings/ssh_keys/' + ssh_key.id, params, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['ssh_key']['name'].should == 'foo'
|
||||
json['ssh_key']['id'].should == ssh_key.id
|
||||
json['ssh_key'].should_not have_key('content')
|
||||
|
||||
repo.reload.settings.ssh_keys.should have(0).ssh_keys
|
||||
end
|
||||
|
||||
it 'returns 404 if ssh_key can\'t be found' do
|
||||
response = delete '/settings/ssh_keys/123', { repository_id: repo.id }, headers
|
||||
json = JSON.parse(response.body)
|
||||
json['error'].should == "Could not find a requested setting"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,6 +26,11 @@ module TestHelpers
|
|||
@custom_endpoints ||= []
|
||||
end
|
||||
|
||||
def add_settings_endpoint(name)
|
||||
Travis::Api::App::SettingsEndpoint.subclass(name)
|
||||
set_app Travis::Api::App.new
|
||||
end
|
||||
|
||||
def add_endpoint(prefix, &block)
|
||||
endpoint = Sinatra.new(Travis::Api::App::Endpoint, &block)
|
||||
endpoint.set(prefix: prefix)
|
||||
|
|
Loading…
Reference in New Issue
Block a user