commit
8df7b9f6f3
Binary file not shown.
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 79 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,125 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is a project management system. |
||||||
|
# Copyright (C) 2012-2014 the OpenProject Foundation (OPF) |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See doc/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
# This capsulates permissions a user has for a work package. It caches based |
||||||
|
# on the work package's project and is thus optimized for the context menu. |
||||||
|
# |
||||||
|
# This is no conern but it was placed here so that it will be removed together |
||||||
|
# with the rest of the experimental API. |
||||||
|
|
||||||
|
module Api |
||||||
|
module Experimental |
||||||
|
module Concerns |
||||||
|
class Can |
||||||
|
attr_accessor :user |
||||||
|
|
||||||
|
def initialize(user) |
||||||
|
self.user = user |
||||||
|
end |
||||||
|
|
||||||
|
def actions(wp) |
||||||
|
cache[wp].each_with_object([]) { |(k, v), a| a << k if v } |
||||||
|
end |
||||||
|
|
||||||
|
def allowed?(work_package, action) |
||||||
|
cache[work_package][action] |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def cache |
||||||
|
@cache ||= Hash.new do |hash, work_package| |
||||||
|
# copy checks for the move_work_packages permission. This makes |
||||||
|
# sense only because the work_packages/moves controller handles |
||||||
|
# copying multiple work packages. |
||||||
|
hash[work_package] = { |
||||||
|
:edit => edit_allowed?(work_package), |
||||||
|
:log_time => log_time_allowed?(work_package), |
||||||
|
:move => move_allowed?(work_package), |
||||||
|
:copy => move_allowed?(work_package), |
||||||
|
:duplicate => copy_allowed?(work_package), # duplicating is another form of copying |
||||||
|
:delete => delete_allowed?(work_package) |
||||||
|
} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def edit_allowed?(work_package) |
||||||
|
@edit_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = user.allowed_to?(:edit_work_packages, project) |
||||||
|
end |
||||||
|
|
||||||
|
@edit_cache[work_package.project] || work_package.new_statuses_allowed_to(user).present? |
||||||
|
end |
||||||
|
|
||||||
|
def log_time_allowed?(work_package) |
||||||
|
@log_time_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = user.allowed_to?(:log_time, project) |
||||||
|
end |
||||||
|
|
||||||
|
@log_time_cache[work_package.project] |
||||||
|
end |
||||||
|
|
||||||
|
def move_allowed?(work_package) |
||||||
|
@move_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = user.allowed_to?(:move_work_packages, project) |
||||||
|
end |
||||||
|
|
||||||
|
@move_cache[work_package.project] |
||||||
|
end |
||||||
|
|
||||||
|
def copy_allowed?(work_package) |
||||||
|
type_active_in_project?(work_package) && add_allowed?(work_package) |
||||||
|
end |
||||||
|
|
||||||
|
def delete_allowed?(work_package) |
||||||
|
@delete_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = user.allowed_to?(:delete_work_packages, project) |
||||||
|
end |
||||||
|
|
||||||
|
@delete_cache[work_package.project] |
||||||
|
end |
||||||
|
|
||||||
|
def add_allowed?(work_package) |
||||||
|
@add_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = user.allowed_to?(:add_work_packages, project) |
||||||
|
end |
||||||
|
|
||||||
|
@add_cache[work_package.project] |
||||||
|
end |
||||||
|
|
||||||
|
def type_active_in_project?(work_package) |
||||||
|
@type_active_cache ||= Hash.new do |hash, project| |
||||||
|
hash[project] = project.types.pluck(:id) |
||||||
|
end |
||||||
|
|
||||||
|
@type_active_cache[work_package.project].include?(work_package.type_id) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,38 @@ |
|||||||
|
#-- encoding: UTF-8 |
||||||
|
#-- copyright |
||||||
|
# OpenProject is a project management system. |
||||||
|
# Copyright (C) 2012-2014 the OpenProject Foundation (OPF) |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See doc/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
|
||||||
|
class ChangeAttachmentJournalsDescriptionLength < ActiveRecord::Migration |
||||||
|
def change |
||||||
|
change_column :attachment_journals, |
||||||
|
:description, |
||||||
|
:text |
||||||
|
end |
||||||
|
end |
||||||
|
|
@ -0,0 +1,52 @@ |
|||||||
|
module API |
||||||
|
module V3 |
||||||
|
module Queries |
||||||
|
class QueriesAPI < Grape::API |
||||||
|
|
||||||
|
resources :queries do |
||||||
|
|
||||||
|
params do |
||||||
|
requires :id, desc: 'Query id' |
||||||
|
end |
||||||
|
namespace ':id' do |
||||||
|
|
||||||
|
before do |
||||||
|
@query = Query.find(params[:id]) |
||||||
|
model = QueryModel.new(query: @query) |
||||||
|
@representer = QueryRepresenter.new(model) |
||||||
|
end |
||||||
|
|
||||||
|
helpers do |
||||||
|
def allowed_to_manage_stars? |
||||||
|
(@query.is_public? && current_user.allowed_to?(:manage_public_queries, @query.project)) || |
||||||
|
(!@query.is_public? && (current_user.admin? || |
||||||
|
(current_user.allowed_to?(:save_queries, @query.project) && @query.user_id == current_user.id))) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
patch :star do |
||||||
|
authorize(:queries, :star, project: @query.project, allow: allowed_to_manage_stars?) |
||||||
|
normalized_query_name = @query.name.parameterize.underscore |
||||||
|
query_menu_item = MenuItems::QueryMenuItem.find_or_initialize_by_name_and_navigatable_id( |
||||||
|
normalized_query_name, @query.id, title: @query.name |
||||||
|
) |
||||||
|
query_menu_item.save! |
||||||
|
@representer.to_json |
||||||
|
end |
||||||
|
|
||||||
|
patch :unstar do |
||||||
|
authorize(:queries, :unstar, project: @query.project, allow: allowed_to_manage_stars?) |
||||||
|
query_menu_item = @query.query_menu_item |
||||||
|
return @representer.to_json if @query.query_menu_item.nil? |
||||||
|
query_menu_item.destroy |
||||||
|
@query.reload |
||||||
|
@representer.to_json |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,54 @@ |
|||||||
|
#-- encoding: UTF-8 |
||||||
|
#-- copyright |
||||||
|
# OpenProject is a project management system. |
||||||
|
# Copyright (C) 2012-2014 the OpenProject Foundation (OPF) |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See doc/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
require 'reform' |
||||||
|
require 'reform/form/coercion' |
||||||
|
|
||||||
|
module API |
||||||
|
module V3 |
||||||
|
module Queries |
||||||
|
class QueryModel < Reform::Form |
||||||
|
include Composition |
||||||
|
include Coercion |
||||||
|
|
||||||
|
model :query |
||||||
|
|
||||||
|
property :name, on: :query, type: String |
||||||
|
property :project_id, on: :query, type: Integer |
||||||
|
property :user_id, on: :query, type: Integer |
||||||
|
property :filters, on: :query, type: String |
||||||
|
property :is_public, on: :query, type: String |
||||||
|
property :column_names, on: :query, type: String |
||||||
|
property :sort_criteria, on: :query, type: String |
||||||
|
property :group_by, on: :query, type: String |
||||||
|
property :display_sums, on: :query, type: String |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,76 @@ |
|||||||
|
#-- encoding: UTF-8 |
||||||
|
#-- copyright |
||||||
|
# OpenProject is a project management system. |
||||||
|
# Copyright (C) 2012-2014 the OpenProject Foundation (OPF) |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See doc/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
require 'roar/decorator' |
||||||
|
require 'roar/representer/json/hal' |
||||||
|
|
||||||
|
module API |
||||||
|
module V3 |
||||||
|
module Queries |
||||||
|
class QueryRepresenter < Roar::Decorator |
||||||
|
include Roar::Representer::JSON::HAL |
||||||
|
include Roar::Representer::Feature::Hypermedia |
||||||
|
include Rails.application.routes.url_helpers |
||||||
|
|
||||||
|
self.as_strategy = API::Utilities::CamelCasingStrategy.new |
||||||
|
|
||||||
|
property :_type, exec_context: :decorator |
||||||
|
|
||||||
|
link :self do |
||||||
|
{ href: "http://localhost:3000/api/v3/queries/#{represented.query.id}", title: "#{represented.name}" } |
||||||
|
end |
||||||
|
|
||||||
|
property :id, getter: -> (*) { query.id }, render_nil: true |
||||||
|
property :name, render_nil: true |
||||||
|
property :project_id, getter: -> (*) { query.project.id } |
||||||
|
property :project_name, getter: -> (*) { query.project.try(:name) } |
||||||
|
property :user_id, getter: -> (*) { query.user.try(:id) }, render_nil: true |
||||||
|
property :user_name, getter: -> (*) { query.user.try(:name) }, render_nil: true |
||||||
|
property :user_login, getter: -> (*) { query.user.try(:login) }, render_nil: true |
||||||
|
property :user_mail, getter: -> (*) { query.user.try(:mail) }, render_nil: true |
||||||
|
property :filters, render_nil: true |
||||||
|
property :is_public, getter: -> (*) { query.is_public.to_s }, render_nil: true |
||||||
|
property :column_names, render_nil: true |
||||||
|
property :sort_criteria, render_nil: true |
||||||
|
property :group_by, render_nil: true |
||||||
|
property :display_sums, getter: -> (*) { query.display_sums.to_s }, render_nil: true |
||||||
|
property :is_starred, getter: -> (*) { is_starred.to_s }, exec_context: :decorator |
||||||
|
|
||||||
|
def _type |
||||||
|
"Query" |
||||||
|
end |
||||||
|
|
||||||
|
def is_starred |
||||||
|
return true if !represented.query.query_menu_item.nil? |
||||||
|
false |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,377 @@ |
|||||||
|
require 'spec_helper' |
||||||
|
require 'rack/test' |
||||||
|
|
||||||
|
describe 'API v3 Query resource' do |
||||||
|
include Rack::Test::Methods |
||||||
|
|
||||||
|
let(:project) { FactoryGirl.create(:project, :identifier => 'test_project', :is_public => false) } |
||||||
|
let(:current_user) { FactoryGirl.create(:user) } |
||||||
|
let(:manage_public_queries_role) { FactoryGirl.create(:role, permissions: [:manage_public_queries]) } |
||||||
|
let(:save_queries_role) { FactoryGirl.create(:role, permissions: [:save_queries]) } |
||||||
|
let(:role_without_query_permissions) { FactoryGirl.create(:role, permissions: [:view_work_packages]) } |
||||||
|
let(:unauthorize_user) { FactoryGirl.create(:user) } |
||||||
|
|
||||||
|
describe '#star' do |
||||||
|
let(:star_path) { "/api/v3/queries/#{query.id}/star" } |
||||||
|
let(:filters) do |
||||||
|
query.filters.map{ |f| {f.field.to_s => { "operator" => f.operator, "values" => f.values }}} |
||||||
|
end |
||||||
|
let(:expected_response) do |
||||||
|
{ |
||||||
|
"_type" => 'Query', |
||||||
|
"_links" => { |
||||||
|
"self" => { |
||||||
|
"href" => "http://localhost:3000/api/v3/queries/#{query.id}", |
||||||
|
"title" => query.name |
||||||
|
} |
||||||
|
}, |
||||||
|
"id" => query.id, |
||||||
|
"name" => query.name, |
||||||
|
"projectId" => query.project_id, |
||||||
|
"projectName" => query.project.name, |
||||||
|
"userId" => query.user_id, |
||||||
|
"userName" => query.user.try(:name), |
||||||
|
"userLogin" => query.user.try(:login), |
||||||
|
"userMail" => query.user.try(:mail), |
||||||
|
"filters" => filters, |
||||||
|
"isPublic" => query.is_public.to_s, |
||||||
|
"columnNames" => query.column_names, |
||||||
|
"sortCriteria" => query.sort_criteria, |
||||||
|
"groupBy" => query.group_by, |
||||||
|
"displaySums" => query.display_sums.to_s, |
||||||
|
"isStarred" => "true" |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
describe 'public queries' do |
||||||
|
let(:query) { FactoryGirl.create(:public_query, project: project) } |
||||||
|
|
||||||
|
context 'user with permission to manage public queries' do |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [manage_public_queries_role.id] |
||||||
|
member.save! |
||||||
|
end |
||||||
|
|
||||||
|
context 'when starring an unstarred query' do |
||||||
|
before(:each) { patch star_path } |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to true' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('true') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when starring already starred query' do |
||||||
|
before(:each) { patch star_path } |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to true' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('true') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when trying to star nonexistent query' do |
||||||
|
let(:star_path) { "/api/v3/queries/999/star" } |
||||||
|
before(:each) { patch star_path } |
||||||
|
|
||||||
|
it 'should respond with 404' do |
||||||
|
last_response.status.should eq(404) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_found', 'messages' => ['Couldn\'t find Query with id=999']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'user without permission to manage public queries' do |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [role_without_query_permissions.id] |
||||||
|
member.save! |
||||||
|
patch star_path |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'private queries' do |
||||||
|
context 'user with permission to save queries' do |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [save_queries_role.id] |
||||||
|
member.save! |
||||||
|
patch star_path |
||||||
|
end |
||||||
|
|
||||||
|
context 'starring his own query' do |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to true' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('true') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'trying to star somebody else\'s query' do |
||||||
|
let(:another_user) { FactoryGirl.create(:user) } |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: another_user) } |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'user without permission to save queries' do |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [role_without_query_permissions.id] |
||||||
|
member.save! |
||||||
|
patch star_path |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe '#unstar' do |
||||||
|
let(:unstar_path) { "/api/v3/queries/#{query.id}/unstar" } |
||||||
|
let(:filters) do |
||||||
|
query.filters.map{ |f| {f.field.to_s => { "operator" => f.operator, "values" => f.values }}} |
||||||
|
end |
||||||
|
let(:expected_response) do |
||||||
|
{ |
||||||
|
"_type" => 'Query', |
||||||
|
"_links" => { |
||||||
|
"self" => { |
||||||
|
"href" => "http://localhost:3000/api/v3/queries/#{query.id}", |
||||||
|
"title" => query.name |
||||||
|
} |
||||||
|
}, |
||||||
|
"id" => query.id, |
||||||
|
"name" => query.name, |
||||||
|
"projectId" => query.project_id, |
||||||
|
"projectName" => query.project.name, |
||||||
|
"userId" => query.user_id, |
||||||
|
"userName" => query.user.try(:name), |
||||||
|
"userLogin" => query.user.try(:login), |
||||||
|
"userMail" => query.user.try(:mail), |
||||||
|
"filters" => filters, |
||||||
|
"isPublic" => query.is_public.to_s, |
||||||
|
"columnNames" => query.column_names, |
||||||
|
"sortCriteria" => query.sort_criteria, |
||||||
|
"groupBy" => query.group_by, |
||||||
|
"displaySums" => query.display_sums.to_s, |
||||||
|
"isStarred" => "true" |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
describe 'public queries' do |
||||||
|
let(:query) { FactoryGirl.create(:public_query, project: project) } |
||||||
|
|
||||||
|
context 'user with permission to manage public queries' do |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [manage_public_queries_role.id] |
||||||
|
member.save! |
||||||
|
end |
||||||
|
|
||||||
|
context 'when unstarring a starred query' do |
||||||
|
before(:each) do |
||||||
|
FactoryGirl.create(:query_menu_item, query: query) |
||||||
|
patch unstar_path |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to false' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('false') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'when unstarring an unstarred query' do |
||||||
|
before(:each) { patch unstar_path } |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to true' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('false') |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
|
||||||
|
context 'when trying to unstar nonexistent query' do |
||||||
|
let(:unstar_path) { "/api/v3/queries/999/unstar" } |
||||||
|
before(:each) { patch unstar_path } |
||||||
|
|
||||||
|
it 'should respond with 404' do |
||||||
|
last_response.status.should eq(404) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_found', 'messages' => ['Couldn\'t find Query with id=999']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'user without permission to manage public queries' do |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [role_without_query_permissions.id] |
||||||
|
member.save! |
||||||
|
patch unstar_path |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe 'private queries' do |
||||||
|
context 'user with permission to save queries' do |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [save_queries_role.id] |
||||||
|
member.save! |
||||||
|
patch unstar_path |
||||||
|
end |
||||||
|
|
||||||
|
context 'unstarring his own query' do |
||||||
|
|
||||||
|
it 'should respond with 200' do |
||||||
|
last_response.status.should eq(200) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query in HAL+JSON format' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response.should eq(expected_response.tap{ |r| r["isStarred"] = "false" }) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should return the query with "isStarred" property set to true' do |
||||||
|
parsed_response = JSON.parse(last_response.body) |
||||||
|
parsed_response['isStarred'].should eq('false') |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'trying to unstar somebody else\'s query' do |
||||||
|
let(:another_user) { FactoryGirl.create(:user) } |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: another_user) } |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'user without permission to save queries' do |
||||||
|
let(:query) { FactoryGirl.create(:private_query, project: project, user: current_user) } |
||||||
|
before(:each) do |
||||||
|
allow(User).to receive(:current).and_return current_user |
||||||
|
member = FactoryGirl.build(:member, user: current_user, project: project) |
||||||
|
member.role_ids = [role_without_query_permissions.id] |
||||||
|
member.save! |
||||||
|
patch unstar_path |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with 403' do |
||||||
|
last_response.status.should eq(403) |
||||||
|
end |
||||||
|
|
||||||
|
it 'should respond with explanatory error message' do |
||||||
|
parsed_errors = JSON.parse(last_response.body)['errors'] |
||||||
|
parsed_errors.should eq([{ 'key' => 'not_authorized', 'messages' => ['You are not authorize to access this resource']}]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue