Merge pull request #2153 from opf/feature/16971_work_package_forms_endpoint
16971 work package forms endpointpull/2173/head
commit
4c1bd38aa8
@ -0,0 +1,52 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
module API |
||||
module V3 |
||||
module Utilities |
||||
module ResourceLinkParser |
||||
def self.parse(resource_link) |
||||
API::V3::Root.routes.each do |route| |
||||
route_options = route.instance_variable_get(:@options) |
||||
match = route_options[:compiled].match(resource_link) |
||||
|
||||
if match |
||||
return { |
||||
ns: /\/(?<ns>\w+)\//.match(route_options[:namespace])[:ns], |
||||
id: match[:id] |
||||
} |
||||
end |
||||
end |
||||
|
||||
nil |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,58 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module Form |
||||
class FormAPI < Grape::API |
||||
helpers do |
||||
def process_form_request |
||||
write_work_package_attributes |
||||
write_request_valid? |
||||
|
||||
error = ::API::Errors::ErrorBase.create(@representer.represented.errors) |
||||
|
||||
if error.is_a? ::API::Errors::Validation |
||||
status 200 |
||||
FormRepresenter.new(@representer.represented, current_user: current_user) |
||||
else |
||||
fail error |
||||
end |
||||
end |
||||
|
||||
end |
||||
|
||||
post '/form' do |
||||
process_form_request |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,111 @@ |
||||
#-- 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/json/hal' |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module Form |
||||
class FormRepresenter < Roar::Decorator |
||||
include Roar::JSON::HAL |
||||
include Roar::Hypermedia |
||||
include API::Utilities::UrlHelper |
||||
include OpenProject::TextFormatting |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
def initialize(model, options = {}) |
||||
@current_user = options[:current_user] |
||||
|
||||
super(model) |
||||
end |
||||
|
||||
property :_type, exec_context: :decorator, writeable: false |
||||
|
||||
link :self do |
||||
{ |
||||
href: "#{root_path}api/v3/work_packages/#{represented.id}/form", |
||||
} |
||||
end |
||||
|
||||
link :validate do |
||||
{ |
||||
href: "#{root_path}api/v3/work_packages/#{represented.id}/form", |
||||
method: :post |
||||
} |
||||
end |
||||
|
||||
link :previewMarkup do |
||||
{ |
||||
href: "#{root_path}api/v3/render/textile?"\ |
||||
"#{root_path}api/v3/work_packages/#{represented.id}", |
||||
method: :post |
||||
} |
||||
end |
||||
|
||||
link :commit do |
||||
{ |
||||
href: "#{root_path}api/v3/work_packages/#{represented.id}", |
||||
method: :patch |
||||
} if @current_user.allowed_to?(:edit_work_packages, represented.project) && |
||||
represented.valid? |
||||
end |
||||
|
||||
property :payload, |
||||
embedded: true, |
||||
decorator: Form::WorkPackagePayloadRepresenter, |
||||
getter: -> (*) { self } |
||||
property :schema, |
||||
embedded: true, |
||||
exec_context: :decorator, |
||||
getter: -> (*) { |
||||
Form::WorkPackageSchemaRepresenter.new(represented, |
||||
current_user: @current_user) |
||||
} |
||||
property :validation_errors, embedded: true, exec_context: :decorator |
||||
|
||||
def _type |
||||
'Form' |
||||
end |
||||
|
||||
def validation_errors |
||||
errors = represented.errors |
||||
|
||||
errors.keys.each_with_object({}) do |key, hash| |
||||
error = ::API::Errors::Validation.new(errors.full_message(key, errors[key])) |
||||
hash[key] = ::API::V3::Errors::ErrorRepresenter.new(error) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,59 @@ |
||||
#-- 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/json/hal' |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module Form |
||||
class WorkPackageAttributeLinksRepresenter < Roar::Decorator |
||||
include Roar::JSON::HAL |
||||
include Roar::Hypermedia |
||||
include API::Utilities::UrlHelper |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
property :status, |
||||
exec_context: :decorator, |
||||
getter: -> (*) { |
||||
{ href: "#{root_path}api/v3/statuses/#{represented.status.id}" } |
||||
}, |
||||
setter: -> (value, *) { |
||||
resource = ::API::V3::Utilities::ResourceLinkParser.parse value['href'] |
||||
|
||||
represented.status_id = resource[:id] if resource[:ns] == 'statuses' |
||||
}, |
||||
if: -> (*) { represented.status } |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,104 @@ |
||||
#-- 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/json/hal' |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module Form |
||||
class WorkPackagePayloadRepresenter < Roar::Decorator |
||||
include Roar::JSON::HAL |
||||
include Roar::Hypermedia |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
def initialize(represented, options={}) |
||||
if options[:enforce_lock_version_validation] |
||||
# enforces availibility validation of lock_version |
||||
represented.lock_version = nil |
||||
end |
||||
|
||||
super(represented) |
||||
end |
||||
|
||||
property :_type, exec_context: :decorator, writeable: false |
||||
|
||||
property :linked_resources, |
||||
as: :_links, |
||||
exec_context: :decorator, |
||||
getter: -> (*) { |
||||
work_package_attribute_links_representer represented |
||||
}, |
||||
setter: -> (value, *) { |
||||
representer = work_package_attribute_links_representer represented |
||||
representer.from_json(value.to_json) |
||||
} |
||||
|
||||
property :lock_version |
||||
property :subject, render_nil: true |
||||
property :raw_description, |
||||
getter: -> (*) { description }, |
||||
setter: -> (value, *) { self.description = value }, |
||||
render_nil: true |
||||
property :parent_id, writeable: true |
||||
|
||||
property :project_id, getter: -> (*) { project.id } |
||||
property :start_date, |
||||
getter: -> (*) { |
||||
start_date.to_datetime.utc.iso8601 unless start_date.nil? |
||||
}, |
||||
render_nil: true |
||||
property :due_date, |
||||
getter: -> (*) { |
||||
due_date.to_datetime.utc.iso8601 unless due_date.nil? |
||||
}, |
||||
render_nil: true |
||||
property :version_id, |
||||
getter: -> (*) { fixed_version.try(:id) }, |
||||
setter: -> (value, *) { self.fixed_version_id = value }, |
||||
render_nil: true |
||||
property :created_at, getter: -> (*) { created_at.utc.iso8601 }, render_nil: true |
||||
property :updated_at, getter: -> (*) { updated_at.utc.iso8601 }, render_nil: true |
||||
|
||||
def _type |
||||
'WorkPackage' |
||||
end |
||||
|
||||
private |
||||
|
||||
def work_package_attribute_links_representer(represented) |
||||
::API::V3::WorkPackages::Form::WorkPackageAttributeLinksRepresenter.new represented |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,94 @@ |
||||
#-- 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/json/hal' |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module Form |
||||
class WorkPackageSchemaRepresenter < Roar::Decorator |
||||
include Roar::JSON::HAL |
||||
include Roar::Hypermedia |
||||
include API::Utilities::UrlHelper |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
def initialize(model, options = {}) |
||||
@current_user = options[:current_user] |
||||
|
||||
super(model) |
||||
end |
||||
|
||||
property :_type, |
||||
getter: -> (*) { { type: 'MetaType', required: true, writable: false } }, |
||||
writeable: false |
||||
property :lock_version, |
||||
getter: -> (*) { { type: 'Integer', required: true, writable: false } }, |
||||
writeable: false |
||||
property :subject, |
||||
getter: -> (*) { { type: 'String' } }, |
||||
writeable: false |
||||
property :status, |
||||
exec_context: :decorator, |
||||
getter: -> (*) { represented.new_statuses_allowed_to(@current_user) } do |
||||
include Roar::JSON::HAL |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
property :links_to_allowed_statuses, |
||||
as: :_links, |
||||
getter: -> (*) { self } do |
||||
include API::Utilities::UrlHelper |
||||
|
||||
self.as_strategy = ::API::Utilities::CamelCasingStrategy.new |
||||
|
||||
property :allowed_values, exec_context: :decorator |
||||
|
||||
def allowed_values |
||||
represented.map do |status| |
||||
{ href: "#{root_path}api/v3/statuses/#{status.id}", title: status.name } |
||||
end |
||||
end |
||||
end |
||||
|
||||
property :type, getter: -> (*) { 'Status' } |
||||
|
||||
collection :allowed_values, |
||||
embedded: true, |
||||
class: ::Status, |
||||
decorator: ::API::V3::Statuses::StatusRepresenter, |
||||
getter: -> (*) { self } |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,49 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
module API |
||||
module V3 |
||||
module WorkPackages |
||||
module LinkToObjectExtractor |
||||
def self.parse_links(links) |
||||
links.keys.each_with_object({}) do |attribute, h| |
||||
resource = ::API::V3::Utilities::ResourceLinkParser.parse links[attribute]['href'] |
||||
|
||||
if resource |
||||
case resource[:ns] |
||||
when 'statuses' |
||||
h[:status_id] = resource[:id] |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,129 @@ |
||||
#-- 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 'spec_helper' |
||||
|
||||
describe ::API::V3::WorkPackages::Form::FormRepresenter do |
||||
let(:work_package) { |
||||
FactoryGirl.build(:work_package, |
||||
id: 42, |
||||
created_at: DateTime.now, |
||||
updated_at: DateTime.now) |
||||
} |
||||
let(:current_user) { |
||||
FactoryGirl.build(:user, member_in_project: work_package.project) |
||||
} |
||||
let(:representer) { described_class.new(work_package, current_user: current_user) } |
||||
|
||||
context 'generation' do |
||||
subject(:generated) { representer.to_json } |
||||
|
||||
it { is_expected.to be_json_eql('Form'.to_json).at_path('_type') } |
||||
|
||||
describe '_links' do |
||||
it { is_expected.to have_json_path('_links') } |
||||
|
||||
it { is_expected.to have_json_path('_links/self/href') } |
||||
|
||||
describe 'validate' do |
||||
it { is_expected.to have_json_path('_links/validate/href') } |
||||
|
||||
it { is_expected.to be_json_eql(:post.to_json).at_path('_links/validate/method') } |
||||
end |
||||
|
||||
describe 'preview markup' do |
||||
it { is_expected.to have_json_path('_links/previewMarkup/href') } |
||||
|
||||
it { is_expected.to be_json_eql(:post.to_json).at_path('_links/previewMarkup/method') } |
||||
|
||||
it 'contains link to work package' do |
||||
body = parse_json(subject) |
||||
preview_markup_wp_link = body['_links']['previewMarkup']['href'].split('?')[1] |
||||
wp_self_link = body['_links']['commit']['href'] |
||||
|
||||
expect(preview_markup_wp_link).to eq(wp_self_link) |
||||
end |
||||
end |
||||
|
||||
describe 'commit' do |
||||
context 'valid work package' do |
||||
before { allow(work_package).to receive(:valid?).and_return(true) } |
||||
|
||||
it { is_expected.to have_json_path('_links/commit/href') } |
||||
|
||||
it { is_expected.to be_json_eql(:patch.to_json).at_path('_links/commit/method') } |
||||
end |
||||
|
||||
context 'invalid work package' do |
||||
before { allow(work_package).to receive(:valid?).and_return(false) } |
||||
|
||||
it { is_expected.not_to have_json_path('_links/commit/href') } |
||||
end |
||||
|
||||
context 'user with insufficient permissions' do |
||||
let(:role) { FactoryGirl.create(:role, permissions: []) } |
||||
let(:current_user) { |
||||
FactoryGirl.build(:user, |
||||
member_in_project: work_package.project, |
||||
member_through_role: role) |
||||
} |
||||
|
||||
before { allow(work_package).to receive(:valid?).and_return(true) } |
||||
|
||||
it { is_expected.not_to have_json_path('_links/commit/href') } |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'validation errors' do |
||||
context 'w/o errors' do |
||||
it { is_expected.to be_json_eql({}.to_json).at_path('_embedded/validationErrors') } |
||||
end |
||||
|
||||
context 'with errors' do |
||||
let(:subject_error_message) { 'Subject can\'t be blank!' } |
||||
let(:status_error_message) { 'Status can\'t be blank!' } |
||||
let(:subject_error) { ::API::Errors::Validation.new(subject_error_message) } |
||||
let(:status_error) { ::API::Errors::Validation.new(status_error_message) } |
||||
let(:api_subject_error) { ::API::V3::Errors::ErrorRepresenter.new(subject_error) } |
||||
let(:api_status_error) { ::API::V3::Errors::ErrorRepresenter.new(status_error) } |
||||
let(:errors) { { subject: api_subject_error, status: api_status_error } } |
||||
|
||||
before do |
||||
allow(work_package.errors).to receive(:keys).and_return(errors.keys) |
||||
allow(work_package.errors).to receive(:full_message).with(:subject, []) |
||||
.and_return(subject_error_message) |
||||
allow(work_package.errors).to receive(:full_message).with(:status, []) |
||||
.and_return(status_error_message) |
||||
end |
||||
|
||||
it { is_expected.to be_json_eql(errors.to_json).at_path('_embedded/validationErrors') } |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,67 @@ |
||||
#-- 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 'spec_helper' |
||||
|
||||
describe ::API::V3::WorkPackages::Form::WorkPackagePayloadRepresenter do |
||||
let(:work_package) { |
||||
FactoryGirl.build(:work_package, |
||||
created_at: DateTime.now, |
||||
updated_at: DateTime.now) |
||||
} |
||||
let(:representer) { described_class.new(work_package) } |
||||
|
||||
before { allow(work_package).to receive(:lock_version).and_return(1) } |
||||
|
||||
context 'generation' do |
||||
subject(:generated) { representer.to_json } |
||||
|
||||
it { is_expected.to include_json('WorkPackage'.to_json).at_path('_type') } |
||||
|
||||
describe 'work_package' do |
||||
it { is_expected.to have_json_path('subject') } |
||||
it { is_expected.to have_json_path('rawDescription') } |
||||
|
||||
describe 'lock version' do |
||||
it { is_expected.to have_json_path('lockVersion') } |
||||
|
||||
it { is_expected.to have_json_type(Integer).at_path('lockVersion') } |
||||
|
||||
it { is_expected.to be_json_eql(work_package.lock_version.to_json).at_path('lockVersion') } |
||||
end |
||||
end |
||||
|
||||
describe '_links' do |
||||
it { is_expected.to have_json_type(Object).at_path('_links') } |
||||
|
||||
it 'should link status' do |
||||
expect(subject).to have_json_path('_links/status/href') |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,126 @@ |
||||
#-- 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 'spec_helper' |
||||
|
||||
describe ::API::V3::WorkPackages::Form::WorkPackageSchemaRepresenter do |
||||
let(:work_package) { FactoryGirl.build(:work_package) } |
||||
let(:current_user) { |
||||
FactoryGirl.build(:user, member_in_project: work_package.project) |
||||
} |
||||
let(:representer) { described_class.new(work_package, current_user: current_user) } |
||||
|
||||
context 'generation' do |
||||
subject(:generated) { representer.to_json } |
||||
|
||||
describe 'schema' do |
||||
shared_examples_for 'schema property' do |path, type, required, writable| |
||||
it { is_expected.to have_json_path(path) } |
||||
|
||||
it { is_expected.to be_json_eql(type.to_json).at_path("#{path}/type") } |
||||
|
||||
it 'has valid required value' do |
||||
required_path = "#{path}/required" |
||||
|
||||
if required.nil? |
||||
is_expected.not_to have_json_path(required_path) |
||||
else |
||||
is_expected.to be_json_eql(required.to_json).at_path(required_path) |
||||
end |
||||
end |
||||
|
||||
it 'has valid writable value' do |
||||
writable_path = "#{path}/writable" |
||||
|
||||
if writable.nil? |
||||
is_expected.not_to have_json_path(writable_path) |
||||
else |
||||
is_expected.to be_json_eql(writable.to_json).at_path(writable_path) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '_type' do |
||||
it_behaves_like 'schema property', '_type', 'MetaType', true, false |
||||
end |
||||
|
||||
describe 'lock version' do |
||||
it_behaves_like 'schema property', 'lockVersion', 'Integer', true, false |
||||
end |
||||
|
||||
describe 'subject' do |
||||
it_behaves_like 'schema property', 'subject', 'String' |
||||
end |
||||
|
||||
describe 'status' do |
||||
shared_examples_for 'contains statuses' do |
||||
it { is_expected.to have_json_path('status') } |
||||
|
||||
it { is_expected.to have_json_path('status/_links') } |
||||
|
||||
it { is_expected.to have_json_path('status/_links/allowedValues') } |
||||
|
||||
it 'contains valid links to statuses' do |
||||
status_links = statuses.map do |status| |
||||
{ href: "/api/v3/statuses/#{status.id}", title: status.name } |
||||
end |
||||
|
||||
is_expected.to be_json_eql(status_links.to_json).at_path('status/_links/allowedValues') |
||||
end |
||||
|
||||
it { is_expected.to be_json_eql('Status'.to_json).at_path('status/type') } |
||||
|
||||
it 'embeds statuses' do |
||||
embedded_statuses = statuses.map do |status| |
||||
{ _type: 'Status', id: status.id, name: status.name } |
||||
end |
||||
|
||||
is_expected.to be_json_eql(embedded_statuses.to_json) |
||||
.at_path('status/_embedded/allowedValues') |
||||
end |
||||
end |
||||
|
||||
context 'w/o allowed statuses' do |
||||
before { allow(work_package).to receive(:new_statuses_allowed_to).and_return([]) } |
||||
|
||||
it_behaves_like 'contains statuses' do |
||||
let(:statuses) { [] } |
||||
end |
||||
end |
||||
|
||||
context 'with allowed statuses' do |
||||
let(:statuses) { FactoryGirl.build_list(:status, 3) } |
||||
|
||||
before { allow(work_package).to receive(:new_statuses_allowed_to).and_return(statuses) } |
||||
|
||||
it_behaves_like 'contains statuses' |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,283 @@ |
||||
#-- 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 'spec_helper' |
||||
require 'rack/test' |
||||
|
||||
describe 'API v3 Work package form resource', type: :request do |
||||
include Rack::Test::Methods |
||||
include Capybara::RSpecMatchers |
||||
|
||||
let(:project) { FactoryGirl.create(:project, is_public: false) } |
||||
let(:work_package) { FactoryGirl.create(:work_package, project: project) } |
||||
let(:authorized_user) { FactoryGirl.create(:user, member_in_project: project) } |
||||
let(:unauthorized_user) { FactoryGirl.create(:user) } |
||||
|
||||
describe '#post' do |
||||
let(:post_path) { "/api/v3/work_packages/#{work_package.id}/form" } |
||||
let(:valid_params) do |
||||
{ |
||||
_type: 'WorkPackage', |
||||
lockVersion: work_package.lock_version |
||||
} |
||||
end |
||||
|
||||
subject(:response) { last_response } |
||||
|
||||
shared_context 'post request' do |
||||
before(:each) do |
||||
allow(User).to receive(:current).and_return current_user |
||||
post post_path, params.to_json, 'CONTENT_TYPE' => 'application/json' |
||||
end |
||||
end |
||||
|
||||
context 'user without needed permissions' do |
||||
let(:work_package) { FactoryGirl.create(:work_package, id: 42, project: project) } |
||||
let(:params) { {} } |
||||
|
||||
include_context 'post request' do |
||||
let(:current_user) { unauthorized_user } |
||||
end |
||||
|
||||
it_behaves_like 'not found', 42, 'WorkPackage' |
||||
end |
||||
|
||||
context 'user with needed permissions' do |
||||
let(:params) {} |
||||
let(:current_user) { authorized_user } |
||||
|
||||
context 'non-existing work package' do |
||||
let(:post_path) { '/api/v3/work_packages/eeek/form' } |
||||
|
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'not found', 'eeek', 'WorkPackage' |
||||
end |
||||
|
||||
context 'existing work package' do |
||||
shared_examples_for 'valid payload' do |
||||
it { expect(response.status).to eq(200) } |
||||
|
||||
it { expect(subject.body).to have_json_path('_embedded/payload') } |
||||
|
||||
it { expect(subject.body).to have_json_path('_embedded/payload/lockVersion') } |
||||
|
||||
it { expect(subject.body).to have_json_path('_embedded/payload/subject') } |
||||
|
||||
it { expect(subject.body).to have_json_path('_embedded/payload/rawDescription') } |
||||
end |
||||
|
||||
shared_examples_for 'valid payload with initial values' do |
||||
it { |
||||
expect(subject.body).to be_json_eql(work_package.lock_version.to_json) |
||||
.at_path('_embedded/payload/lockVersion') |
||||
} |
||||
|
||||
it { |
||||
expect(subject.body).to be_json_eql(work_package.subject.to_json) |
||||
.at_path('_embedded/payload/subject') |
||||
} |
||||
|
||||
it { |
||||
expect(subject.body).to be_json_eql(work_package.description.to_json) |
||||
.at_path('_embedded/payload/rawDescription') |
||||
} |
||||
end |
||||
|
||||
shared_examples_for 'having no errors' do |
||||
it { |
||||
expect(subject.body).to be_json_eql({}.to_json) |
||||
.at_path('_embedded/validationErrors') |
||||
} |
||||
end |
||||
|
||||
shared_examples_for 'having an error' do |property| |
||||
it { expect(subject.body).to have_json_path("_embedded/validationErrors/#{property}") } |
||||
|
||||
describe 'error body' do |
||||
let(:error_id) { 'urn:openproject-org:api:v3:errors:PropertyConstraintViolation' } |
||||
|
||||
let(:error_body) { |
||||
parse_json(subject.body)['_embedded']['validationErrors'][property] |
||||
} |
||||
|
||||
it { expect(error_body['errorIdentifier']).to eq(error_id) } |
||||
end |
||||
end |
||||
|
||||
describe 'body' do |
||||
context 'empty' do |
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'valid payload with initial values' |
||||
|
||||
it_behaves_like 'having no errors' |
||||
end |
||||
|
||||
context 'filled' do |
||||
let(:valid_params) do |
||||
{ |
||||
_type: 'WorkPackage', |
||||
lockVersion: work_package.lock_version |
||||
} |
||||
end |
||||
|
||||
describe 'no change' do |
||||
let(:params) { valid_params } |
||||
|
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'valid payload with initial values' |
||||
|
||||
it_behaves_like 'having no errors' |
||||
end |
||||
|
||||
describe 'lock version' do |
||||
context 'missing lock version' do |
||||
let(:params) { valid_params.except(:lockVersion) } |
||||
|
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'update conflict' |
||||
end |
||||
|
||||
context 'stale object' do |
||||
let(:params) { valid_params.merge(subject: 'Updated subject') } |
||||
|
||||
before do |
||||
params |
||||
|
||||
work_package.subject = 'I am the first!' |
||||
work_package.save! |
||||
|
||||
expect(valid_params[:lockVersion]).not_to eq(work_package.lock_version) |
||||
end |
||||
|
||||
include_context 'post request' |
||||
|
||||
it { expect(response.status).to eq(409) } |
||||
|
||||
it_behaves_like 'update conflict' |
||||
end |
||||
end |
||||
|
||||
describe 'subject' do |
||||
include_context 'post request' |
||||
|
||||
context 'valid subject' do |
||||
let(:params) { valid_params.merge(subject: 'Updated subject') } |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'having no errors' |
||||
|
||||
it 'should respond with updated work package subject' do |
||||
expect(subject.body).to be_json_eql('Updated subject'.to_json) |
||||
.at_path('_embedded/payload/subject') |
||||
end |
||||
end |
||||
|
||||
context 'invalid subject' do |
||||
let(:params) { valid_params.merge(subject: nil) } |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'having an error', 'subject' |
||||
|
||||
it 'should respond with updated work package subject' do |
||||
expect(subject.body).to be_json_eql(nil.to_json) |
||||
.at_path('_embedded/payload/subject') |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'description' do |
||||
let(:path) { '_embedded/payload/rawDescription' } |
||||
let(:description) { '*Some text* _describing_ *something*...' } |
||||
let(:params) { valid_params.merge(rawDescription: description) } |
||||
|
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'having no errors' |
||||
|
||||
it 'should respond with updated work package description' do |
||||
expect(subject.body).to be_json_eql(description.to_json).at_path(path) |
||||
end |
||||
end |
||||
|
||||
describe 'status' do |
||||
let(:path) { '_embedded/payload/_links/status/href' } |
||||
let(:target_status) { FactoryGirl.create(:status) } |
||||
let(:status_link) { "/api/v3/statuses/#{target_status.id}" } |
||||
let(:status_parameter) { { _links: { status: { href: status_link } } } } |
||||
let(:params) { valid_params.merge(status_parameter) } |
||||
|
||||
context 'valid status' do |
||||
let!(:workflow) { |
||||
FactoryGirl.create(:workflow, |
||||
type_id: work_package.type.id, |
||||
old_status: work_package.status, |
||||
new_status: target_status, |
||||
role: current_user.memberships[0].roles[0]) |
||||
} |
||||
|
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'having no errors' |
||||
|
||||
it 'should respond with updated work package status' do |
||||
expect(subject.body).to be_json_eql(status_link.to_json).at_path(path) |
||||
end |
||||
end |
||||
|
||||
context 'invalid status' do |
||||
include_context 'post request' |
||||
|
||||
it_behaves_like 'valid payload' |
||||
|
||||
it_behaves_like 'having an error', 'status_id' |
||||
|
||||
it 'should respond with updated work package status' do |
||||
expect(subject.body).to be_json_eql(status_link.to_json).at_path(path) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue