From 4005760c8c3cc6fd16ac4eff7c5561565ceec096 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 21 Jul 2014 17:09:32 +0200 Subject: [PATCH] Add SingletonSettingsEndpoint This class allows to easily add an endpoint for a nested model in settings. --- .../endpoint/singleton_settings_endpoint.rb | 46 +++++++ .../singleton_settings_endpoint_spec.rb | 123 ++++++++++++++++++ spec/spec_helper.rb | 8 +- 3 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 lib/travis/api/app/endpoint/singleton_settings_endpoint.rb create mode 100644 spec/integration/singleton_settings_endpoint_spec.rb diff --git a/lib/travis/api/app/endpoint/singleton_settings_endpoint.rb b/lib/travis/api/app/endpoint/singleton_settings_endpoint.rb new file mode 100644 index 00000000..d87afe8a --- /dev/null +++ b/lib/travis/api/app/endpoint/singleton_settings_endpoint.rb @@ -0,0 +1,46 @@ +require 'travis/api/app' +require 'travis/api/app/endpoint/setting_endpoint' + +class Travis::Api::App + class SingletonSettingsEndpoint < SettingsEndpoint + class << self + def create_settings_class(name) + klass = Class.new(self) do + define_method(:name) { name } + get("/", scope: :private) do show end + patch("/", scope: :private) do update end + delete("/", scope: :private) do destroy end + end + end + end + + 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 + + def destroy + record = parent.delete(name) + repo_settings.save + respond_with(record, type: singular_name, version: :v2) + end + + def record + parent.get(name) || record_not_found + end + + def parent + repo_settings + end + + def record_not_found + halt(404, { error: "Could not find a requested setting" }) + end + end +end diff --git a/spec/integration/singleton_settings_endpoint_spec.rb b/spec/integration/singleton_settings_endpoint_spec.rb new file mode 100644 index 00000000..061c7baa --- /dev/null +++ b/spec/integration/singleton_settings_endpoint_spec.rb @@ -0,0 +1,123 @@ +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 + attribute :name, String + attribute :secret, Travis::Settings::EncryptedValue + + validates :name, presence: true + validates :secret, presence: true + end + Repository::Settings.class_eval do + attribute :item, model_class + end + serializer_class = Class.new(Travis::Api::Serializer) do + attributes :name + end + Travis::Api::V2::Http.const_set(:Item, serializer_class) + + add_settings_endpoint :item, singleton: true + end + + after do + Travis::Api::App.send :remove_const, :Item + 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 /item' do + it 'returns an item' do + settings = repo.settings + item = settings.create(:item, name: 'an item', secret: 'TEH SECRET') + settings.save + + response = get '/settings/item', { repository_id: repo.id }, headers + json = JSON.parse(response.body) + json['item']['name'].should == 'an item' + json['item'].should_not have_key('secret') + end + + it 'returns 404 if item can\'t be found' do + response = get '/settings/item', { repository_id: repo.id }, headers + json = JSON.parse(response.body) + json['error'].should == "Could not find a requested setting" + end + end + + describe 'PATCH /item' do + it 'should update an item' do + settings = repo.settings + item = settings.create(:item, name: 'an item', secret: 'TEH SECRET') + settings.save + + body = { item: { name: 'a new name', secret: 'a new secret' } }.to_json + response = patch "/settings/item?repository_id=#{repo.id}", body, headers + json = JSON.parse(response.body) + json['item']['name'].should == 'a new name' + json['item'].should_not have_key('secret') + + updated_item = repo.reload.settings.item + updated_item.name.should == 'a new name' + updated_item.secret.decrypt.should == 'a new secret' + end + + it 'should create an item if it does not exist' do + repo.settings.item.should be_nil + + body = { item: { name: 'a name', secret: 'a secret' } }.to_json + response = patch "/settings/item?repository_id=#{repo.id}", body, headers + json = JSON.parse(response.body) + json['item']['name'].should == 'a name' + json['item'].should_not have_key('secret') + + item = repo.reload.settings.item + item.name.should == 'a name' + item.secret.decrypt.should == 'a secret' + end + + it 'returns an error message if item is invalid' do + body = { item: { name: '' } }.to_json + response = patch "/settings/item?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' + }, { + 'field' => 'secret', + 'code' => 'missing_field' + }] + + repo.reload.settings.item.should be_nil + end + end + + describe 'DELETE /item' do + it 'should delete an item' do + settings = repo.settings + item = settings.create(:item, name: 'an item') + settings.save + + params = { repository_id: repo.id } + response = delete '/settings/item', params, headers + json = JSON.parse(response.body) + json['item']['name'].should == 'an item' + json['item'].should_not have_key('secret') + + repo.reload.settings.item.should be_nil + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 197cb126..c98039c2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,8 +26,12 @@ module TestHelpers @custom_endpoints ||= [] end - def add_settings_endpoint(name) - Travis::Api::App::SettingsEndpoint.subclass(name) + def add_settings_endpoint(name, options = {}) + if options[:singleton] + Travis::Api::App::SingletonSettingsEndpoint.subclass(name) + else + Travis::Api::App::SettingsEndpoint.subclass(name) + end set_app Travis::Api::App.new end