Merge pull request #699 from warpc/698-rest-api-for-advisories

[refs #698]: REST API for Advisories
This commit is contained in:
Vladimir Sharshov 2012-10-19 07:47:46 -07:00
commit d6db260385
12 changed files with 283 additions and 37 deletions

View File

@ -5,8 +5,6 @@ class AdvisoriesController < ApplicationController
load_resource :find_by => :advisory_id load_resource :find_by => :advisory_id
authorize_resource authorize_resource
before_filter :fetch_packages_info, :only => [:show]
def index def index
@advisories = @advisories.scoped(:include => :platforms) @advisories = @advisories.scoped(:include => :platforms)
@advisories = @advisories.search_by_id(params[:q]) if params[:q] @advisories = @advisories.search_by_id(params[:q]) if params[:q]
@ -18,6 +16,7 @@ class AdvisoriesController < ApplicationController
end end
def show def show
@packages_info = @advisory.fetch_packages_info
end end
def search def search
@ -27,24 +26,4 @@ class AdvisoriesController < ApplicationController
format.json { render @advisory } format.json { render @advisory }
end end
end end
protected
# this method fetches and structurize packages attached to current advisory.
def fetch_packages_info
@packages_info = Hash.new { |h, k| h[k] = {} } # maaagic, it's maaagic ;)
@advisory.build_lists.find_in_batches(:include => [:save_to_platform, :packages, :project]) do |batch|
batch.each do |build_list|
tmp = build_list.packages.inject({:srpm => nil, :rpm => []}) do |h, p|
p.package_type == 'binary' ? h[:rpm] << p.fullname : h[:srpm] = p.fullname
h
end
h = { build_list.project => tmp }
@packages_info[build_list.save_to_platform].merge!(h) do |pr, old, new|
{:srpm => new[:srpm], :rpm => old[:rpm].concat(new[:rpm]).uniq}
end
end
end
end
end end

View File

@ -0,0 +1,44 @@
# -*- encoding : utf-8 -*-
class Api::V1::AdvisoriesController < Api::V1::BaseController
before_filter :authenticate_user!
skip_before_filter :authenticate_user!, :only => [:index, :show] if APP_CONFIG['anonymous_access']
load_resource :advisory, :find_by => :advisory_id
before_filter :find_and_authorize_build_list, :only => [:create, :update]
authorize_resource :build_list, :only => [:create, :update]
def index
@advisories = @advisories.scoped(:include => :platforms).
paginate(paginate_params)
end
def show
@packages_info = @advisory.fetch_packages_info
end
def create
if @build_list.can_attach_to_advisory? &&
@build_list.associate_and_create_advisory(params[:advisory]) &&
@build_list.save
render_json_response @advisory, 'Advisory has been created successfully'
else
render_validation_error @advisory, error_message(@build_list, 'Advisory has not been created')
end
end
def update
if @advisory && @build_list.can_attach_to_advisory? &&
@advisory.attach_build_list(@build_list) && @build_list.save
render_json_response @advisory, "Build list '#{@build_list.id}' has been attached to advisory successfully"
else
render_validation_error @advisory, error_message(@build_list, 'Build list has not been attached to advisory')
end
end
protected
def find_and_authorize_build_list
@build_list = BuildList.find params[:build_list_id]
authorize! :update, @build_list.save_to_platform
end
end

View File

@ -83,7 +83,7 @@ class Api::V1::BaseController < ApplicationController
id = status != 200 ? nil : subject.id id = status != 200 ? nil : subject.id
render :json => { render :json => {
subject.class.name.downcase.to_sym => { subject.class.name.underscore.to_sym => {
:id => id, :id => id,
:message => message :message => message
} }

View File

@ -196,23 +196,13 @@ class Projects::BuildListsController < Projects::BaseController
if params[:attach_advisory] == 'new' if params[:attach_advisory] == 'new'
# create new advisory # create new advisory
unless @build_list.build_advisory(params[:build_list][:advisory]) do |a| unless @build_list.associate_and_create_advisory(params[:build_list][:advisory])
a.update_type = @build_list.update_type
a.projects << @build_list.project
a.platforms << @build_list.save_to_platform unless a.platforms.include? @build_list.save_to_platform
end.save
redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return
end end
else else
# attach existing advisory # attach existing advisory
a = Advisory.where(:advisory_id => params[:attach_advisory]).limit(1).first a = Advisory.where(:advisory_id => params[:attach_advisory]).first
if a.update_type != @build_list.update_type unless (a && a.attach_build_list(@build_list))
redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return
end
a.platforms << @build_list.save_to_platform unless a.platforms.include? @build_list.save_to_platform
a.projects << @build_list.project unless a.projects.include? @build_list.project
@build_list.advisory = a
unless a.save
redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return redirect_to :back, :notice => t('layout.build_lists.publish_fail') and return
end end
end end

View File

@ -21,6 +21,32 @@ class Advisory < ActiveRecord::Base
advisory_id advisory_id
end end
def attach_build_list(build_list)
return false if update_type != build_list.update_type
self.platforms << build_list.save_to_platform unless platforms.include? build_list.save_to_platform
self.projects << build_list.project unless projects.include? build_list.project
build_list.advisory = self
save
end
# this method fetches and structurize packages attached to current advisory.
def fetch_packages_info
packages_info = Hash.new { |h, k| h[k] = {} } # maaagic, it's maaagic ;)
build_lists.find_in_batches(:include => [:save_to_platform, :packages, :project]) do |batch|
batch.each do |build_list|
tmp = build_list.packages.inject({:srpm => nil, :rpm => []}) do |h, p|
p.package_type == 'binary' ? h[:rpm] << p.fullname : h[:srpm] = p.fullname
h
end
h = { build_list.project => tmp }
packages_info[build_list.save_to_platform].merge!(h) do |pr, old, new|
{:srpm => new[:srpm], :rpm => old[:rpm].concat(new[:rpm]).uniq}
end
end
end
packages_info
end
protected protected
def generate_advisory_id def generate_advisory_id

View File

@ -303,6 +303,18 @@ class BuildList < ActiveRecord::Base
#[WAITING_FOR_RESPONSE, BuildServer::BUILD_PENDING, BuildServer::BUILD_STARTED].include?(status) #[WAITING_FOR_RESPONSE, BuildServer::BUILD_PENDING, BuildServer::BUILD_STARTED].include?(status)
end end
def associate_and_create_advisory(params)
build_advisory(params){ |a| a.update_type = update_type }
advisory.attach_build_list(self)
end
def can_attach_to_advisory?
!save_to_repository.publish_without_qa &&
save_to_platform.main? &&
save_to_platform.released &&
status == BUILD_PUBLISHED
end
protected protected
def notify_users def notify_users

View File

@ -0,0 +1,12 @@
json.id advisory.advisory_id
json.(advisory, :description)
json.platforms advisory.platforms do |json_platform, platform|
json_platform.(platform, :id, :released)
json_platform.url api_v1_platform_path(platform.id, :format => :json)
end
json.projects advisory.projects do |json_project, project|
json_project.(project, :id, :name)
json_project.fullname project.name_with_owner
json_project.url api_v1_project_path(project.id, :format => :json)
end
json.url api_v1_advisory_path(advisory.advisory_id, :format => :json)

View File

@ -0,0 +1,4 @@
json.advisories @advisories do |json, advisory|
json.partial! 'advisory', :advisory => advisory, :json => json
end
json.url api_v1_advisories_path(:format => :json)

View File

@ -0,0 +1,27 @@
json.advisory do |json|
json.partial! 'advisory', :advisory => @advisory, :json => json
json.created_at @advisory.created_at.to_i
json.updated_at @advisory.updated_at.to_i
json.(@advisory, :update_type)
json.references @advisory.references.split('\n')
json.build_lists @advisory.build_lists do |json_build_list, build_list|
json_build_list.(build_list, :id)
json_build_list.url api_v1_build_list_path(build_list.id, :format => :json)
end
json.affected_in @packages_info do |json_platform, package_info|
platform = package_info[0]
json_platform.(platform, :id)
json_platform.url api_v1_platform_path(platform.id, :format => :json)
json_platform.projects package_info[1] do |json_project, info|
project = info[0]
json_project.(project, :id)
json_project.url api_v1_project_path(project.id, :format => :json)
packages = info[1]
json_project.srpm packages[:srpm]
json_project.rpm packages[:rpm]
end
end
end

View File

@ -12,6 +12,7 @@ Rosa::Application.routes.draw do
namespace :api do namespace :api do
namespace :v1 do namespace :v1 do
resources :advisories, :only => [:index, :show, :create, :update]
resources :build_lists, :only => [:index, :create, :show] do resources :build_lists, :only => [:index, :create, :show] do
member { member {
get :publish get :publish

View File

@ -0,0 +1,144 @@
# -*- encoding : utf-8 -*-
require 'spec_helper'
shared_examples_for 'api advisories user with show rights' do
it 'should be able to perform show action' do
get :show, :id => @advisory.advisory_id, :format => :json
response.should be_success
end
it 'should be able to perform index action' do
get :index, :format => :json
response.should be_success
end
end
shared_examples_for 'api advisories user with admin rights' do
context 'api advisories user with create rights' do
let(:params) { {:build_list_id => @build_list.id, :advisory => {:description => 'test'}} }
it 'should be able to perform create action' do
post :create, params, :format => :json
response.should be_success
end
it 'ensures that advisory has been created' do
lambda { post :create, params, :format => :json }.should change{ Advisory.count }.by(1)
end
it 'ensures that build_list has been associated with advisory' do
post :create, params, :format => :json
@build_list.reload
@build_list.advisory.should_not be_nil
end
end
context 'api advisories user with update rights' do
let(:params) { {:id => @advisory.advisory_id, :build_list_id => @build_list.id} }
it 'should be able to perform update action' do
put :update, params, :format => :json
response.should be_success
end
it 'ensures that advisory has not been created' do
lambda { put :update, params, :format => :json }.should_not change{ Advisory.count }
end
it 'ensures that build_list has been associated with advisory' do
put :update, params, :format => :json
@build_list.reload
@build_list.advisory.should_not be_nil
end
end
end
shared_examples_for 'api advisories user without admin rights' do
context 'api advisories user without create rights' do
let(:params) { {:build_list_id => @build_list.id, :advisory => {:description => 'test'}} }
it 'should not be able to perform create action' do
post :create, params, :format => :json
response.should_not be_success
end
it 'ensures that advisory has not been created' do
lambda { post :create, params, :format => :json }.should_not change{ Advisory.count }
end
it 'ensures that build_list has not been associated with advisory' do
post :create, params, :format => :json
@build_list.reload
@build_list.advisory.should be_nil
end
end
context 'api advisories user without update rights' do
let(:params) { {:id => @advisory.advisory_id, :build_list_id => @build_list.id} }
it 'should not be able to perform update action' do
put :update, params, :format => :json
response.should_not be_success
end
it 'ensures that advisory has not been created' do
lambda { put :update, params, :format => :json }.should_not change{ Advisory.count }
end
it 'ensures that build_list has not been associated with advisory' do
put :update, params, :format => :json
@build_list.reload
@build_list.advisory.should be_nil
end
end
end
describe Api::V1::AdvisoriesController do
before do
stub_symlink_methods
@advisory = FactoryGirl.create(:advisory)
@build_list = FactoryGirl.create(:build_list_core)
@build_list.save_to_platform.update_column(:released, true)
@build_list.save_to_repository.update_column(:publish_without_qa, false)
@build_list.update_column(:status, BuildList::BUILD_PUBLISHED)
end
context 'for guest' do
if APP_CONFIG['anonymous_access']
it_should_behave_like 'api advisories user with show rights'
end
it 'should not be able to perform show action', :anonymous_access => false do
get :show, :id => @advisory.advisory_id, :format => :json
response.should_not be_success
end
it 'should not be able to perform index action', :anonymous_access => false do
get :index, :format => :json
response.should_not be_success
end
it_should_behave_like 'api advisories user without admin rights'
end
context 'for simple user' do
before do
@user = FactoryGirl.create(:user)
http_login(@user)
end
it_should_behave_like 'api advisories user with show rights'
it_should_behave_like 'api advisories user without admin rights'
end
context 'for admin' do
before do
@admin = FactoryGirl.create(:admin)
http_login(@admin)
end
it_should_behave_like 'api advisories user with show rights'
it_should_behave_like 'api advisories user with admin rights'
end
context 'for user who has access to update build_list' do
before do
@user = FactoryGirl.create(:user)
@build_list.save_to_platform.relations.create(:role => 'admin', :actor => @user)
http_login(@user)
end
it_should_behave_like 'api advisories user with show rights'
it_should_behave_like 'api advisories user with admin rights'
end
end

View File

@ -0,0 +1,7 @@
# -*- encoding : utf-8 -*-
FactoryGirl.define do
factory :advisory do
description { FactoryGirl.generate(:string) }
update_type 'security'
end
end