From cf950cc75f6b0a2370641b8d91bfe0146a1011ea Mon Sep 17 00:00:00 2001 From: Kabiru Mwenja Date: Fri, 11 Apr 2025 11:09:17 +0300 Subject: [PATCH] Add attachment by activity comment API spec Support contaner aliasing needed from "journal" to "activity" --- lib/api/utilities/property_name_converter.rb | 11 +++ .../v3/attachments/attachment_representer.rb | 4 +- .../attachment_upload_representer.rb | 4 +- .../utilities/property_name_converter_spec.rb | 20 ++++++ .../v3/attachments/activity_comment_spec.rb | 72 +++++++++++++++++++ .../attachment_resource_shared_examples.rb | 4 +- 6 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 spec/requests/api/v3/attachments/activity_comment_spec.rb diff --git a/lib/api/utilities/property_name_converter.rb b/lib/api/utilities/property_name_converter.rb index 5d489d73b67..08a3ea77726 100644 --- a/lib/api/utilities/property_name_converter.rb +++ b/lib/api/utilities/property_name_converter.rb @@ -45,6 +45,17 @@ module API # sense in different contexts. class PropertyNameConverter class << self + def from_ar_name_with_aliases(attribute, ar_name_aliases = {}) + normalized_attribute = attribute.to_s + normalized_aliases = ar_name_aliases.transform_keys(&:to_s) + + if normalized_aliases.key?(normalized_attribute) + normalized_attribute = normalized_aliases[normalized_attribute] + end + + from_ar_name(normalized_attribute) + end + # Converts the attribute name as referred to by ActiveRecord to a corresponding API-conform # attribute name: # * camelCasing the attribute name diff --git a/lib/api/v3/attachments/attachment_representer.rb b/lib/api/v3/attachments/attachment_representer.rb index d4cdb37a4a1..3a0154e885e 100644 --- a/lib/api/v3/attachments/attachment_representer.rb +++ b/lib/api/v3/attachments/attachment_representer.rb @@ -129,7 +129,9 @@ module API end def v3_container_name - ::API::Utilities::PropertyNameConverter.from_ar_name(represented.container.class.name.underscore).underscore + ar_name = represented.container.class.name.underscore + + ::API::Utilities::PropertyNameConverter.from_ar_name_with_aliases(ar_name, "journal" => "activity").underscore end def container_title_attribute diff --git a/lib/api/v3/attachments/attachment_upload_representer.rb b/lib/api/v3/attachments/attachment_upload_representer.rb index 78fb23c82a7..77cb0966099 100644 --- a/lib/api/v3/attachments/attachment_upload_representer.rb +++ b/lib/api/v3/attachments/attachment_upload_representer.rb @@ -141,7 +141,9 @@ module API end def v3_container_name - ::API::Utilities::PropertyNameConverter.from_ar_name(represented.container.class.name.underscore).underscore + ar_name = represented.container.class.name.underscore + + ::API::Utilities::PropertyNameConverter.from_ar_name_with_aliases(ar_name, "journal" => "activity").underscore end def container_title_attribute diff --git a/spec/lib/api/utilities/property_name_converter_spec.rb b/spec/lib/api/utilities/property_name_converter_spec.rb index f7b07be4c37..dbb89131aa2 100644 --- a/spec/lib/api/utilities/property_name_converter_spec.rb +++ b/spec/lib/api/utilities/property_name_converter_spec.rb @@ -29,6 +29,26 @@ require "spec_helper" RSpec.describe API::Utilities::PropertyNameConverter do + describe "#from_ar_name_with_aliases" do + let(:attribute_name) { :an_attribute } + + context "without any attribute aliases" do + it "returns the ar name" do + result = described_class.from_ar_name_with_aliases(attribute_name) + expect(result).to eql("anAttribute") + end + end + + context "with attribute aliases" do + let(:attribute_aliases) { { "an_attribute" => "anAttributeAlias" } } + + it "returns the alias" do + result = described_class.from_ar_name_with_aliases(attribute_name, attribute_aliases) + expect(result).to eql("anAttributeAlias") + end + end + end + describe "#from_ar_name" do let(:attribute_name) { :an_attribute } diff --git a/spec/requests/api/v3/attachments/activity_comment_spec.rb b/spec/requests/api/v3/attachments/activity_comment_spec.rb new file mode 100644 index 00000000000..e70c516d621 --- /dev/null +++ b/spec/requests/api/v3/attachments/activity_comment_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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_relative "attachment_resource_shared_examples" + +RSpec.describe "Activity comment attachments" do + it_behaves_like "an APIv3 attachment resource" do + let(:work_package) do + create(:work_package, author: current_user, project:) + end + + let(:activity) do + work_package.add_journal(user: current_user, notes: "A comment") + work_package.save(validate: false) + work_package.journals.last + end + + let(:attachment_type) { :activity } + + let(:create_permission) { :add_work_package_notes } + let(:read_permission) { :view_work_packages } + let(:update_permission) { :edit_own_work_package_notes } + end + + context "with a restricted journal" do + it_behaves_like "an APIv3 attachment resource" do + let(:work_package) do + create(:work_package, author: current_user, project:) + end + + let(:activity) do + work_package.add_journal(user: current_user, notes: "Need to know!", restricted: true) + work_package.save(validate: false) + work_package.journals.last + end + + let(:attachment_type) { :activity } + + let(:create_permission) { %i[add_work_package_notes add_comments_with_restricted_visibility] } + let(:read_permission) { %i[view_work_packages view_comments_with_restricted_visibility] } + let(:update_permission) { %i[edit_own_work_package_notes edit_own_comments_with_restricted_visibility] } + end + end +end diff --git a/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb b/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb index c5e01aa10a0..081c6a8e0f2 100644 --- a/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb +++ b/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb @@ -547,8 +547,8 @@ RSpec.shared_examples "an APIv3 attachment resource", content_type: :json, type: context "by container", if: include_by_container do it_behaves_like "it supports direct uploads" do - let(:request_path) { "/api/v3/#{attachment_type}s/#{container.id}/attachments/prepare" } - let(:container_href) { "/api/v3/#{attachment_type}s/#{container.id}" } + let(:request_path) { "/api/v3/#{attachment_type.to_s.pluralize}/#{container.id}/attachments/prepare" } + let(:container_href) { "/api/v3/#{attachment_type.to_s.pluralize}/#{container.id}" } end subject(:response) { last_response }