Creates the PageLinks::CreateService (#23270)

* Creates CreateContract and SetAttributesService
* Rename the contract for clarity
* Adds the actual create service
* Adds subclass methods to InexistentProvider
* Simplyfy validation methods and adds extra presence checks.
This commit is contained in:
Marcello Rocha
2026-05-26 10:26:49 +02:00
committed by GitHub
parent 344cb17994
commit 99a4b32bcf
7 changed files with 386 additions and 0 deletions
@@ -0,0 +1,66 @@
# 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.
#++
module Wikis
module PageLinks
class RelationPageLinkCreateContract < ::ModelContract
attribute :author
attribute :identifier
attribute :linkable
attribute :provider
validates :identifier, presence: true
validates :linkable, presence: true
validates :provider, presence: true
validate :provider_exists?
validate :author_must_be_user
validate :validate_user_allowed_to_manage
private
def author_must_be_user
errors.add(:author, :invalid) unless author == user
end
def validate_user_allowed_to_manage
linkable = model.linkable
if linkable.present? && !user.allowed_in_project?(:manage_wiki_page_links, linkable.project)
errors.add(:base, :error_unauthorized)
end
end
def provider_exists?
errors.add(:provider, :does_not_exist) if model.provider.is_a?(InexistentProvider)
end
end
end
end
@@ -0,0 +1,41 @@
# 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.
#++
module Wikis
class InexistentProvider < Wikis::Provider
include InexistentModel
def self.visible? = false
def self.registry_prefix = :inexistent
def user_connected?(_user) = false
end
end
@@ -0,0 +1,39 @@
# 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.
#++
module Wikis
module PageLinks
class CreateService < ::BaseServices::Create
private
def default_contract_class = RelationPageLinkCreateContract
end
end
end
@@ -0,0 +1,36 @@
# 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.
#++
module Wikis
module PageLinks
class SetAttributesService < ::BaseServices::SetAttributes
end
end
end
@@ -0,0 +1,64 @@
# 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 "contracts/shared/model_contract_shared_context"
require_module_spec_helper
module Wikis
module PageLinks
RSpec.describe RelationPageLinkCreateContract do
include_context "ModelContract shared context"
let(:linkable) { create(:work_package) }
let(:project) { linkable.project }
let(:current_user) { create(:user, member_with_permissions: { project => %i(manage_wiki_page_links view_work_packages) }) }
let(:relation_page_link) { build_stubbed(:relation_wiki_page_link, author: current_user, linkable:) }
subject(:contract) { described_class.new(relation_page_link, current_user) }
it_behaves_like "contract is valid"
context "when creator is not the current user" do
let(:author) { create(:user, member_with_permissions: { project => %i(manage_wiki_page_links view_work_packages) }) }
let(:relation_page_link) { build_stubbed(:relation_wiki_page_link, author:, linkable:) }
include_examples "contract is invalid", author: :invalid
end
context "when the provider is inexistent" do
let(:provider) { InexistentProvider.new }
before { relation_page_link.provider = provider }
include_examples "contract is invalid", provider: :does_not_exist
end
end
end
end
@@ -0,0 +1,51 @@
# 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 "services/base_services/behaves_like_create_service"
require_module_spec_helper
module Wikis
module PageLinks
RSpec.describe CreateService do
it_behaves_like "BaseServices create service" do
let(:contract_class) { RelationPageLinkCreateContract }
let(:factory) { :relation_wiki_page_link }
end
it "defaults to the RelationPageLinkCreateContract" do
service = described_class.new(user: nil)
expect(service.contract_class).to eq(RelationPageLinkCreateContract)
end
end
end
end
@@ -0,0 +1,89 @@
# 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_module_spec_helper
module Wikis
module PageLinks
RSpec.describe SetAttributesService do
let(:model_instance) { RelationPageLink.new }
let(:contract_instance) do
instance_double(RelationPageLinkCreateContract, validate: contract_valid, errors: contract_errors)
end
let(:contract_class) do
class_double(RelationPageLinkCreateContract, new: contract_instance)
end
let(:contract_errors) { instance_double(ActiveModel::Errors) }
let(:contract_valid) { true }
let(:current_user) { build(:user) }
let(:service) do
described_class.new(user: current_user, model: model_instance, contract_class:, contract_options: {})
end
let(:params) { {} }
subject(:result) { service.call(params) }
it "returns the instance as the result" do
expect(subject.result).to eql model_instance
end
it "is a success" do
expect(subject).to be_success
end
context "with params" do
let(:params) { { identifier: "Foobar" } }
it "assigns the params" do
service.call(params)
expect(model_instance.identifier).to eq "Foobar"
end
end
context "with an invalid contract" do
let(:contract_valid) { false }
it "returns failure" do
expect(subject).to be_failure
end
it "returns the contract's errors" do
expect(subject.errors).to eql(contract_errors)
end
end
end
end
end