Merge pull request #10368 from opf/feature/include-subprojects-attribute

[41135] Add new query attribute include_subprojects
pull/10426/head
Oliver Günther 3 years ago committed by GitHub
commit 241a7ea981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/contracts/queries/base_contract.rb
  2. 27
      app/models/queries/work_packages/filter/project_filter.rb
  3. 6
      app/models/query.rb
  4. 3
      app/seeders/demo_data/query_builder.rb
  5. 1
      app/seeders/demo_data/work_package_board_seeder.rb
  6. 2
      app/services/api/parse_resource_params_service.rb
  7. 42
      app/services/api/parser_struct.rb
  8. 2
      app/services/base_services/set_attributes.rb
  9. 30
      app/services/queries/create_service.rb
  10. 35
      app/services/queries/set_attributes_service.rb
  11. 14
      db/migrate/20220323083000_add_include_subprojects_to_query.rb
  12. 6
      lib/api/v3/queries/queries_api.rb
  13. 11
      lib/api/v3/queries/query_helper.rb
  14. 8
      lib/api/v3/queries/query_representer.rb
  15. 8
      modules/calendar/spec/features/calendar_project_include_spec.rb
  16. 8
      modules/team_planner/spec/features/team_planner_project_include_spec.rb
  17. 62
      spec/contracts/queries/create_contract_spec.rb
  18. 52
      spec/contracts/queries/shared_contract_examples.rb
  19. 32
      spec/contracts/queries/update_contract_spec.rb
  20. 1
      spec/factories/query_factory.rb
  21. 14
      spec/features/work_packages/table/work_packages_table_project_include_spec.rb
  22. 6
      spec/features/work_packages/timeline/timeline_labels_spec.rb
  23. 15
      spec/lib/api/v3/queries/query_representer_parsing_spec.rb
  24. 26
      spec/models/queries/work_packages/filter/project_filter_spec.rb
  25. 150
      spec/models/query/results_project_filter_integration_spec.rb
  26. 106
      spec/models/query/results_subproject_filter_integration_spec.rb
  27. 22
      spec/models/query_spec.rb
  28. 2
      spec/models/work_package/exporter/csv_integration_spec.rb
  29. 8
      spec/services/principals/replace_references_service_call_integration_spec.rb
  30. 15
      spec/services/queries/create_service_spec.rb
  31. 2
      spec/support/components/work_packages/table_configuration_modal.rb
  32. 9
      spec_legacy/fixtures/queries.yml

@ -43,6 +43,7 @@ module Queries
attribute :highlighted_attributes
attribute :show_hierarchies
attribute :display_representation
attribute :include_subprojects
attribute :column_names # => columns
attribute :filters

@ -56,16 +56,33 @@ class Queries::WorkPackages::Filter::ProjectFilter < Queries::WorkPackages::Filt
end
def value_objects
available_projects = visible_projects.index_by(&:id)
visible_projects.where(id: values.map(&:to_i))
end
values
.map { |project_id| available_projects[project_id.to_i] }
.compact
def where
operator_strategy.sql_for_field(projects_and_descendants, self.class.model.table_name, :project_id)
end
private
def visible_projects
@visible_projects ||= Project.visible.active
Project.visible.active
end
##
# Depending on whether subprojects are included in the query,
# expand selected projects with its descendants
def projects_and_descendants
value_objects
.inject(Set.new) { |project_set, project| project_set + expand_subprojects(project) }
.map(&:id)
end
def expand_subprojects(selected_project)
if context.include_subprojects?
[selected_project].concat(selected_project.descendants.visible)
else
[selected_project]
end
end
end

@ -45,6 +45,9 @@ class Query < ApplicationRecord
presence: true,
length: { maximum: 255 }
validates :include_subprojects,
inclusion: [true, false]
validate :validate_work_package_filters
validate :validate_columns
validate :validate_sort_criteria
@ -62,6 +65,7 @@ class Query < ApplicationRecord
query.add_default_filter
query.set_default_sort
query.show_hierarchies = true
query.include_subprojects = Setting.display_subprojects_work_packages?
end
end
@ -368,7 +372,7 @@ class Query < ApplicationRecord
subproject_filter = Queries::WorkPackages::Filter::SubprojectFilter.create!
subproject_filter.context = self
subproject_filter.operator = if Setting.display_subprojects_work_packages?
subproject_filter.operator = if include_subprojects?
'*'
else
'!*'

@ -52,7 +52,8 @@ module DemoData
public: config.fetch(:public, true),
starred: config.fetch(:starred, false),
show_hierarchies: config.fetch(:hierarchy, false),
timeline_visible: config.fetch(:timeline, false)
timeline_visible: config.fetch(:timeline, false),
include_subprojects: true
}
end

@ -160,6 +160,7 @@ module DemoData
Query.new(project: project, user: admin).tap do |query|
# Make it public so that new members can see it too
query.public = true
query.include_subprojects = true
query.name = list[:name]

@ -76,7 +76,7 @@ module API
end
def struct
Hashie::Mash.new
ParserStruct.new
end
def deep_to_h(value)

@ -0,0 +1,42 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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 API
class ParserStruct < ::Hashie::Mash
##
# TODO: Hashie::Mash extends from Hash and
# does not allow overriding any enumerable methods.
#
# This clashed with moving the queries services to BaseContracted,
# as we now use a +group_by+ attribute clashing with +Enumerable#group_by#.
# This redefines the method to ensure it works with queries, but does not solve the underlying issue.
def group_by
self[:group_by]
end
end
end

@ -31,6 +31,8 @@ module BaseServices
include Contracted
def initialize(user:, model:, contract_class:, contract_options: {})
super()
self.user = user
self.model = prepare_model(model)

@ -26,32 +26,4 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Queries::CreateService < Queries::BaseService
def initialize(**args)
super(**args)
self.contract_class = Queries::CreateContract
end
def call(query)
remove_invalid_order(query)
super
end
private
def remove_invalid_order(query)
# Check which of the work package IDs exist
ids = query.ordered_work_packages.map(&:work_package_id)
existent_wps = WorkPackage.where(id: ids).pluck(:id).to_set
query.ordered_work_packages = query.ordered_work_packages.select do |order_item|
existent_wps.include?(order_item.work_package_id)
end
end
def service_result(result, errors, query)
query.update user: user
super
end
end
class Queries::CreateService < ::BaseServices::Create; end

@ -26,4 +26,37 @@
# See COPYRIGHT and LICENSE files for more details.
#++
class Queries::SetAttributesService < ::BaseServices::SetAttributes; end
class Queries::SetAttributesService < ::BaseServices::SetAttributes
def set_attributes(params)
set_ordered_work_packages params.delete(:ordered_work_packages)
super
end
def set_default_attributes(_params)
if model.include_subprojects.nil?
model.include_subprojects = Setting.display_subprojects_work_packages?
end
set_default_user
end
def set_default_user
model.change_by_system do
model.user = user
end
end
def set_ordered_work_packages(ordered_hash)
return if ordered_hash.nil?
available = WorkPackage.where(id: ordered_hash.keys.map(&:to_s)).pluck(:id).to_set
ordered_hash.each do |key, position|
# input keys are symbols due to hashie::mash, and AR doesn't like that
wp_id = key.to_s.to_i
next unless available.include?(wp_id.to_s.to_i)
model.ordered_work_packages.build(work_package_id: wp_id, position: position)
end
end
end

@ -0,0 +1,14 @@
class AddIncludeSubprojectsToQuery < ActiveRecord::Migration[6.1]
def change
add_column :queries,
:include_subprojects,
:boolean,
null: false,
default: Setting.display_subprojects_work_packages?
# Remove the default now
reversible do |dir|
dir.up { change_column_default :queries, :include_subprojects, nil }
end
end
end

@ -96,9 +96,9 @@ module API
end
end
post do
create_query request_body, current_user
end
post &::API::V3::Utilities::Endpoints::Create
.new(model: Query)
.mount
route_param :id, type: Integer, desc: 'Query ID' do
after_validation do

@ -63,17 +63,6 @@ module API
end
end
def create_query(request_body, current_user)
rep = representer.new Query.new, current_user: current_user
query = rep.from_hash request_body
call = ::Queries::CreateService.new(user: current_user).call query
if call.success?
representer.new call.result, current_user: current_user, embed_links: true
else
fail ::API::Errors::ErrorBase.create_and_merge_errors(call.errors)
end
end
def update_query(query, request_body, current_user)
rep = representer.new query, current_user: current_user
query = rep.from_hash request_body

@ -259,11 +259,9 @@ module API
exec_context: :decorator,
getter: nil,
setter: ->(fragment:, **) {
next unless represented.new_record?
next if represented.persisted?
Hash(fragment).each do |wp_id, position|
represented.ordered_work_packages.build(work_package_id: wp_id, position: position)
end
represented.ordered_work_packages = fragment
}
property :starred,
@ -288,6 +286,8 @@ module API
property :filters,
exec_context: :decorator
property :include_subprojects
property :display_sums, as: :sums
property :public

@ -50,8 +50,8 @@ describe 'Calendar project include', type: :feature, js: true do
dropdown.expect_closed
work_package_view.expect_event task
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug, present: true
work_package_view.expect_event other_task
work_package_view.expect_event other_other_task, present: false
@ -60,7 +60,7 @@ describe 'Calendar project include', type: :feature, js: true do
dropdown.click_button 'Apply'
dropdown.expect_count 2
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug
dropdown.toggle!
@ -74,7 +74,7 @@ describe 'Calendar project include', type: :feature, js: true do
page.refresh
work_package_view.expect_event task
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug
work_package_view.expect_event other_task
work_package_view.expect_event other_other_task

@ -76,8 +76,8 @@ describe 'Team planner project include', type: :feature, js: true do
work_package_view.within_lane(user) do
work_package_view.expect_event task
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug, present: true
end
work_package_view.within_lane(other_user) do
@ -92,7 +92,7 @@ describe 'Team planner project include', type: :feature, js: true do
work_package_view.within_lane(user) do
work_package_view.expect_event task
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug
end
@ -115,7 +115,7 @@ describe 'Team planner project include', type: :feature, js: true do
work_package_view.within_lane(user) do
work_package_view.expect_event task
work_package_view.expect_event sub_bug, present: false
work_package_view.expect_event sub_bug, present: true
work_package_view.expect_event sub_sub_bug
end
end

@ -0,0 +1,62 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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_relative 'shared_contract_examples'
describe Queries::CreateContract do
include_context 'ModelContract shared context'
include_context 'with queries contract'
describe 'include subprojects' do
let(:query) do
Query.new name: 'foo',
include_subprojects: include_subprojects,
project: project
end
context 'when true' do
let(:include_subprojects) { true }
it_behaves_like 'contract is valid'
end
context 'when falsea' do
let(:include_subprojects) { false }
it_behaves_like 'contract is valid'
end
context 'when nil' do
let(:include_subprojects) { nil }
it_behaves_like 'contract is invalid', include_subprojects: %i[inclusion]
end
end
end

@ -0,0 +1,52 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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'
shared_context 'with queries contract' do
let(:project) { build_stubbed :project }
let(:query) do
build_stubbed(:query, project: project, public: public, user: user)
end
let(:current_user) do
build_stubbed(:user) do |user|
allow(user)
.to receive(:allowed_to?) do |permission, permission_project|
permissions.include?(permission) && project == permission_project
end
end
end
let(:contract) { described_class.new(query, current_user) }
before do
# Assume project is always visible
allow(contract).to receive(:project_visible?).and_return true
end
end

@ -28,29 +28,11 @@
require 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
require_relative 'shared_contract_examples'
describe Queries::UpdateContract do
include_context 'ModelContract shared context'
let(:project) { build_stubbed :project }
let(:query) do
build_stubbed(:query, project: project, public: public, user: user)
end
let(:current_user) do
build_stubbed(:user) do |user|
allow(user)
.to receive(:allowed_to?) do |permission, permission_project|
permissions.include?(permission) && project == permission_project
end
end
end
let(:contract) { described_class.new(query, current_user) }
before do
# Assume project is always visible
allow(contract).to receive(:project_visible?).and_return true
end
include_context 'with queries contract'
describe 'private query' do
let(:public) { false }
@ -58,13 +40,13 @@ describe Queries::UpdateContract do
context 'when user is author' do
let(:user) { current_user }
context 'user has no permission to save' do
context 'when user has no permission to save' do
let(:permissions) { %i(edit_work_packages) }
it_behaves_like 'contract user is unauthorized'
end
context 'user has permission to save' do
context 'when user has permission to save' do
let(:permissions) { %i(save_queries) }
it_behaves_like 'contract is valid'
@ -83,19 +65,19 @@ describe Queries::UpdateContract do
let(:public) { true }
let(:user) { nil }
context 'user has no permission to save' do
context 'when user has no permission to save' do
let(:permissions) { %i(invalid_permission) }
it_behaves_like 'contract user is unauthorized'
end
context 'user has no permission to manage public' do
context 'when user has no permission to manage public' do
let(:permissions) { %i(manage_public_queries) }
it_behaves_like 'contract is valid'
end
context 'user has permission to save only own' do
context 'when user has permission to save only own' do
let(:permissions) { %i(save_queries) }
it_behaves_like 'contract user is unauthorized'

@ -30,6 +30,7 @@ FactoryBot.define do
factory :query do
project
user factory: :user
include_subprojects { Setting.display_subprojects_work_packages? }
sequence(:name) { |n| "Query #{n}" }
factory :public_query do

@ -45,29 +45,27 @@ describe 'Calendar project include', type: :feature, js: true do
dropdown.click_button 'Apply'
dropdown.expect_closed
work_package_view.expect_work_package_listed(task, other_task)
work_package_view.ensure_work_package_not_listed!(sub_bug, sub_sub_bug, other_other_task)
work_package_view.expect_work_package_listed(task, other_task, sub_bug, sub_sub_bug)
work_package_view.ensure_work_package_not_listed!(other_other_task)
dropdown.toggle!
dropdown.toggle_checkbox(sub_sub_project.id)
dropdown.click_button 'Apply'
dropdown.expect_count 2
work_package_view.expect_work_package_listed(task, other_task, sub_sub_bug)
work_package_view.ensure_work_package_not_listed!(sub_bug, other_other_task)
work_package_view.expect_work_package_listed(task, other_task, sub_sub_bug, sub_bug)
work_package_view.ensure_work_package_not_listed!(other_other_task)
dropdown.toggle!
dropdown.toggle_checkbox(other_project.id)
dropdown.click_button 'Apply'
dropdown.expect_count 3
work_package_view.expect_work_package_listed(task, other_task, sub_sub_bug, other_other_task)
work_package_view.ensure_work_package_not_listed!(sub_bug)
work_package_view.expect_work_package_listed(task, other_task, sub_sub_bug, sub_bug, other_other_task)
page.refresh
work_package_view.expect_work_package_listed(task, other_task, sub_sub_bug, other_other_task)
work_package_view.ensure_work_package_not_listed!(sub_bug)
work_package_view.expect_work_package_listed(task, other_task, sub_bug, sub_sub_bug, other_other_task)
end
end
end

@ -135,9 +135,9 @@ RSpec.feature 'Work package timeline labels',
# Check the query
query = Query.last
expect(query.timeline_labels).to eq 'left' => 'assignee',
'right' => 'type',
'farRight' => 'status'
expect(query.timeline_labels).to eq left: 'assignee',
right: 'type',
farRight: 'status'
# Revisit page
wp_timeline.visit_query query

@ -31,7 +31,7 @@ require 'spec_helper'
describe ::API::V3::Queries::QueryRepresenter, 'parsing' do
include ::API::V3::Utilities::PathHelper
let(:query) { build_stubbed(:query, project: project) }
let(:query) { ::API::ParserStruct.new }
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user) }
let(:representer) do
@ -82,10 +82,7 @@ describe ::API::V3::Queries::QueryRepresenter, 'parsing' do
end
it 'unsets group_by' do
expect(query).to be_grouped
expect(query.group_by).to eq('project')
expect(subject).not_to be_grouped
end
end
@ -115,20 +112,20 @@ describe ::API::V3::Queries::QueryRepresenter, 'parsing' do
end
before do
allow(query).to receive(:new_record?).and_return(new_record)
allow(query).to receive(:persisted?).and_return(persisted)
end
context 'if query is new' do
let(:new_record) { true }
let(:persisted) { nil }
it 'sets ordered_work_packages' do
order = subject.ordered_work_packages.map { |el| [el.work_package_id, el.position] }
expect(order).to match_array [[50, 0], [38, 1234], [102, 81234123]]
order = subject.ordered_work_packages
expect(order).to eq({ '50' => 0, '38' => 1234, '102' => 81234123 })
end
end
context 'if query is not new' do
let(:new_record) { false }
let(:persisted) { true }
it 'sets ordered_work_packages' do
allow(query)

@ -48,6 +48,12 @@ describe Queries::WorkPackages::Filter::ProjectFilter, type: :model do
allow(visible_projects)
.to receive(:exists?)
.and_return(visible_projects.any?)
allow(visible_projects)
.to receive(:where) do |args|
ids = args[:id]
visible_projects.select { |p| ids.include?(p.id) }
end
end
describe '#available?' do
@ -106,13 +112,29 @@ describe Queries::WorkPackages::Filter::ProjectFilter, type: :model do
end
describe '#value_objects' do
let(:selected) { visible_projects.first }
let(:visible_descendants) { [] }
let(:descendants) { double('Project', visible: visible_descendants) } # rubocop:disable RSpec/VerifiedDoubles
before do
instance.values = [visible_projects.first.id.to_s]
allow(selected).to receive(:descendants).and_return(descendants)
instance.values = [selected.id.to_s]
end
it 'returns an array of projects' do
expect(instance.value_objects)
.to match_array([visible_projects.first])
.to match_array([selected])
end
context 'with a visible child' do
let(:child) { build_stubbed(:project, parent: selected, id: 2134) }
let(:visible_descendants) { [child] }
it 'still only returns the parent object' do
expect(instance.value_objects)
.to match_array([selected])
end
end
end
end

@ -0,0 +1,150 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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'
describe ::Query::Results, 'Project filter integration', type: :model, with_mail: false do
let(:query) do
build(:query,
user: user,
project: parent_project).tap do |q|
q.filters.clear
end
end
let(:query_results) do
described_class.new query
end
shared_let(:parent_project) { create :project }
shared_let(:child_project) { create :project, parent: parent_project }
shared_let(:second_parent_project) { create :project }
shared_let(:second_child_project) { create :project, parent: second_parent_project }
shared_let(:user) do
create(:user,
firstname: 'user',
lastname: '1',
member_in_projects: [parent_project, child_project, second_parent_project, second_child_project],
member_with_permissions: [:view_work_packages])
end
shared_let(:parent_wp) { create :work_package, project: parent_project }
shared_let(:child_wp) { create :work_package, project: child_project }
shared_let(:second_parent_wp) { create :work_package, project: second_parent_project }
shared_let(:second_child_wp) { create :work_package, project: second_child_project }
before do
login_as user
end
describe 'both parent projects selected' do
before do
query.add_filter 'project_id', '=', [parent_project.id, second_parent_project.id]
end
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp, second_parent_wp, second_child_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, second_parent_wp]
end
end
context 'when subprojects explicitly disabled' do
before do
query.include_subprojects = false
end
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, second_parent_wp]
end
end
end
describe 'one parent projects selected' do
before do
query.add_filter 'project_id', '=', [second_parent_project.id]
end
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [second_parent_wp, second_child_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [second_parent_wp]
end
end
context 'when subprojects explicitly disabled' do
before do
query.include_subprojects = false
end
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [second_parent_wp]
end
end
end
describe 'one parent and one other child selected' do
before do
query.add_filter 'project_id', '=', [child_project.id, second_parent_project.id]
end
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp, second_parent_wp, second_child_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp, second_parent_wp]
end
end
context 'when subprojects explicitly disabled' do
before do
query.include_subprojects = false
end
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp, second_parent_wp]
end
end
end
end

@ -58,34 +58,112 @@ describe ::Query::Results, 'Subproject filter integration', type: :model, with_m
login_as user
end
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
describe 'new default query' do
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp]
end
context 'when subproject filter added manually' do
before do
query.add_filter('subproject_id', '=', [child_project.id])
end
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
end
context 'when only subproject filter added manually' do
before do
query.add_filter('only_subproject_id', '=', [child_project.id])
end
it 'shows only the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp]
end
end
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp]
describe 'query with overriden include_subprojects = true' do
before do
query.include_subprojects = true
end
context 'when subproject filter added manually' do
before do
query.add_filter('subproject_id', '=', [child_project.id])
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
context 'when subproject filter added manually' do
before do
query.add_filter('subproject_id', '=', [child_project.id])
end
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
end
context 'when only subproject filter added manually' do
before do
query.add_filter('only_subproject_id', '=', [child_project.id])
end
it 'shows only the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp]
end
end
end
end
describe 'query with overriden include_subprojects = false' do
before do
query.include_subprojects = false
end
context 'when only subproject filter added manually' do
before do
query.add_filter('only_subproject_id', '=', [child_project.id])
context 'when subprojects included', with_settings: { display_subprojects_work_packages: true } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp]
end
end
context 'when subprojects not included', with_settings: { display_subprojects_work_packages: false } do
it 'does not show the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp]
end
context 'when subproject filter added manually' do
before do
query.add_filter('subproject_id', '=', [child_project.id])
end
it 'shows the sub work packages' do
expect(query_results.work_packages).to match_array [parent_wp, child_wp]
end
end
context 'when only subproject filter added manually' do
before do
query.add_filter('only_subproject_id', '=', [child_project.id])
end
it 'shows only the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp]
it 'shows only the sub work packages' do
expect(query_results.work_packages).to match_array [child_wp]
end
end
end
end

@ -60,6 +60,28 @@ describe Query, type: :model do
expect(query.sort_criteria)
.to match_array([['id', 'asc']])
end
context 'with global subprojects include', with_settings: { display_subprojects_work_packages: true } do
it 'sets the include subprojects' do
expect(query.include_subprojects).to be true
end
end
context 'with global subprojects include', with_settings: { display_subprojects_work_packages: false } do
it 'sets the include subprojects' do
expect(query.include_subprojects).to be false
end
end
end
describe 'include_subprojects' do
let(:query) { described_class.new name: 'foo' }
it 'is required' do
expect(query).not_to be_valid
expect(query.errors[:include_subprojects]).to include 'is not set to one of the allowed values.'
end
end
describe 'hidden' do

@ -41,7 +41,7 @@ describe WorkPackage::Exports::CSV, 'integration', type: :model do
member_with_permissions: %i(view_work_packages))
end
let(:query) do
Query.new(name: '_').tap do |query|
Query.new_default(name: '_').tap do |query|
query.column_names = %i(subject assigned_to updated_at estimated_hours)
end
end

@ -380,7 +380,13 @@ describe Principals::ReplaceReferencesService, '#call', type: :model do
context 'with Query' do
it_behaves_like 'rewritten record',
:query,
:user_id
:user_id do
let(:attributes) do
{
include_subprojects: true
}
end
end
end
context 'with CostQuery' do

@ -30,17 +30,20 @@ require 'spec_helper'
describe Queries::CreateService do
let(:user) { build_stubbed(:admin) }
let(:query) { build(:query, user: user) }
let(:instance) { described_class.new(user: user) }
subject { instance.call(query).result }
subject { instance.call(params).result }
describe 'ordered work packages' do
let!(:work_package) { create :work_package }
before do
query.ordered_work_packages.build(work_package_id: work_package.id, position: 0)
query.ordered_work_packages.build(work_package_id: 99999, position: 1)
let(:params) do
{
name: 'My query',
ordered_work_packages: {
work_package.id => 0,
9999 => 1
}
}
end
it 'removes items for which work packages do not exist' do

@ -89,7 +89,7 @@ module Components
end
def expect_disabled_tab(name)
expect(page).to have_selector("#{selector} [data-qa-tab-disabled]", text: name.upcase)
expect(page).to have_selector("#{selector} [data-qa-tab-disabled]", text: name.upcase, wait: 10)
end
def selected_tab(name)

@ -31,6 +31,7 @@ queries_001:
id: 1
project_id: 1
public: true
include_subprojects: true
name: Multiple custom fields query
filters: |
---
@ -53,6 +54,7 @@ queries_002:
id: 2
project_id: 1
public: false
include_subprojects: true
name: Private query for cookbook
filters: |
---
@ -71,6 +73,7 @@ queries_003:
id: 3
project_id:
public: false
include_subprojects: true
name: Private query for all projects
filters: |
---
@ -85,6 +88,7 @@ queries_004:
id: 4
project_id:
public: true
include_subprojects: true
name: Public query for all projects
filters: |
---
@ -99,6 +103,7 @@ queries_005:
id: 5
project_id:
public: true
include_subprojects: true
name: Open issues by priority and type
filters: |
---
@ -119,6 +124,7 @@ queries_006:
id: 6
project_id:
public: true
include_subprojects: true
name: Open issues grouped by type
filters: |
---
@ -138,6 +144,7 @@ queries_007:
id: 7
project_id: 2
public: true
include_subprojects: true
name: Public query for project 2
filters: |
---
@ -152,6 +159,7 @@ queries_008:
id: 8
project_id: 2
public: false
include_subprojects: true
name: Private query for project 2
filters: |
---
@ -166,6 +174,7 @@ queries_009:
id: 9
project_id:
public: true
include_subprojects: true
name: Open issues grouped by list custom field
filters: |
---

Loading…
Cancel
Save