From abc685b71e76e3181b37966e037b923dc5c4789e Mon Sep 17 00:00:00 2001 From: Jens Ulferts Date: Thu, 20 Apr 2017 10:02:01 +0200 Subject: [PATCH] adapt backlogs type filter to core changes --- .../backlogs_type_representer.rb | 62 +++++++++ .../backlogs_type_dependency_representer.rb | 44 +++++-- lib/open_project/backlogs/engine.rb | 11 +- .../backlogs/work_package_filter.rb | 119 ++++++++++++------ spec/features/work_packages/filter_spec.rb | 116 +++++++++++++++++ 5 files changed, 299 insertions(+), 53 deletions(-) create mode 100644 lib/api/v3/backlogs_types/backlogs_type_representer.rb create mode 100644 spec/features/work_packages/filter_spec.rb diff --git a/lib/api/v3/backlogs_types/backlogs_type_representer.rb b/lib/api/v3/backlogs_types/backlogs_type_representer.rb new file mode 100644 index 0000000000..7da13e5783 --- /dev/null +++ b/lib/api/v3/backlogs_types/backlogs_type_representer.rb @@ -0,0 +1,62 @@ +#-- copyright +# OpenProject Backlogs Plugin +# +# Copyright (C)2013-2014 the OpenProject Foundation (OPF) +# Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda +# Copyright (C)2010-2011 friflaj +# Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns +# Copyright (C)2009-2010 Mark Maglana +# Copyright (C)2009 Joe Heck, Nate Lowrie +# +# 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 Backlogs is a derivative work based on ChiliProject Backlogs. +# The copyright follows: +# Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj +# Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany +# +# 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 doc/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module BacklogsTypes + class BacklogsTypeRepresenter < ::API::Decorators::Single + self_link path: :backlogs_type, + id_attribute: ->(*) { represented.last }, + title_getter: ->(*) { represented.first } + + property :id, + exec_context: :decorator + + property :name, + exec_context: :decorator + + private + + def id + represented.last + end + + def name + represented.first + end + end + end + end +end diff --git a/lib/api/v3/queries/schemas/backlogs_type_dependency_representer.rb b/lib/api/v3/queries/schemas/backlogs_type_dependency_representer.rb index b9fba43fac..2e21973abe 100644 --- a/lib/api/v3/queries/schemas/backlogs_type_dependency_representer.rb +++ b/lib/api/v3/queries/schemas/backlogs_type_dependency_representer.rb @@ -1,14 +1,20 @@ -#-- encoding: UTF-8 #-- copyright -# OpenProject is a project management system. -# Copyright (C) 2012-2017 the OpenProject Foundation (OPF) +# OpenProject Backlogs Plugin # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License version 3. +# Copyright (C)2013-2014 the OpenProject Foundation (OPF) +# Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda +# Copyright (C)2010-2011 friflaj +# Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns +# Copyright (C)2009-2010 Mark Maglana +# Copyright (C)2009 Joe Heck, Nate Lowrie +# +# 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-2017 Jean-Philippe Lang -# Copyright (C) 2010-2013 the ChiliProject Team +# OpenProject Backlogs is a derivative work based on ChiliProject Backlogs. +# The copyright follows: +# Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj +# Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,12 +40,32 @@ module API class BacklogsTypeDependencyRepresenter < FilterDependencyRepresenter + schema_with_allowed_collection :values, + type: ->(*) { type }, + writable: true, + has_default: false, + required: true, + visibility: false, + values_callback: ->(*) { + represented.allowed_values + }, + value_representer: BacklogsTypes::BacklogsTypeRepresenter, + link_factory: ->(value) { + { + href: api_v3_paths.backlogs_type(value.last), + title: value.first + } + }, + show_if: ->(*) { + value_required? + } + def href_callback; end private def type - '[]String' + '[1]BacklogsType' end end end diff --git a/lib/open_project/backlogs/engine.rb b/lib/open_project/backlogs/engine.rb index 0074317ac0..00f37e989a 100644 --- a/lib/open_project/backlogs/engine.rb +++ b/lib/open_project/backlogs/engine.rb @@ -243,12 +243,13 @@ module OpenProject::Backlogs end end - initializer 'backlogs.register_hooks' do - require 'open_project/backlogs/hooks' + add_api_path :backlogs_type do |id| + # There is no api endpoint for this url + "#{root}/backlogs_types/#{id}" end - initializer 'backlogs.register_query_filter' do - Queries::Register.filter Query, OpenProject::Backlogs::WorkPackageFilter + initializer 'backlogs.register_hooks' do + require 'open_project/backlogs/hooks' end config.to_prepare do @@ -267,6 +268,8 @@ module OpenProject::Backlogs end ::Type.add_default_mapping(:estimates_and_time, :story_points, :remaining_time) + + Queries::Register.filter Query, OpenProject::Backlogs::WorkPackageFilter end end end diff --git a/lib/open_project/backlogs/work_package_filter.rb b/lib/open_project/backlogs/work_package_filter.rb index 150d00323a..056a540617 100644 --- a/lib/open_project/backlogs/work_package_filter.rb +++ b/lib/open_project/backlogs/work_package_filter.rb @@ -37,11 +37,7 @@ require 'story' require 'task' module OpenProject::Backlogs - class WorkPackageFilter < ::Queries::Filters::Base - - alias :project :context - alias :project= :context= - + class WorkPackageFilter < ::Queries::WorkPackages::Filter::WorkPackageFilter def allowed_values [[I18n.t(:story, scope: [:backlogs]), 'story'], [I18n.t(:task, scope: [:backlogs]), 'task'], @@ -58,41 +54,8 @@ module OpenProject::Backlogs :backlogs_work_package_type end - def self.sql_for_field(values, db_table, _db_field) - sql = [] - - selected_values = ['story', 'task'] if values.include?('any') - - story_types = Story.types.map(&:to_s).join(',') - all_types = (Story.types + [Task.type]).map(&:to_s).join(',') - - selected_values.each do |val| - case val - when 'story' - sql << "(#{db_table}.type_id IN (#{story_types}))" - when 'task' - sql << "(#{db_table}.type_id = #{Task.type} AND NOT #{db_table}.parent_id IS NULL)" - when 'impediment' - sql << "(#{db_table}.id IN ( - select from_id - FROM relations ir - JOIN work_packages blocked - ON - blocked.id = ir.to_id - AND blocked.type_id IN (#{all_types}) - WHERE ir.relation_type = 'blocks' - ) AND #{db_table}.parent_id IS NULL)" - end - - case operator - when '=' - sql = sql.join(' OR ') - when '!' - sql = 'NOT (' + sql.join(' OR ') + ')' - end - end - - sql + def where + sql_for_field(values) end def order @@ -111,6 +74,16 @@ module OpenProject::Backlogs '::API::V3::Queries::Schemas::BacklogsTypeDependencyRepresenter' end + def ar_object_filter? + true + end + + def value_objects + allowed_values + .select { |av| values.include?(av.last) } + .map { |value| BacklogsType.new(*value) } + end + private def backlogs_configured? @@ -120,5 +93,71 @@ module OpenProject::Backlogs def backlogs_enabled? project.nil? || project.module_enabled?(:backlogs) end + + def sql_for_field(values) + selected_values = if values.include?('any') + ['story', 'task'] + else + values + end + + sql_parts = selected_values.map do |val| + case val + when 'story' + sql_for_story + when 'task' + sql_for_task + when 'impediment' + sql_for_impediment + end + end + + case operator + when '=' + sql_parts.join(' OR ') + when '!' + 'NOT (' + sql_parts.join(' OR ') + ')' + end + end + + def db_table + WorkPackage.table_name + end + + def sql_for_story + story_types = Story.types.map(&:to_s).join(',') + + "(#{db_table}.type_id IN (#{story_types}))" + end + + def sql_for_task + "(#{db_table}.type_id = #{Task.type} AND NOT #{db_table}.parent_id IS NULL)" + end + + def sql_for_impediment + all_types = (Story.types + [Task.type]).map(&:to_s).join(',') + + "(#{db_table}.id IN ( + select from_id + FROM relations ir + JOIN work_packages blocked + ON + blocked.id = ir.to_id + AND blocked.type_id IN (#{all_types}) + WHERE ir.relation_type = 'blocks' + ) AND #{db_table}.parent_id IS NULL)" + end + end + + # Need to be conformant to the interface required + # by api/v3/queries/filters/query_filter_instance_representer.rb + class BacklogsType + attr_accessor :id, + :name + + def initialize(name, id) + self.id = id + self.name = name + end end end diff --git a/spec/features/work_packages/filter_spec.rb b/spec/features/work_packages/filter_spec.rb new file mode 100644 index 0000000000..94cc9af90f --- /dev/null +++ b/spec/features/work_packages/filter_spec.rb @@ -0,0 +1,116 @@ +#-- copyright +# OpenProject Backlogs Plugin +# +# Copyright (C)2013-2014 the OpenProject Foundation (OPF) +# Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda +# Copyright (C)2010-2011 friflaj +# Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns +# Copyright (C)2009-2010 Mark Maglana +# Copyright (C)2009 Joe Heck, Nate Lowrie +# +# 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 Backlogs is a derivative work based on ChiliProject Backlogs. +# The copyright follows: +# Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj +# Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany +# +# 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 doc/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' + +describe 'Filter by backlog type', js: true do + let(:story_type) do + type = FactoryGirl.create(:type_feature) + project.types << type + + type + end + + let(:task_type) do + type = FactoryGirl.create(:type_task) + project.types << type + + type + end + + let(:user) { FactoryGirl.create :admin } + let(:project) { FactoryGirl.create :project } + + let(:wp_table) { ::Pages::WorkPackagesTable.new(project) } + let(:filters) { ::Components::WorkPackages::Filters.new } + + let(:member) do + FactoryGirl.create(:member, + user: user, + project: project, + roles: [FactoryGirl.create(:role)]) + end + + let(:work_package_with_story_type) do + FactoryGirl.create(:work_package, + type: story_type, + project: project) + end + + let(:work_package_with_task_type) do + FactoryGirl.create(:work_package, + type: task_type, + project: project) + end + + before do + login_as(user) + work_package_with_task_type + work_package_with_story_type + + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return('story_types' => [story_type.id.to_s], + 'task_type' => task_type.id.to_s) + + wp_table.visit! + end + + it 'allows filtering, saving and retaining the filter' do + filters.open + + filters.add_filter_by('Backlog type', 'is', 'Story', 'backlogsWorkPackageType') + + expect(wp_table).to have_work_packages_listed [work_package_with_story_type] + expect(wp_table).not_to have_work_packages_listed [work_package_with_task_type] + + wp_table.save_as('Some query name') + + filters.remove_filter 'backlogsWorkPackageType' + + expect(wp_table).to have_work_packages_listed [work_package_with_story_type, work_package_with_task_type] + + last_query = Query.last + + wp_table.visit_query(last_query) + + expect(wp_table).to have_work_packages_listed [work_package_with_story_type] + expect(wp_table).not_to have_work_packages_listed [work_package_with_task_type] + + filters.open + + filters.expect_filter_by('Backlog type', 'is', 'Story', 'backlogsWorkPackageType') + end +end