parent
4c32d17ede
commit
9e79d02d8f
@ -0,0 +1,54 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 'model_contract' |
||||
|
||||
module Queries |
||||
class BaseContract < ::ModelContract |
||||
attribute :name |
||||
|
||||
attribute :project_id |
||||
attribute :is_public # => public |
||||
attribute :display_sums # => sums |
||||
|
||||
attribute :column_names # => columns |
||||
attribute :filters |
||||
|
||||
attribute :sort_criteria # => sortBy |
||||
attribute :group_by # => groupBy |
||||
|
||||
attr_reader :user |
||||
|
||||
def initialize(query, user) |
||||
super query |
||||
|
||||
@user = user |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 'queries/base_contract' |
||||
|
||||
module Queries |
||||
class CreateContract < BaseContract |
||||
validate :user_allowed_to_make_public |
||||
|
||||
def user_allowed_to_make_public |
||||
if is_public && !user.allowed_to?(:manage_public_queries, model.project) |
||||
errors.add :public, :error_unauthorized |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,59 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 CreateQueryService |
||||
include Concerns::Contracted |
||||
|
||||
attr_reader :user |
||||
|
||||
self.contract = Queries::CreateContract |
||||
|
||||
def initialize(user:) |
||||
@user = user |
||||
end |
||||
|
||||
def call(query) |
||||
create query |
||||
end |
||||
|
||||
private |
||||
|
||||
def create(query) |
||||
initialize_contract! query |
||||
|
||||
result, errors = validate_and_save query |
||||
query.update user: user |
||||
|
||||
ServiceResult.new success: result, errors: errors, result: query |
||||
end |
||||
|
||||
def initialize_contract!(query) |
||||
self.contract = self.class.contract.new query, user |
||||
end |
||||
end |
@ -0,0 +1,62 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 'api/v3/queries/query_representer' |
||||
require 'queries/create_query_service' |
||||
|
||||
module API |
||||
module V3 |
||||
module Queries |
||||
class CreateFormAPI < ::API::OpenProjectAPI |
||||
resource :form do |
||||
helpers ::API::V3::Queries::CreateQuery |
||||
|
||||
post do |
||||
representer = ::API::V3::Queries::QueryRepresenter.create Query.new_default, current_user: current_user |
||||
query = representer.from_hash Hash(request_body) |
||||
|
||||
contract = ::Queries::CreateContract.new query, current_user |
||||
contract.validate |
||||
|
||||
query.user = current_user |
||||
|
||||
api_errors = ::API::Errors::ErrorBase.create_errors(contract.errors) |
||||
|
||||
# errors for invalid data (e.g. validation errors) are handled inside the form |
||||
if api_errors.all? { |error| error.code == 422 } |
||||
status 200 |
||||
CreateFormRepresenter.new query, current_user: current_user, errors: api_errors |
||||
else |
||||
fail ::API::Errors::MultipleErrors.create_if_many(api_errors) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,68 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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. |
||||
#++ |
||||
|
||||
module API |
||||
module V3 |
||||
module Queries |
||||
class CreateFormRepresenter < FormRepresenter |
||||
link :self do |
||||
{ |
||||
href: api_v3_paths.query_form, |
||||
method: :post |
||||
} |
||||
end |
||||
|
||||
link :validate do |
||||
{ |
||||
href: api_v3_paths.query_form, |
||||
method: :post |
||||
} |
||||
end |
||||
|
||||
link :commit do |
||||
if allow_commit? |
||||
{ |
||||
href: api_v3_paths.queries, |
||||
method: :post |
||||
} |
||||
end |
||||
end |
||||
|
||||
private |
||||
|
||||
def allow_commit? |
||||
represented.name.present? && ( |
||||
(!represented.is_public && current_user.allowed_to?(:save_queries, represented.project)) || |
||||
(represented.is_public && current_user.allowed_to?(:manage_public_queries, represented.project)) |
||||
) && @errors.empty? |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,79 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 'api/v3/queries/query_representer' |
||||
require 'queries/create_query_service' |
||||
|
||||
module API |
||||
module V3 |
||||
module Queries |
||||
module CreateQuery |
||||
def create_query(request_body, current_user) |
||||
rep = representer.new Query.new, current_user: current_user |
||||
query = rep.from_hash request_body |
||||
call = ::CreateQueryService.new(user: current_user).call query |
||||
|
||||
if call.success? |
||||
representer.new call.result, current_user: current_user, embed_links: true |
||||
else |
||||
fail ::API::Errors::ErrorBase.create_and_merge_errors(call.errors) |
||||
end |
||||
end |
||||
|
||||
def representer |
||||
::API::V3::Queries::QueryRepresenter |
||||
end |
||||
end |
||||
|
||||
def create_query_form( |
||||
query, |
||||
current_user:, |
||||
contract_class: ::Queries::CreateContract, |
||||
form_class: ::API::V3::Queries::CreateFormRepresenter, |
||||
action: :update |
||||
) |
||||
write_work_package_attributes(work_package, request_body, reset_lock_version: true) |
||||
contract = contract_class.new(query, current_user) |
||||
contract.validate |
||||
|
||||
api_errors = ::API::Errors::ErrorBase.create_errors(contract.errors) |
||||
|
||||
# errors for invalid data (e.g. validation errors) are handled inside the form |
||||
if only_validation_errors(api_errors) |
||||
status 200 |
||||
form_class.new(query, |
||||
current_user: current_user, |
||||
errors: api_errors, |
||||
action: action) |
||||
else |
||||
fail ::API::Errors::MultipleErrors.create_if_many(api_errors) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,98 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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/json/hal' |
||||
|
||||
module API |
||||
module V3 |
||||
module Queries |
||||
class QueryPayloadRepresenter < ::API::Decorators::Single |
||||
prepend QuerySerialization |
||||
|
||||
links :columns do |
||||
represented.columns.map do |column| |
||||
{ href: api_v3_paths.query_column(convert_attribute(column.name)) } |
||||
end |
||||
end |
||||
|
||||
link :groupBy do |
||||
column = represented.group_by_column |
||||
|
||||
if column |
||||
{ href: api_v3_paths.query_group_by(convert_attribute(column.name)) } |
||||
else |
||||
{ href: nil } |
||||
end |
||||
end |
||||
|
||||
links :sortBy do |
||||
represented.sort_criteria.map do |column, dir| |
||||
name = ::API::Utilities::PropertyNameConverter.from_ar_name column |
||||
|
||||
{ href: api_v3_paths.query_sort_by(name, dir) } |
||||
end |
||||
end |
||||
|
||||
linked_property :project |
||||
|
||||
property :name |
||||
property :filters, |
||||
exec_context: :decorator, |
||||
getter: ->(*) { trimmed_filters filters } |
||||
|
||||
property :display_sums, as: :sums |
||||
property :is_public, as: :public |
||||
|
||||
private |
||||
|
||||
## |
||||
# Uses the a normal query's filter representation and removes the bits |
||||
# we don't want for a payload. |
||||
def trimmed_filters(filters) |
||||
filters.map(&:to_hash).map { |v| trim_links v } |
||||
end |
||||
|
||||
def trim_links(value) |
||||
if value.is_a? ::Hash |
||||
value.except("_type", "name", "title", "schema").map_values { |v| trim_links v } |
||||
elsif value.is_a? Array |
||||
value.map { |v| trim_links v } |
||||
else |
||||
value |
||||
end |
||||
end |
||||
|
||||
def convert_attribute(attribute) |
||||
::API::Utilities::PropertyNameConverter.from_ar_name(attribute) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,186 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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. |
||||
#++ |
||||
|
||||
module API |
||||
module V3 |
||||
module Queries |
||||
module QuerySerialization |
||||
## |
||||
# Overriding this to initialize properties whose values depend on the "_links" attribute. |
||||
def from_hash(hash) |
||||
query = super |
||||
|
||||
initialize_links! query, hash |
||||
|
||||
query |
||||
end |
||||
|
||||
def columns |
||||
represented.columns.map do |column| |
||||
::API::V3::Queries::Columns::QueryColumnRepresenter.new(column) |
||||
end |
||||
end |
||||
|
||||
def filters |
||||
represented.filters.map do |filter| |
||||
::API::V3::Queries::Filters::QueryFilterInstanceRepresenter.new(filter) |
||||
end |
||||
end |
||||
|
||||
def filters=(filters_hash) |
||||
represented.filters = [] |
||||
|
||||
filters_hash.each do |filter_attributes| |
||||
name = get_filter_name filter_attributes |
||||
operator = get_filter_operator filter_attributes |
||||
|
||||
if name && operator |
||||
represented.add_filter name, operator, get_filter_values(filter_attributes) |
||||
else |
||||
raise API::Errors::InvalidRequestBody, "Could not read filter from: #{filter_attributes}" |
||||
end |
||||
end |
||||
end |
||||
|
||||
def sort_by |
||||
return unless represented.sort_criteria |
||||
|
||||
map_with_sort_by_as_decorated(represented.sort_criteria) do |sort_by| |
||||
::API::V3::Queries::SortBys::QuerySortByRepresenter.new(sort_by) |
||||
end |
||||
end |
||||
|
||||
def group_by |
||||
return unless represented.grouped? |
||||
|
||||
column = represented.group_by_column |
||||
|
||||
::API::V3::Queries::GroupBys::QueryGroupByRepresenter.new(column) |
||||
end |
||||
|
||||
module_function |
||||
|
||||
def get_filter_name(filter_attributes) |
||||
href = filter_attributes.dig("_links", "filter", "href") |
||||
id = id_from_href "queries/filters", href |
||||
|
||||
::API::Utilities::QueryFiltersNameConverter.to_ar_name id, refer_to_ids: true if id |
||||
end |
||||
|
||||
def get_filter_operator(filter_attributes) |
||||
op_href = filter_attributes.dig("_links", "operator", "href") |
||||
|
||||
id_from_href "queries/operators", op_href |
||||
end |
||||
|
||||
def get_filter_values(filter_attributes) |
||||
filter_attributes["values"] || |
||||
Array(filter_attributes.dig("_links", "values")) |
||||
.map { |value| id_from_href nil, value["href"] } |
||||
.compact |
||||
end |
||||
|
||||
def initialize_links!(query, attributes) |
||||
query.project_id = get_project_id attributes |
||||
query.group_by = get_group_by attributes |
||||
query.column_names = get_columns attributes |
||||
|
||||
if sort_criteria = get_sort_criteria(attributes) |
||||
query.sort_criteria = sort_criteria |
||||
end |
||||
end |
||||
|
||||
def get_user_id(query_attributes) |
||||
href = query_attributes.dig("_links", "user", "href") |
||||
|
||||
id_from_href "users", href |
||||
end |
||||
|
||||
def get_project_id(query_attributes) |
||||
href = query_attributes.dig("_links", "project", "href") |
||||
|
||||
id_from_href "projects", href |
||||
end |
||||
|
||||
def get_sort_criteria(query_attributes) |
||||
criteria = Array(query_attributes.dig("_links", "sortBy")).map do |sort_by| |
||||
if id = id_from_href("queries/sort_bys", sort_by.href) |
||||
column, direction = id.split("-") # e.g. ["start_date", "desc"] |
||||
|
||||
if column && direction |
||||
column = ::API::Utilities::PropertyNameConverter.to_ar_name(column, context: WorkPackage.new) |
||||
direction = nil unless ["asc", "desc"].include? direction |
||||
|
||||
[column, direction] |
||||
end |
||||
end |
||||
end |
||||
|
||||
criteria.compact.presence |
||||
end |
||||
|
||||
def get_group_by(query_attributes) |
||||
href = query_attributes.dig "_links", "groupBy", "href" |
||||
attr = id_from_href "queries/group_bys", href |
||||
|
||||
::API::Utilities::PropertyNameConverter.to_ar_name(attr, context: WorkPackage.new) if attr |
||||
end |
||||
|
||||
def get_columns(query_attributes) |
||||
columns = Array(query_attributes.dig("_links", "columns")).map do |column| |
||||
name = id_from_href "queries/columns", column.href |
||||
|
||||
::API::Utilities::PropertyNameConverter.to_ar_name(name, context: WorkPackage.new) if name |
||||
end |
||||
|
||||
columns.map(&:to_sym).compact.presence |
||||
end |
||||
|
||||
def id_from_href(expected_namespace, href) |
||||
return nil if href.blank? |
||||
|
||||
::API::Utilities::ResourceLinkParser.parse_id( |
||||
href, |
||||
property: (expected_namespace && expected_namespace.split("/").last) || "filter_value", |
||||
expected_version: "3", |
||||
expected_namespace: expected_namespace |
||||
) |
||||
end |
||||
|
||||
def map_with_sort_by_as_decorated(sort_criteria) |
||||
sort_criteria.map do |attribute, order| |
||||
decorated = ::API::V3::Queries::SortBys::SortByDecorator.new(attribute, order) |
||||
|
||||
yield decorated |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,283 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2015 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 'spec_helper' |
||||
require 'rack/test' |
||||
|
||||
describe "POST /api/v3/queries/form", type: :request do |
||||
include API::V3::Utilities::PathHelper |
||||
|
||||
let(:path) { api_v3_paths.query_form } |
||||
let(:user) { FactoryGirl.create(:admin) } |
||||
let!(:project) { FactoryGirl.create(:project_with_types) } |
||||
|
||||
let(:parameters) { {} } |
||||
let(:override_params) { {} } |
||||
let(:form) { JSON.parse response.body } |
||||
|
||||
before do |
||||
login_as(user) |
||||
|
||||
post path, |
||||
params: parameters.merge(override_params).to_json, |
||||
headers: { 'CONTENT_TYPE' => 'application/json' } |
||||
end |
||||
|
||||
it 'should return 200(OK)' do |
||||
expect(response.status).to eq(200) |
||||
end |
||||
|
||||
it 'should be of type form' do |
||||
expect(form["_type"]).to eq "Form" |
||||
end |
||||
|
||||
it 'has the available_projects link for creation in the schema' do |
||||
expect(form.dig("_embedded", "schema", "project", "_links", "allowedValues", "href")) |
||||
.to eq "/api/v3/queries/available_projects" |
||||
end |
||||
|
||||
describe 'with empty parameters' do |
||||
it 'has 1 validation error' do |
||||
expect(form.dig("_embedded", "validationErrors").size).to eq 1 |
||||
end |
||||
|
||||
it 'has a validation error on name' do |
||||
expect(form.dig("_embedded", "validationErrors", "name", "message")).to eq "Name can't be blank." |
||||
end |
||||
|
||||
it 'has no commit link' do |
||||
expect(form.dig("_links", "commit")).to be_nil |
||||
end |
||||
end |
||||
|
||||
describe 'with all minimum parameters' do |
||||
let(:parameters) do |
||||
{ |
||||
name: "Some Query" |
||||
} |
||||
end |
||||
|
||||
it 'has 0 validation errors' do |
||||
expect(form.dig("_embedded", "validationErrors")).to be_empty |
||||
end |
||||
|
||||
it 'has the given name set' do |
||||
expect(form.dig("_embedded", "payload", "name")).to eq parameters[:name] |
||||
end |
||||
end |
||||
|
||||
describe 'with all parameters given' do |
||||
let(:status) { FactoryGirl.create :status } |
||||
|
||||
let(:parameters) do |
||||
{ |
||||
name: "Some Query", |
||||
public: true, |
||||
sums: true, |
||||
filters: [ |
||||
{ |
||||
name: "Status", |
||||
_links: { |
||||
filter: { |
||||
href: "/api/v3/queries/filters/status" |
||||
}, |
||||
operator: { |
||||
"href": "/api/v3/queries/operators/=" |
||||
}, |
||||
values: [ |
||||
{ |
||||
href: "/api/v3/statuses/#{status.id}", |
||||
} |
||||
] |
||||
} |
||||
} |
||||
], |
||||
_links: { |
||||
project: { |
||||
href: "/api/v3/projects/#{project.id}" |
||||
}, |
||||
columns: [ |
||||
{ |
||||
href: "/api/v3/queries/columns/id" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/columns/subject" |
||||
} |
||||
], |
||||
sortBy: [ |
||||
{ |
||||
href: "/api/v3/queries/sort_bys/id-desc" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/sort_bys/assignee-asc" |
||||
} |
||||
], |
||||
groupBy: { |
||||
href: "/api/v3/queries/group_bys/assignee" |
||||
} |
||||
} |
||||
} |
||||
end |
||||
|
||||
it 'has 0 validation errors' do |
||||
expect(form.dig("_embedded", "validationErrors")).to be_empty |
||||
end |
||||
|
||||
it 'has a commit link' do |
||||
expect(form.dig("_links", "commit")).to be_present |
||||
end |
||||
|
||||
it 'has the given name set' do |
||||
expect(form.dig("_embedded", "payload", "name")).to eq parameters[:name] |
||||
end |
||||
|
||||
it 'has the project set' do |
||||
project_link = { |
||||
"href" => "/api/v3/projects/#{project.id}", |
||||
"title" => project.name |
||||
} |
||||
|
||||
expect(form.dig("_embedded", "payload", "_links", "project")).to eq project_link |
||||
end |
||||
|
||||
it 'is set to public' do |
||||
expect(form.dig("_embedded", "payload", "public")).to eq true |
||||
end |
||||
|
||||
it 'has the filters set' do |
||||
filters = [ |
||||
{ |
||||
"_links" => { |
||||
"filter" => { "href" => "/api/v3/queries/filters/status" }, |
||||
"operator" => { "href" => "/api/v3/queries/operators/=" }, |
||||
"values" => [ |
||||
{ "href" => "/api/v3/statuses/#{status.id}" } |
||||
] |
||||
} |
||||
} |
||||
] |
||||
|
||||
expect(form.dig("_embedded", "payload", "filters")).to eq filters |
||||
end |
||||
|
||||
it 'has the columns set' do |
||||
columns = [ |
||||
{ "href" => "/api/v3/queries/columns/id" }, |
||||
{ "href" => "/api/v3/queries/columns/subject" } |
||||
] |
||||
|
||||
expect(form.dig("_embedded", "payload", "_links", "columns")).to eq columns |
||||
end |
||||
|
||||
it 'has the groupBy set' do |
||||
group_by = { "href" => "/api/v3/queries/group_bys/assignee" } |
||||
|
||||
expect(form.dig("_embedded", "payload", "_links", "groupBy")).to eq group_by |
||||
end |
||||
|
||||
it 'has the columns set' do |
||||
sort_by = [ |
||||
{ "href" => "/api/v3/queries/sort_bys/id-desc" }, |
||||
{ "href" => "/api/v3/queries/sort_bys/assignee-asc" } |
||||
] |
||||
|
||||
expect(form.dig("_embedded", "payload", "_links", "sortBy")).to eq sort_by |
||||
end |
||||
|
||||
context "with an unknown filter" do |
||||
let(:override_params) do |
||||
filter = parameters[:filters][0] |
||||
|
||||
filter[:_links][:filter][:href] = "/api/v3/queries/filters/statuz" |
||||
|
||||
{ filters: [filter] } |
||||
end |
||||
|
||||
it "returns a validation error" do |
||||
expect(form.dig("_embedded", "validationErrors", "base", "message")).to eq "Statuz does not exist." |
||||
end |
||||
end |
||||
|
||||
context "with an unknown column" do |
||||
let(:override_params) do |
||||
column = { href: "/api/v3/queries/columns/wurst" } |
||||
links = parameters[:_links] |
||||
|
||||
links[:columns] = links[:columns] + [column] |
||||
|
||||
{ _links: links } |
||||
end |
||||
|
||||
it "returns a validation error" do |
||||
expect(form.dig("_embedded", "validationErrors", "columnNames", "message")) |
||||
.to eq "Invalid query column: wurst" |
||||
end |
||||
end |
||||
|
||||
context "with an invalid groupBy column" do |
||||
let(:override_params) do |
||||
column = { href: "/api/v3/queries/group_bys/foobar" } |
||||
links = parameters[:_links] |
||||
|
||||
links[:groupBy] = column |
||||
|
||||
{ _links: links } |
||||
end |
||||
|
||||
it "returns a validation error" do |
||||
expect(form.dig("_embedded", "validationErrors", "groupBy", "message")) |
||||
.to eq "Can't group by: foobar" |
||||
end |
||||
end |
||||
|
||||
context "with an invalid sort criterion" do |
||||
let(:override_params) do |
||||
sort_criterion = { href: "/api/v3/queries/sort_bys/spentTime-desc" } |
||||
links = parameters[:_links] |
||||
|
||||
links[:sortBy] = links[:sortBy] + [sort_criterion] |
||||
|
||||
{ _links: links } |
||||
end |
||||
|
||||
it "returns a validation error" do |
||||
expect(form.dig("_embedded", "validationErrors", "sortCriteria", "message")) |
||||
.to eq "Can't sort by column: spent_hours" |
||||
end |
||||
end |
||||
|
||||
context "with an unauthorized user trying to set the query public" do |
||||
let(:user) { FactoryGirl.create :user } |
||||
|
||||
it "should reject the request" do |
||||
expect(form.dig("_embedded", "validationErrors", "public", "message")) |
||||
.to eq "Public - The user has no permission to create public queries." |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,173 @@ |
||||
#-- copyright |
||||
# OpenProject is a project management system. |
||||
# Copyright (C) 2012-2017 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-2017 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 'spec_helper' |
||||
|
||||
describe "POST /api/v3/queries", type: :request do |
||||
let(:user) { FactoryGirl.create :admin } |
||||
let(:status) { FactoryGirl.create :status } |
||||
let(:project) { FactoryGirl.create :project } |
||||
|
||||
let(:params) do |
||||
{ |
||||
name: "Dummy Query", |
||||
filters: [ |
||||
{ |
||||
name: "Status", |
||||
_links: { |
||||
filter: { |
||||
href: "/api/v3/queries/filters/status" |
||||
}, |
||||
operator: { |
||||
"href": "/api/v3/queries/operators/=" |
||||
}, |
||||
schema: { |
||||
"href": "/api/v3/queries/filter_instance_schemas/status" |
||||
}, |
||||
values: [ |
||||
{ |
||||
href: "/api/v3/statuses/#{status.id}", |
||||
} |
||||
] |
||||
} |
||||
} |
||||
], |
||||
_links: { |
||||
project: { |
||||
href: "/api/v3/projects/#{project.id}" |
||||
}, |
||||
columns: [ |
||||
{ |
||||
href: "/api/v3/queries/columns/id" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/columns/subject" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/columns/status" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/columns/assignee" |
||||
} |
||||
], |
||||
sortBy: [ |
||||
{ |
||||
href: "/api/v3/queries/sort_bys/id-desc" |
||||
}, |
||||
{ |
||||
href: "/api/v3/queries/sort_bys/assignee-asc" |
||||
} |
||||
], |
||||
groupBy: { |
||||
href: "/api/v3/queries/group_bys/assignee" |
||||
} |
||||
} |
||||
} |
||||
end |
||||
|
||||
before do |
||||
login_as user |
||||
end |
||||
|
||||
describe "creating a query" do |
||||
before do |
||||
post "/api/v3/queries", |
||||
params: params.to_json, |
||||
headers: { "Content-Type": "application/json" } |
||||
end |
||||
|
||||
it 'should return 201 (created)' do |
||||
expect(response.status).to eq(201) |
||||
end |
||||
|
||||
it 'should render the created query' do |
||||
json = JSON.parse(response.body) |
||||
|
||||
expect(json["_type"]).to eq "Query" |
||||
expect(json["name"]).to eq "Dummy Query" |
||||
end |
||||
|
||||
it 'should create the query correctly' do |
||||
query = Query.first |
||||
|
||||
expect(query.group_by_column.name).to eq :assigned_to |
||||
expect(query.sort_criteria).to eq [["id", "desc"], ["assigned_to", "asc"]] |
||||
expect(query.columns.map(&:name)).to eq [:id, :subject, :status, :assigned_to] |
||||
expect(query.user).to eq user |
||||
expect(query.project).to eq project |
||||
end |
||||
end |
||||
|
||||
context "with invalid parameters" do |
||||
def post! |
||||
post "/api/v3/queries", |
||||
params: params.to_json, |
||||
headers: { "Content-Type": "application/json" } |
||||
end |
||||
|
||||
def json |
||||
JSON.parse response.body |
||||
end |
||||
|
||||
it "yields a 404 error given an unknown user" do |
||||
params[:_links][:user][:href] = "/api/v3/users/#{user.id}352" |
||||
|
||||
post! |
||||
|
||||
expect(response.status).to eq 404 |
||||
expect(json["message"]).to eq "User #{user.id}352 not found" |
||||
end |
||||
|
||||
it "yields a 404 error given an unknown project" do |
||||
params[:_links][:project][:href] = "/api/v3/projects/#{project.id}42" |
||||
|
||||
post! |
||||
|
||||
expect(response.status).to eq 404 |
||||
expect(json["message"]).to eq "Project #{project.id}42 not found" |
||||
end |
||||
|
||||
it "yields a 422 error given an unknown operator" do |
||||
params[:filters][0][:_links][:operator][:href] = "/api/v3/queries/operators/wut" |
||||
|
||||
post! |
||||
|
||||
expect(response.status).to eq 422 |
||||
expect(json["message"]).to eq "Status Operator is not included in the list" |
||||
end |
||||
|
||||
it "yields a 422 error given an unknown filter" do |
||||
params[:filters][0][:_links][:filter][:href] = "/api/v3/queries/filters/statuz" |
||||
|
||||
post! |
||||
|
||||
expect(response.status).to eq 422 |
||||
expect(json["message"]).to eq "Statuz does not exist." |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue