diff --git a/.rubocop.yml b/.rubocop.yml index fc623bc2bf6..12079947632 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -100,6 +100,10 @@ Naming/AccessorMethodName: Naming/AsciiIdentifiers: Enabled: false +Naming/ClassAndModuleCamelCase: + AllowedNames: + - V2_1 + Naming/FileName: Enabled: false diff --git a/app/contracts/journals/update_contract.rb b/app/contracts/journals/update_contract.rb new file mode 100644 index 00000000000..39218a41f86 --- /dev/null +++ b/app/contracts/journals/update_contract.rb @@ -0,0 +1,43 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Journals + class UpdateContract < BaseContract + attribute :notes + + validate :user_allowed_to_edit + + private + + def user_allowed_to_edit + errors.add(:base, :error_unauthorized) unless model.editable_by?(user) + end + end +end diff --git a/app/contracts/work_packages/update_contract.rb b/app/contracts/work_packages/update_contract.rb index d46272ccf0b..b6bd58471c6 100644 --- a/app/contracts/work_packages/update_contract.rb +++ b/app/contracts/work_packages/update_contract.rb @@ -28,8 +28,6 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'work_packages/base_contract' - module WorkPackages class UpdateContract < BaseContract include UnchangedProject diff --git a/app/services/journals/create_service.rb b/app/services/journals/create_service.rb index 4e8eefbbc93..6ee33f16870 100644 --- a/app/services/journals/create_service.rb +++ b/app/services/journals/create_service.rb @@ -1,3 +1,33 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + # Will create journals for a journable (e.g. WorkPackage and Meeting) # As a journal is basically a copy of the current state of the database, consisting of the journable as well as its # custom values and attachments, those entries are copied in the database. diff --git a/app/services/journals/set_attributes_service.rb b/app/services/journals/set_attributes_service.rb new file mode 100644 index 00000000000..6cea1e1c2ce --- /dev/null +++ b/app/services/journals/set_attributes_service.rb @@ -0,0 +1,33 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Journals + class SetAttributesService < ::BaseServices::SetAttributes; end +end diff --git a/app/services/journals/update_service.rb b/app/services/journals/update_service.rb new file mode 100644 index 00000000000..19369a96088 --- /dev/null +++ b/app/services/journals/update_service.rb @@ -0,0 +1,33 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Journals + class UpdateService < ::BaseServices::Update; end +end diff --git a/db/migrate/20210910092414_add_bcf_comment_hierarchy.rb b/db/migrate/20210910092414_add_bcf_comment_hierarchy.rb new file mode 100644 index 00000000000..09f2ac3ba0f --- /dev/null +++ b/db/migrate/20210910092414_add_bcf_comment_hierarchy.rb @@ -0,0 +1,6 @@ +class AddBcfCommentHierarchy < ActiveRecord::Migration[6.1] + def change + add_column :bcf_comments, :reply_to, :bigint, default: nil, null: true + add_foreign_key :bcf_comments, :bcf_comments, column: :reply_to, on_delete: :nullify + end +end diff --git a/lib/api/v3/activities/activities_api.rb b/lib/api/v3/activities/activities_api.rb index c12a3d213db..1f7282522d1 100644 --- a/lib/api/v3/activities/activities_api.rb +++ b/lib/api/v3/activities/activities_api.rb @@ -26,8 +26,6 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'api/v3/activities/activity_representer' - module API module V3 module Activities @@ -42,18 +40,6 @@ module API end end - helpers do - def save_activity(activity) - unless activity.save - fail ::API::Errors::ErrorBase.create_and_merge_errors(activity.errors) - end - end - - def authorize_edit_own(activity) - authorize_by_with_raise activity.editable_by?(current_user) - end - end - get &::API::V3::Utilities::Endpoints::Show.new(model: ::Journal, api_name: 'Activity', instance_generator: ->(*) { @activity }) @@ -63,14 +49,13 @@ module API requires :comment, type: String end - patch do - # TODO: Write a journal update notes service and mount default endpoint - authorize_edit_own(@activity) - @activity.notes = declared_params[:comment] - save_activity(@activity) - - ActivityRepresenter.new(@activity, current_user: current_user) - end + patch &::API::V3::Utilities::Endpoints::Update.new(model: ::Journal, + api_name: 'Activity', + instance_generator: ->(*) { @activity }, + params_modifier: ->(*) { + { notes: declared_params[:comment] } + }) + .mount end end end diff --git a/lib/api/v3/activities/activity_payload_representer.rb b/lib/api/v3/activities/activity_payload_representer.rb new file mode 100644 index 00000000000..298e099769c --- /dev/null +++ b/lib/api/v3/activities/activity_payload_representer.rb @@ -0,0 +1,39 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module API + module V3 + module Activities + class ActivityPayloadRepresenter < ::API::V3::Activities::ActivityRepresenter + include API::Utilities::PayloadRepresenter + end + end + end +end diff --git a/modules/bim/app/contracts/bim/bcf/comments/create_contract.rb b/modules/bim/app/contracts/bim/bcf/comments/create_contract.rb new file mode 100644 index 00000000000..c191708ba9d --- /dev/null +++ b/modules/bim/app/contracts/bim/bcf/comments/create_contract.rb @@ -0,0 +1,61 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf + module Comments + class CreateContract < BaseContract + attribute :journal + attribute :issue + attribute :viewpoint + attribute :reply_to + + validate :user_allowed_to_create + validate :validate_journal + validate :validate_viewpoint_reference + validate :validate_reply_to_comment + + private + + def user_allowed_to_create + errors.add :base, :error_unauthorized unless @user.allowed_to?(:manage_bcf, model.issue.work_package.project) + end + + def validate_journal + errors.add(:base, :invalid) if model.journal.journable != model.issue.work_package + end + + def validate_viewpoint_reference + errors.add(:viewpoint, :does_not_exist) if model.viewpoint.is_a?(::Bim::Bcf::NonExistentViewpoint) + end + + def validate_reply_to_comment + errors.add(:bcf_comment, :does_not_exist) if model.reply_to.is_a?(::Bim::Bcf::NonExistentComment) + end + end + end +end diff --git a/modules/bim/app/contracts/bim/bcf/comments/update_contract.rb b/modules/bim/app/contracts/bim/bcf/comments/update_contract.rb new file mode 100644 index 00000000000..c7105bd12c2 --- /dev/null +++ b/modules/bim/app/contracts/bim/bcf/comments/update_contract.rb @@ -0,0 +1,54 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf + module Comments + class UpdateContract < BaseContract + attribute :viewpoint + attribute :reply_to + + validate :user_allowed_to_update + validate :validate_viewpoint_reference + validate :validate_reply_to_comment + + private + + def user_allowed_to_update + errors.add :base, :error_unauthorized unless @user.allowed_to?(:manage_bcf, model.issue.work_package.project) + end + + def validate_viewpoint_reference + errors.add(:viewpoint, :does_not_exist) if model.viewpoint.is_a?(::Bim::Bcf::NonExistentViewpoint) + end + + def validate_reply_to_comment + errors.add(:bcf_comment, :does_not_exist) if model.reply_to.is_a?(::Bim::Bcf::NonExistentComment) + end + end + end +end diff --git a/modules/bim/app/contracts/bim/bcf/viewpoints/create_contract.rb b/modules/bim/app/contracts/bim/bcf/viewpoints/create_contract.rb index cda08b5cad3..504ba111263 100644 --- a/modules/bim/app/contracts/bim/bcf/viewpoints/create_contract.rb +++ b/modules/bim/app/contracts/bim/bcf/viewpoints/create_contract.rb @@ -104,7 +104,7 @@ module Bim::Bcf end def validate_json_viewpoint_present - errors.add(:json_viewpoint, :blank) unless viewpoint.present? + errors.add(:json_viewpoint, :blank) if viewpoint.blank? end def validate_json_viewpoint_hash diff --git a/modules/bim/app/controllers/bim/bcf/api/v2_1/comments/api.rb b/modules/bim/app/controllers/bim/bcf/api/v2_1/comments/api.rb new file mode 100644 index 00000000000..11e3877783f --- /dev/null +++ b/modules/bim/app/controllers/bim/bcf/api/v2_1/comments/api.rb @@ -0,0 +1,102 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf::API::V2_1 + module Comments + class API < ::API::OpenProjectAPI + resources :comments do + helpers do + def all_comments + @issue.comments.includes(:journal, :issue, :viewpoint) + end + + def transform_create_parameter(params) + viewpoint = if params[:viewpoint_guid] == nil + nil + else + @issue.viewpoints + .find_by(uuid: params[:viewpoint_guid]) || ::Bim::Bcf::NonExistentViewpoint.new + end + replied_comment = if params[:reply_to_comment_guid] == nil + nil + else + @issue.comments + .find_by(uuid: params[:reply_to_comment_guid]) || ::Bim::Bcf::NonExistentComment.new + end + { + issue: @issue, + viewpoint: viewpoint, + reply_to: replied_comment + } + end + + def transform_update_parameter(params) + viewpoint = if params[:viewpoint_guid] == nil + nil + else + @issue.viewpoints + .find_by(uuid: params[:viewpoint_guid]) || ::Bim::Bcf::NonExistentViewpoint.new + end + replied_comment = if params[:reply_to_comment_guid] == nil + nil + else + @issue.comments + .find_by(uuid: params[:reply_to_comment_guid]) || ::Bim::Bcf::NonExistentComment.new + end + + { + original_comment: @comment, + viewpoint: viewpoint, + reply_to: replied_comment + } + end + end + + get &::Bim::Bcf::API::V2_1::Endpoints::Index.new(model: Bim::Bcf::Comment, scope: -> { all_comments }).mount + + post &::Bim::Bcf::API::V2_1::Endpoints::Create + .new(model: Bim::Bcf::Comment, + params_modifier: ->(params) { transform_create_parameter(params).merge(params) }) + .mount + + route_param :comment_guid, regexp: /\A[a-f0-9\-]+\z/ do + after_validation do + @comment = all_comments.find_by!(uuid: params[:comment_guid]) + end + + get &::Bim::Bcf::API::V2_1::Endpoints::Show.new(model: Bim::Bcf::Comment).mount + + put &::Bim::Bcf::API::V2_1::Endpoints::Update + .new(model: Bim::Bcf::Comment, + params_modifier: ->(params) { transform_update_parameter(params).merge(params) }) + .mount + end + end + end + end +end diff --git a/modules/bim/app/controllers/bim/bcf/api/v2_1/projects_api.rb b/modules/bim/app/controllers/bim/bcf/api/v2_1/projects_api.rb index 11ab1962bd0..1ea7c280ece 100644 --- a/modules/bim/app/controllers/bim/bcf/api/v2_1/projects_api.rb +++ b/modules/bim/app/controllers/bim/bcf/api/v2_1/projects_api.rb @@ -28,7 +28,9 @@ # See COPYRIGHT and LICENSE files for more details. #++ +# rubocop:disable Naming/ClassAndModuleCamelCase module Bim::Bcf::API::V2_1 + # rubocop:enable Naming/ClassAndModuleCamelCase class ProjectsAPI < ::API::OpenProjectAPI resources :projects do helpers do diff --git a/modules/bim/app/controllers/bim/bcf/api/v2_1/topics_api.rb b/modules/bim/app/controllers/bim/bcf/api/v2_1/topics_api.rb index 6a8a8b3e884..183ad44cc69 100644 --- a/modules/bim/app/controllers/bim/bcf/api/v2_1/topics_api.rb +++ b/modules/bim/app/controllers/bim/bcf/api/v2_1/topics_api.rb @@ -28,7 +28,9 @@ # See COPYRIGHT and LICENSE files for more details. #++ +# rubocop:disable Naming/ClassAndModuleCamelCase module Bim::Bcf::API::V2_1 + # rubocop:enable Naming/ClassAndModuleCamelCase class TopicsAPI < ::API::OpenProjectAPI resources :topics do helpers do @@ -85,7 +87,7 @@ module Bim::Bcf::API::V2_1 route_param :topic_uuid, regexp: /\A[a-f0-9\-]+\z/ do after_validation do - @issue = topics.find_by_uuid!(params[:topic_uuid]) + @issue = topics.find_by!(uuid: params[:topic_uuid]) end get &::Bim::Bcf::API::V2_1::Endpoints::Show @@ -108,6 +110,7 @@ module Bim::Bcf::API::V2_1 .mount mount ::Bim::Bcf::API::V2_1::Viewpoints::API + mount ::Bim::Bcf::API::V2_1::Comments::API end end end diff --git a/modules/bim/app/models/bim/bcf/comment.rb b/modules/bim/app/models/bim/bcf/comment.rb index a52180b60af..38c4d01ed50 100644 --- a/modules/bim/app/models/bim/bcf/comment.rb +++ b/modules/bim/app/models/bim/bcf/comment.rb @@ -1,18 +1,50 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + module Bim::Bcf class Comment < ActiveRecord::Base self.table_name = :bcf_comments include InitializeWithUuid + CREATE_ATTRIBUTES = %i[journal issue viewpoint reply_to].freeze + UPDATE_ATTRIBUTES = %i[viewpoint reply_to].freeze + belongs_to :journal belongs_to :issue, foreign_key: :issue_id, class_name: "Bim::Bcf::Issue" belongs_to :viewpoint, foreign_key: :viewpoint_id, class_name: "Bim::Bcf::Viewpoint", optional: true + belongs_to :reply_to, foreign_key: :reply_to, class_name: "Bim::Bcf::Comment", optional: true validates_presence_of :uuid validates_uniqueness_of :uuid, scope: [:issue_id] def self.has_uuid?(uuid, issue_id) - where(uuid: uuid, issue_id: issue_id).exists? + exists?(uuid: uuid, issue_id: issue_id) end end end diff --git a/modules/bim/app/models/bim/bcf/non_existent_comment.rb b/modules/bim/app/models/bim/bcf/non_existent_comment.rb new file mode 100644 index 00000000000..c80cf89b04f --- /dev/null +++ b/modules/bim/app/models/bim/bcf/non_existent_comment.rb @@ -0,0 +1,4 @@ +module Bim::Bcf + class NonExistentComment < Comment + end +end diff --git a/modules/bim/app/models/bim/bcf/non_existent_viewpoint.rb b/modules/bim/app/models/bim/bcf/non_existent_viewpoint.rb new file mode 100644 index 00000000000..338bf97274c --- /dev/null +++ b/modules/bim/app/models/bim/bcf/non_existent_viewpoint.rb @@ -0,0 +1,4 @@ +module Bim::Bcf + class NonExistentViewpoint < Viewpoint + end +end diff --git a/modules/bim/app/representers/bim/bcf/api/v2_1/base_representer.rb b/modules/bim/app/representers/bim/bcf/api/v2_1/base_representer.rb index 5b0d20313ef..8aca129ccd5 100644 --- a/modules/bim/app/representers/bim/bcf/api/v2_1/base_representer.rb +++ b/modules/bim/app/representers/bim/bcf/api/v2_1/base_representer.rb @@ -33,5 +33,9 @@ module Bim::Bcf::API::V2_1 include Representable::JSON defaults render_nil: true + + def datetime_formatter + ::API::V3::Utilities::DateTimeFormatter + end end end diff --git a/modules/bim/app/representers/bim/bcf/api/v2_1/comments/authorization_representer.rb b/modules/bim/app/representers/bim/bcf/api/v2_1/comments/authorization_representer.rb new file mode 100644 index 00000000000..97c8a135854 --- /dev/null +++ b/modules/bim/app/representers/bim/bcf/api/v2_1/comments/authorization_representer.rb @@ -0,0 +1,46 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +# rubocop:disable Naming/ClassAndModuleCamelCase +module Bim::Bcf::API::V2_1 + # rubocop:enable Naming/ClassAndModuleCamelCase + class Comments::AuthorizationRepresenter < BaseRepresenter + property :comment_actions, + getter: ->(decorator:, **) { + if decorator.manage_bcf_allowed? + %w[update] + else + [] + end + } + + def manage_bcf_allowed? + represented.user.allowed_to?(:manage_bcf, represented.model.project) + end + end +end diff --git a/modules/bim/app/representers/bim/bcf/api/v2_1/comments/single_representer.rb b/modules/bim/app/representers/bim/bcf/api/v2_1/comments/single_representer.rb new file mode 100644 index 00000000000..6558ab6a710 --- /dev/null +++ b/modules/bim/app/representers/bim/bcf/api/v2_1/comments/single_representer.rb @@ -0,0 +1,88 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +# rubocop:disable Naming/ClassAndModuleCamelCase +module Bim::Bcf::API::V2_1 + # rubocop:enable Naming/ClassAndModuleCamelCase + class Comments::SingleRepresenter < BaseRepresenter + include API::Decorators::DateProperty + + property :uuid, + as: :guid + + property :date, + getter: ->(represented:, decorator:, **) { + decorator.datetime_formatter.format_datetime(represented.journal.created_at, allow_nil: true) + } + + property :author, + getter: ->(represented:, **) { + represented.journal.user.mail + } + + property :comment, + getter: ->(represented:, **) { + represented.journal.notes + } + + property :topic_guid, + getter: ->(represented:, **) { + represented.issue.uuid + } + + # not required properties + property :viewpoint_guid, + getter: ->(represented:, **) { + represented.viewpoint&.uuid + } + + property :reply_to_comment_guid, + getter: ->(represented:, **) { + represented.reply_to&.uuid + } + + property :modified_date, + getter: ->(represented:, decorator:, **) { + decorator.datetime_formatter.format_datetime(represented.journal.updated_at, allow_nil: true) + } + + # we do not store the author when editing a journal, hence the "modified author" is the same as the creator + property :modified_author, + getter: ->(represented:, **) { + represented.journal.user.mail + } + + property :authorization, + getter: ->(represented:, **) { + contract = WorkPackages::UpdateContract.new(represented.issue.work_package, User.current) + Comments::AuthorizationRepresenter.new(contract) + } + end +end diff --git a/modules/bim/app/services/bim/bcf/comments/create_service.rb b/modules/bim/app/services/bim/bcf/comments/create_service.rb new file mode 100644 index 00000000000..8ea32e06f9e --- /dev/null +++ b/modules/bim/app/services/bim/bcf/comments/create_service.rb @@ -0,0 +1,52 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf + module Comments + class CreateService < ::BaseServices::Create + private + + def before_perform(params) + journal_call = create_journal(params[:issue].work_package, + params[:comment]) + return journal_call if journal_call.failure? + + input = { journal: journal_call.result } + .merge(params) + .slice(*::Bim::Bcf::Comment::CREATE_ATTRIBUTES) + super input + end + + def create_journal(work_package, comment) + ::Journals::CreateService.new(work_package, user).call(notes: comment) + end + end + end +end diff --git a/modules/bim/app/services/bim/bcf/comments/set_attributes_service.rb b/modules/bim/app/services/bim/bcf/comments/set_attributes_service.rb new file mode 100644 index 00000000000..b7e2b8f8f2a --- /dev/null +++ b/modules/bim/app/services/bim/bcf/comments/set_attributes_service.rb @@ -0,0 +1,36 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf + module Comments + class SetAttributesService < ::BaseServices::SetAttributes + end + end +end diff --git a/modules/bim/app/services/bim/bcf/comments/update_service.rb b/modules/bim/app/services/bim/bcf/comments/update_service.rb new file mode 100644 index 00000000000..71bacc49fc7 --- /dev/null +++ b/modules/bim/app/services/bim/bcf/comments/update_service.rb @@ -0,0 +1,49 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +module Bim::Bcf + module Comments + class UpdateService < ::BaseServices::Update + private + + def before_perform(params) + journal_call = update_journal(params[:original_comment].journal, params[:comment]) + return journal_call if journal_call.failure? + + super params.slice(*::Bim::Bcf::Comment::UPDATE_ATTRIBUTES) + end + + def update_journal(journal, comment) + ::Journals::UpdateService.new(user: user, + model: journal, + contract_class: ::EmptyContract) + .call(notes: comment) + end + end + end +end diff --git a/modules/bim/spec/factories/bcf_comment_factory.rb b/modules/bim/spec/factories/bcf_comment_factory.rb index c93896dbab0..ce847d61aa9 100644 --- a/modules/bim/spec/factories/bcf_comment_factory.rb +++ b/modules/bim/spec/factories/bcf_comment_factory.rb @@ -28,8 +28,16 @@ FactoryBot.define do factory :bcf_comment, class: '::Bim::Bcf::Comment' do - after(:create) do |bcf_comment| - bcf_comment.journal = create(:work_package_journal) + transient do + author { nil } + end + + after(:create) do |bcf_comment, evaluator| + bcf_comment.journal = if evaluator.author == nil + create(:work_package_journal) + else + create(:work_package_journal, user: evaluator.author) + end bcf_comment.journal.update_attribute(:notes, 'Some BCF comment.') bcf_comment.journal.save bcf_comment.save diff --git a/modules/bim/spec/requests/api/bcf/v2_1/comments_api_spec.rb b/modules/bim/spec/requests/api/bcf/v2_1/comments_api_spec.rb new file mode 100644 index 00000000000..4a292742dc0 --- /dev/null +++ b/modules/bim/spec/requests/api/bcf/v2_1/comments_api_spec.rb @@ -0,0 +1,611 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# 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 COPYRIGHT and LICENSE files for more details. +#++ + +require 'spec_helper' +require 'rack/test' + +require_relative './shared_responses' + +describe 'BCF 2.1 comments resource', type: :request, content_type: :json, with_mail: false do + include Rack::Test::Methods + include API::V3::Utilities::PathHelper + + let(:project) do + FactoryBot.create(:project, enabled_module_names: %i[bim work_package_tracking]) + end + + let(:view_only_user) do + FactoryBot.create(:user, + member_in_project: project, + member_with_permissions: %i[view_linked_issues view_work_packages]) + end + + let(:edit_user) do + FactoryBot.create(:user, + member_in_project: project, + member_with_permissions: %i[view_linked_issues view_work_packages manage_bcf]) + end + + let(:user_without_permission) { FactoryBot.create(:user, member_in_project: project) } + + let(:assignee) { FactoryBot.create(:user) } + + let(:work_package) do + FactoryBot.create(:work_package, assigned_to: assignee, due_date: Date.today, project: project) + end + + let(:bcf_issue) { FactoryBot.create(:bcf_issue_with_viewpoint, work_package: work_package) } + let(:viewpoint) { bcf_issue.viewpoints.first } + + let(:bcf_comment) { FactoryBot.create(:bcf_comment, issue: bcf_issue, author: view_only_user) } + let(:bcf_answer) { FactoryBot.create(:bcf_comment, issue: bcf_issue, reply_to: bcf_comment, author: assignee) } + let(:bcf_comment_to_viewpoint) do + FactoryBot.create(:bcf_comment, issue: bcf_issue, viewpoint: viewpoint, author: edit_user) + end + + subject(:response) { last_response } + + describe 'GET /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments' do + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments" } + let(:current_user) { view_only_user } + let(:comments) { bcf_comment } + + before do + login_as(current_user) + comments + get path + end + + it_behaves_like 'bcf api successful response' do + let(:comments) { [bcf_comment, bcf_answer, bcf_comment_to_viewpoint] } + + let(:expected_body) do + [ + { + guid: bcf_comment.uuid, + date: bcf_comment.journal.created_at, + author: view_only_user.mail, + comment: bcf_comment.journal.notes, + modified_date: bcf_comment.journal.updated_at, + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [] + } + }, + { + guid: bcf_answer.uuid, + date: bcf_answer.journal.created_at, + author: assignee.mail, + comment: bcf_answer.journal.notes, + modified_date: bcf_answer.journal.updated_at, + modified_author: assignee.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: bcf_comment.uuid, + viewpoint_guid: nil, + authorization: { + comment_actions: [] + } + }, + { + guid: bcf_comment_to_viewpoint.uuid, + date: bcf_comment_to_viewpoint.journal.created_at, + author: edit_user.mail, + comment: bcf_comment_to_viewpoint.journal.notes, + modified_date: bcf_comment_to_viewpoint.journal.updated_at, + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: viewpoint.uuid, + authorization: { + comment_actions: [] + } + } + ] + end + end + + context 'with edit comments permission' do + let(:current_user) { edit_user } + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + [ + { + guid: bcf_comment.uuid, + date: bcf_comment.journal.created_at, + author: view_only_user.mail, + comment: bcf_comment.journal.notes, + modified_date: bcf_comment.journal.updated_at, + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + ] + end + end + end + + context 'without view permissions' do + let(:current_user) { user_without_permission } + + it_behaves_like 'bcf api not allowed response' + end + end + + describe 'POST /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments' do + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments" } + let(:current_user) { edit_user } + let(:params) do + { + comment: "this is a new bcf comment" + } + end + + before do + login_as(current_user) + post path, params.to_json + end + + it_behaves_like 'bcf api successful response' do + let(:expected_status) { 201 } + let(:expected_body) do + comment = Bim::Bcf::Comment.last.reload + creation_date = comment&.journal&.created_at&.iso8601 + + { + guid: comment&.uuid, + date: creation_date, + author: edit_user.mail, + comment: "this is a new bcf comment", + modified_date: creation_date, + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + context 'if user has no permission to write comments' do + let(:current_user) { view_only_user } + + it_behaves_like 'bcf api not allowed response' + end + + context 'if request contains viewpoint guid' do + let(:params) do + { + comment: "this is a comment to a specific viewpoint", + viewpoint_guid: viewpoint.uuid + } + end + + it_behaves_like 'bcf api successful response' do + let(:expected_status) { 201 } + let(:expected_body) do + comment = Bim::Bcf::Comment.last.reload + creation_date = comment&.journal&.created_at&.iso8601 + + { + guid: comment&.uuid, + date: creation_date, + author: edit_user.mail, + comment: "this is a comment to a specific viewpoint", + modified_date: creation_date, + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: viewpoint.uuid, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + context 'if the viewpoint guid does not exist' do + let(:params) do + { + comment: "this is a comment to a specific viewpoint", + viewpoint_guid: "00000000-0000-0000-0000-000000000000" + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Viewpoint does not exist.' } + end + end + end + + context 'if request contains reply comment' do + let(:params) do + { + comment: "this is a reply comment to another comment", + reply_to_comment_guid: bcf_comment.uuid + } + end + + it_behaves_like 'bcf api successful response' do + let(:expected_status) { 201 } + let(:expected_body) do + comment = Bim::Bcf::Comment.last.reload + creation_date = comment&.journal&.created_at&.iso8601 + + { + guid: comment&.uuid, + date: creation_date, + author: edit_user.mail, + comment: "this is a reply comment to another comment", + modified_date: creation_date, + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: bcf_comment.uuid, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + context 'if the comment guid does not exist' do + let(:params) do + { + comment: "this is a reply comment to another comment", + reply_to_comment_guid: "00000000-0000-0000-0000-000000000000" + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Bcf comment does not exist.' } + end + end + end + + context 'if request contains reply comment and viewpoint reference' do + let(:params) do + { + comment: "this is a reply comment to another comment with a specific reference to a viewpoint", + reply_to_comment_guid: bcf_comment.uuid, + viewpoint_guid: viewpoint.uuid + } + end + + it_behaves_like 'bcf api successful response' do + let(:expected_status) { 201 } + let(:expected_body) do + comment = Bim::Bcf::Comment.last.reload + creation_date = comment&.journal&.created_at&.iso8601 + + { + guid: comment&.uuid, + date: creation_date, + author: edit_user.mail, + comment: "this is a reply comment to another comment with a specific reference to a viewpoint", + modified_date: creation_date, + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: bcf_comment.uuid, + viewpoint_guid: viewpoint.uuid, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + context 'if the comment guid does not exist' do + let(:params) do + { + comment: "this is a reply comment to another comment", + reply_to_comment_guid: "00000000-0000-0000-0000-000000000000", + viewpoint_guid: viewpoint.uuid + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Bcf comment does not exist.' } + end + end + + context 'if the viewpoint guid does not exist' do + let(:params) do + { + comment: "this is a reply comment to another comment", + viewpoint_guid: "00000000-0000-0000-0000-000000000000", + reply_to_comment_guid: bcf_comment.uuid + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Viewpoint does not exist.' } + end + end + + context 'if the comment and viewpoint guid does not exist' do + let(:params) do + { + comment: "this is a reply comment to another comment", + reply_to_comment_guid: "00000000-0000-0000-0000-000000000000", + viewpoint_guid: "00000000-0000-0000-0000-000000000000" + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Multiple field constraints have been violated. Viewpoint does not exist. Bcf comment does not exist.' } + end + end + end + end + + describe 'PUT /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments' do + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments" } + let(:current_user) { edit_user } + let(:params) { { comment: "This is a bad comment update ... " } } + + before do + login_as(current_user) + put path, params.to_json + end + + it_behaves_like 'bcf api method not allowed response' + end + + describe 'GET /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments/:comment_guid' do + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments/#{bcf_comment.uuid}" } + let(:current_user) { view_only_user } + let(:comments) { [bcf_comment, bcf_answer, bcf_comment_to_viewpoint] } + + before do + login_as(current_user) + comments + get path + end + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + { + guid: bcf_comment.uuid, + date: bcf_comment.journal.created_at, + author: view_only_user.mail, + comment: bcf_comment.journal.notes, + modified_date: bcf_comment.journal.updated_at, + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { comment_actions: [] } + } + end + end + + context 'if user has editing permissions' do + let(:current_user) { edit_user } + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + { + guid: bcf_comment.uuid, + date: bcf_comment.journal.created_at, + author: view_only_user.mail, + comment: bcf_comment.journal.notes, + modified_date: bcf_comment.journal.updated_at, + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + end + + context 'if comment id does not exist' do + let(:invalid_id) { "1337" } + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments/#{invalid_id}" } + + it_behaves_like 'bcf api not found response' + end + + context 'without view permissions' do + let(:current_user) { user_without_permission } + + it_behaves_like 'bcf api not allowed response' + end + end + + describe 'PUT /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments/:comment_guid' do + let(:updated_comment) { bcf_comment } + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments/#{updated_comment.uuid}" } + let(:current_user) { edit_user } + let(:params) { { comment: "Update comment to this elaborate text." } } + + before do + login_as(current_user) + put path, params.to_json + end + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + { + guid: updated_comment.uuid, + date: updated_comment.journal.created_at, + author: view_only_user.mail, + comment: "Update comment to this elaborate text.", + modified_date: updated_comment.journal.updated_at, + # we cannot store another author then the journal creator + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + context 'if user has no edit permissions' do + let(:current_user) { view_only_user } + + it_behaves_like 'bcf api not allowed response' + end + + context 'if viewpoint reference and reply to is changed' do + let(:params) do + { + comment: "A new updated text", + viewpoint_guid: viewpoint.uuid, + reply_to_comment_guid: bcf_comment_to_viewpoint.uuid + } + end + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + { + guid: updated_comment.uuid, + date: updated_comment.journal.created_at, + author: view_only_user.mail, + comment: "A new updated text", + modified_date: updated_comment.journal.updated_at, + # we cannot store another author then the journal creator + modified_author: view_only_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: bcf_comment_to_viewpoint.uuid, + viewpoint_guid: viewpoint.uuid, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + end + + context 'if an invalid viewpoint guid is given' do + let(:params) do + { + comment: "A new updated text", + viewpoint_guid: "00000000-0000-0000-0000-000000000000" + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Viewpoint does not exist.' } + end + end + + context 'if an invalid comment guid is given' do + let(:params) do + { + comment: "A new updated text", + reply_to_comment_guid: "00000000-0000-0000-0000-000000000000" + } + end + + it_behaves_like 'bcf api unprocessable response' do + let(:message) { 'Bcf comment does not exist.' } + end + end + + context 'if the updated comment contains viewpoint reference and is a reply, but update does not set those attributes' do + let(:updated_comment) do + FactoryBot.create(:bcf_comment, + issue: bcf_issue, + viewpoint: viewpoint, + reply_to: bcf_comment, + author: edit_user) + end + + let(:params) { { comment: "Only change the comment text and leave the reply and viewpoint guid empty." } } + + it_behaves_like 'bcf api successful response' do + let(:expected_body) do + { + guid: updated_comment.uuid, + date: updated_comment.journal.created_at, + author: edit_user.mail, + comment: "Only change the comment text and leave the reply and viewpoint guid empty.", + modified_date: updated_comment.journal.updated_at, + # we cannot store another author then the journal creator + modified_author: edit_user.mail, + topic_guid: bcf_issue.uuid, + reply_to_comment_guid: nil, + viewpoint_guid: nil, + authorization: { + comment_actions: [ + "update" + ] + } + } + end + end + + end + end + + describe 'POST /api/bcf/2.1/projects/:project_id/topics/:topic_guid/comments/:comment_guid' do + let(:path) { "/api/bcf/2.1/projects/#{project.id}/topics/#{bcf_issue.uuid}/comments/#{bcf_comment.uuid}" } + let(:current_user) { edit_user } + let(:params) { { comment: "This is an invalid try to create a comment ..." } } + + before do + login_as(current_user) + post path, params.to_json + end + + it_behaves_like 'bcf api method not allowed response' + end +end diff --git a/modules/bim/spec/requests/api/bcf/v2_1/projects_api_spec.rb b/modules/bim/spec/requests/api/bcf/v2_1/projects_api_spec.rb index 84911af73a8..c3c13d2ad37 100644 --- a/modules/bim/spec/requests/api/bcf/v2_1/projects_api_spec.rb +++ b/modules/bim/spec/requests/api/bcf/v2_1/projects_api_spec.rb @@ -48,6 +48,7 @@ describe 'BCF 2.1 projects resource', type: :request, content_type: :json do end let(:project) { FactoryBot.create(:project, enabled_module_names: [:bim]) } + subject(:response) { last_response } describe 'GET /api/bcf/2.1/projects/:project_id' do diff --git a/modules/bim/spec/requests/api/bcf/v2_1/shared_responses.rb b/modules/bim/spec/requests/api/bcf/v2_1/shared_responses.rb index f25b85f7b3e..9265f1e305d 100644 --- a/modules/bim/spec/requests/api/bcf/v2_1/shared_responses.rb +++ b/modules/bim/spec/requests/api/bcf/v2_1/shared_responses.rb @@ -28,12 +28,15 @@ shared_examples_for 'bcf api successful response' do def expect_identical_without_time(subject, expected_body) - # Remove modified date body = Array.wrap(JSON.parse(subject.body)) - Array.wrap(expected_body).each_with_index do |expected_item, index| + expected = Array.wrap(expected_body) + expect(body.size).to eql(expected.size) + + expected.each_with_index do |expected_item, index| subject_body = body[index] expected_item.stringify_keys! + # Remove date strings and compare separately subject_modified_date = subject_body.delete('modified_date')&.to_time expected_modified_date = expected_item.delete('modified_date')&.to_time @@ -43,6 +46,15 @@ shared_examples_for 'bcf api successful response' do expect(subject_modified_date).to eql(expected_modified_date) end + subject_created_date = subject_body.delete('date')&.to_time + expected_created_date = expected_item.delete('date')&.to_time + + if expected_created_date + expect(subject_created_date).to be_within(10.seconds).of(expected_created_date) + else + expect(subject_created_date).to eql(expected_created_date) + end + expect(subject_body.to_json).to be_json_eql(expected_item.to_json) end end @@ -83,6 +95,16 @@ shared_examples_for 'bcf api not found response' do end end +shared_examples_for 'bcf api method not allowed response' do + let(:expect_405) { "405 Not Allowed" } + + it 'responds 405 METHOD NOT ALLOWED', :aggregate_failures do + expect(subject.status).to eq 405 + expect(subject.body).to eql(expect_405) + expect(subject.headers['Content-Type']).to eql 'application/json; charset=utf-8' + end +end + shared_examples_for 'bcf api not allowed response' do let(:expect_403) do { message: 'You are not authorized to access this resource.' }