Skip to content

Commit

Permalink
Merge pull request #16884 from opf/implementation/58105-item-persiste…
Browse files Browse the repository at this point in the history
…nce-service

Implementation/58105 item persistence service
  • Loading branch information
Andreas Pfohl authored Oct 8, 2024
2 parents 44714ee + 06599b2 commit a91efed
Show file tree
Hide file tree
Showing 9 changed files with 626 additions and 0 deletions.
43 changes: 43 additions & 0 deletions app/contracts/custom_fields/hierarchy/generate_root_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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 CustomFields
module Hierarchy
class GenerateRootContract < Dry::Validation::Contract
params do
required(:hierarchy_root)
end

rule(:hierarchy_root) do
key.failure("Hierarchical root already set") unless value.nil?
end
end
end
end
57 changes: 57 additions & 0 deletions app/contracts/custom_fields/hierarchy/insert_item_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 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 CustomFields
module Hierarchy
class InsertItemContract < Dry::Validation::Contract
params do
required(:parent).filled
required(:label).filled(:string)
optional(:short).filled(:string)
end

rule(:parent) do
if value.is_a?(CustomField::Hierarchy::Item)
unless value.persisted?
key.failure("Parent must exist")
end
else
key.failure("Parent must be of type 'Item'")
end
end

rule(:label) do
if CustomField::Hierarchy::Item.exists?(parent_id: values[:parent], label: value)
key.failure("Label must be unique within the same hierarchy level")
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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 CustomFields
module Hierarchy
class ServiceInitializationContract < Dry::Validation::Contract
params do
required(:field_format).filled(:string)
end

rule(:field_format) do
key.failure("Custom field must have field format 'hierarchy'") if value != "hierarchy"
end
end
end
end
79 changes: 79 additions & 0 deletions app/services/custom_fields/hierarchy/hierarchical_item_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 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 CustomFields
module Hierarchy
class HierarchicalItemService
include Dry::Monads[:result]

def initialize(custom_field)
validation = ServiceInitializationContract.new.call(field_format: custom_field.field_format)
# rubocop:disable Rails/DeprecatedActiveModelErrorsMethods
raise ArgumentError, "Invalid custom field: #{validation.errors.to_h}" if validation.failure?
# rubocop:enable Rails/DeprecatedActiveModelErrorsMethods

@custom_field = custom_field
end

def generate_root
CustomFields::Hierarchy::GenerateRootContract
.new
.call(hierarchy_root: @custom_field.hierarchy_root)
.to_monad
.bind { create_root_item }
end

def insert_item(parent:, label:, short: nil)
CustomFields::Hierarchy::InsertItemContract
.new
.call({ parent:, label:, short: }.compact)
.to_monad
.bind { |validation| create_child_item(validation) }
end

private

def create_root_item
item = CustomField::Hierarchy::Item.create(custom_field: @custom_field)
return Failure(item.errors) unless item.persisted?

Success(item)
end

def create_child_item(validation)
item = CustomField::Hierarchy::Item
.create(parent: validation[:parent], label: validation[:label], short: validation[:short])
return Failure(item.errors) unless item.persisted?

Success(item)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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 "rails_helper"

RSpec.describe CustomFields::Hierarchy::GenerateRootContract do
subject { described_class.new }

describe "#call" do
context "when hierarchy_root is nil" do
let(:custom_field) { create(:custom_field, field_format: "hierarchy", hierarchy_root: nil) }

it "is valid" do
result = subject.call(hierarchy_root: custom_field.hierarchy_root)
expect(result).to be_success
end
end

context "when hierarchy_root is not nil" do
let(:hierarchy_root) { create(:hierarchy_item) }
let(:custom_field) { create(:custom_field, field_format: "hierarchy", hierarchy_root:) }

it "is invalid" do
result = subject.call(hierarchy_root: custom_field.hierarchy_root)
expect(result).to be_failure
# rubocop:disable Rails/DeprecatedActiveModelErrorsMethods
expect(result.errors.to_h).to include(hierarchy_root: ["Hierarchical root already set"])
# rubocop:enable Rails/DeprecatedActiveModelErrorsMethods
end
end

context "when inputs are valid" do
it "creates a success result" do
[
{ hierarchy_root: nil }
].each { |params| expect(subject.call(params)).to be_success }
end
end

context "when inputs are invalid" do
it "creates a failure result" do
[
{},
{ hierarchy_root: create(:hierarchy_item) },
{ hierarchy_root: "" },
{ hierarchy_root: 42 }
].each { |params| expect(subject.call(params)).to be_failure }
end
end
end
end
Loading

0 comments on commit a91efed

Please sign in to comment.