From a34c017d6ef9dfccdf5e5d785f75483bd9f8c7da Mon Sep 17 00:00:00 2001 From: Marcello Rocha Date: Tue, 24 Dec 2024 10:13:57 +0100 Subject: [PATCH] Implements all the non-custom-fields tokens --- app/models/types/pattern.rb | 2 +- ...{pattern_mapper.rb => pattern_resolver.rb} | 37 +++------ app/models/types/patterns/token.rb | 8 +- .../types/patterns/token_property_mapper.rb | 75 +++++++++++++++++++ ...apper_spec.rb => pattern_resolver_spec.rb} | 4 +- .../patterns/token_property_mapper_spec.rb | 55 ++++++++++++++ 6 files changed, 144 insertions(+), 37 deletions(-) rename app/models/types/{pattern_mapper.rb => pattern_resolver.rb} (68%) create mode 100644 app/models/types/patterns/token_property_mapper.rb rename spec/models/types/{pattern_mapper_spec.rb => pattern_resolver_spec.rb} (94%) create mode 100644 spec/models/types/patterns/token_property_mapper_spec.rb diff --git a/app/models/types/pattern.rb b/app/models/types/pattern.rb index 8442fcac254f..8efe8eac5c41 100644 --- a/app/models/types/pattern.rb +++ b/app/models/types/pattern.rb @@ -33,7 +33,7 @@ module Types def enabled? = !!enabled def resolve(work_package) - PatternMapper.new(self).resolve(work_package) + PatternResolver.new(blueprint).resolve(work_package) end def to_h diff --git a/app/models/types/pattern_mapper.rb b/app/models/types/pattern_resolver.rb similarity index 68% rename from app/models/types/pattern_mapper.rb rename to app/models/types/pattern_resolver.rb index 4444c231850e..b63e4f56319d 100644 --- a/app/models/types/pattern_mapper.rb +++ b/app/models/types/pattern_resolver.rb @@ -29,31 +29,16 @@ #++ module Types - class PatternMapper + class PatternResolver TOKEN_REGEX = /{{[0-9A-Za-z_]+}}/ - - MAPPING = { - type: ->(wp) { wp.type.name }, - assignee: ->(wp) { wp.assigned_to&.name }, - created: ->(wp) { wp.created_at }, - author: ->(wp) { wp.author.name }, - parent_id: ->(wp) { wp.parent&.id }, - project_name: ->(wp) { wp.project.name } - }.freeze - - private_constant :MAPPING + private_constant :TOKEN_REGEX def initialize(pattern) + @mapper = Patterns::TokenPropertyMapper.new @pattern = pattern @tokens = pattern.scan(TOKEN_REGEX).map { |token| Patterns::Token.build(token) } end - def valid?(work_package) - @tokens.each { |token| get_value(work_package, token) } - rescue NoMethodError - false - end - def resolve(work_package) @tokens.inject(@pattern) do |pattern, token| pattern.gsub(token.pattern, get_value(work_package, token)) @@ -63,17 +48,13 @@ def resolve(work_package) private def get_value(work_package, token) - raw_value = if token.custom_field? && token.context != work_package.context - fn(key).call(work_package.public_send(token.context)) - else - fn(token.key).call(work_package) - end - - stringify(raw_value) - end + context = if token.custom_field_context == :work_package + work_package + else + work_package.public_send(token.context) + end - def fn(key) - MAPPING.fetch(key) { ->(wp) { wp.public_send(key.to_sym) } } + stringify(@mapper[token.key].call(context)) end def stringify(value) diff --git a/app/models/types/patterns/token.rb b/app/models/types/patterns/token.rb index 442ee9bf3367..ee9ee41a0b57 100644 --- a/app/models/types/patterns/token.rb +++ b/app/models/types/patterns/token.rb @@ -39,13 +39,9 @@ def self.build(pattern) def custom_field? = key.to_s.include?("custom_field") - def custom_field_id - return nil unless custom_field? + def context + return :work_package unless custom_field? - Integer(key.to_s.gsub(/\D+/, "")) - end - - def custom_field_context context = key.to_s.gsub(/_?custom_field_\d+/, "") return :work_package if context.blank? diff --git a/app/models/types/patterns/token_property_mapper.rb b/app/models/types/patterns/token_property_mapper.rb new file mode 100644 index 000000000000..5a416624795c --- /dev/null +++ b/app/models/types/patterns/token_property_mapper.rb @@ -0,0 +1,75 @@ +# 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 Types + module Patterns + class TokenPropertyMapper + MAPPING = { + accountable: ->(wp) { wp.responsible&.name }, + assignee: ->(wp) { wp.assigned_to&.name }, + author: ->(wp) { wp.author&.name }, + category: ->(wp) { wp.category&.name }, + creation_date: ->(wp) { wp.created_at }, + estimated_time: ->(wp) { wp.estimated_hours }, + finish_date: ->(wp) { wp.due_date }, + parent: ->(wp) { wp.parent&.id }, + parent_author: ->(wp) { wp.parent&.author&.name }, + parent_category: ->(wp) { wp.parent&.category&.name }, + parent_creation_date: ->(wp) { wp.parent&.created_at }, + parent_estimated_time: ->(wp) { wp.parent&.estimated_hours }, + parent_finish_date: ->(wp) { wp.parent&.due_date }, + parent_priority: ->(wp) { wp.parent&.priority }, + priority: ->(wp) { wp.priority }, + project: ->(wp) { wp.project_id }, + project_active: ->(wp) { wp.project&.active? }, + project_name: ->(wp) { wp.project&.name }, + project_status: ->(wp) { wp.project&.status_code }, + project_parent: ->(wp) { wp.project&.parent_id }, + project_public: ->(wp) { wp.project&.public? }, + start_date: ->(wp) { wp.start_date }, + status: ->(wp) { wp.status&.name }, + type: ->(wp) { wp.type&.name } + }.freeze + + def fetch(key) + MAPPING.fetch(key) { ->(context) { context.public_send(key.to_sym) } } + end + + alias :[] :fetch + + def tokens_for_type(_type) + [] + # Fetch all CustomFields for type + # Fetch all customFields prefixed as parent + # fetch all project attributes prefixed as project + end + end + end +end diff --git a/spec/models/types/pattern_mapper_spec.rb b/spec/models/types/pattern_resolver_spec.rb similarity index 94% rename from spec/models/types/pattern_mapper_spec.rb rename to spec/models/types/pattern_resolver_spec.rb index 470d4813c5f3..f1de10e0f8ff 100644 --- a/spec/models/types/pattern_mapper_spec.rb +++ b/spec/models/types/pattern_resolver_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" -RSpec.describe Types::PatternMapper do +RSpec.describe Types::PatternResolver do let(:subject_pattern) { "ID Please: {{id}}" } let(:work_package) { create(:work_package) } @@ -41,7 +41,7 @@ end context "when the pattern has WorkPackage properties" do - let(:subject_pattern) { "{{id}} | {{done_ratio}} | {{created}}" } + let(:subject_pattern) { "{{id}} | {{done_ratio}} | {{creation_date}}" } it "resolves the pattern" do expect(subject.resolve(work_package)) diff --git a/spec/models/types/patterns/token_property_mapper_spec.rb b/spec/models/types/patterns/token_property_mapper_spec.rb new file mode 100644 index 000000000000..7b276944929b --- /dev/null +++ b/spec/models/types/patterns/token_property_mapper_spec.rb @@ -0,0 +1,55 @@ +# 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" + +RSpec.describe Types::Patterns::TokenPropertyMapper do + shared_let(:responsible) { create(:user, firstname: "Responsible") } + shared_let(:assignee) { create(:user, firstname: "Assignee") } + + shared_let(:category) { create(:category) } + + shared_let(:project) { create(:project, parent: create(:project), status_code: 1, status_explanation: "A Mess") } + + shared_let(:work_package_parent) do + create(:work_package, project:, category:, start_date: Date.yesterday, estimated_hours: 120, due_date: 3.months.from_now) + end + shared_let(:work_package) do + create(:work_package, responsible:, project:, category:, due_date: 1.month.from_now, + assigned_to: assignee, parent: work_package_parent, start_date: Time.zone.today, estimated_hours: 30) + end + + described_class::MAPPING.each_pair do |key, fn| + it "the token named #{key} resolves successfully" do + expect { fn.call(work_package) }.not_to raise_error + expect(fn.call(work_package)).not_to be_nil + end + end +end