implement filter on grids

pull/6834/head
Jens Ulferts 6 years ago
parent 5bba60202e
commit e0bf7894b3
No known key found for this signature in database
GPG Key ID: 3CAA4B1182CF5308
  1. 3
      app/contracts/grids/base_contract.rb
  2. 2
      app/models/grid.rb
  3. 4
      app/models/my_page_grid.rb
  4. 35
      app/models/queries/grids.rb
  5. 37
      app/models/queries/grids/filters/grid_filter.rb
  6. 56
      app/models/queries/grids/filters/page_filter.rb
  7. 39
      app/models/queries/grids/grid_query.rb
  8. 1
      app/services/grids/set_attributes_service.rb
  9. 3
      db/migrate/20181118193730_create_grid.rb
  10. 39
      lib/api/v3/grids/grid_collection_representer.rb
  11. 8
      lib/api/v3/grids/grid_representer.rb
  12. 70
      lib/api/v3/grids/grids_api.rb
  13. 6
      spec/models/grids/shared_model.rb
  14. 70
      spec/models/queries/grids/filters/page_filter_spec.rb
  15. 73
      spec/models/queries/grids/grid_query_spec.rb
  16. 93
      spec/requests/api/v3/grids/grids_resource_spec.rb

@ -34,7 +34,8 @@ module Grids
class BaseContract < ::ModelContract
attribute :row_count
attribute :column_count
attribute :page
# TODO: check how this can be restricted to only MyPage
attribute :user
attribute :widgets
def self.model

@ -29,5 +29,5 @@
#++
class Grid < ActiveRecord::Base
has_many :widgets, class_name: GridWidget
has_many :widgets, class_name: 'GridWidget'
end

@ -31,9 +31,9 @@
class MyPageGrid < Grid
belongs_to :user
def self.new_default
def self.new_default(user)
new(
page: OpenProject::StaticRouting::StaticRouter.new.url_helpers.my_page_path,
user: user,
row_count: 4,
column_count: 5,
widgets: [

@ -0,0 +1,35 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
module Queries::Grids
query = Queries::Grids::GridQuery
Queries::Register.filter query, Queries::Grids::Filters::PageFilter
end

@ -0,0 +1,37 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
class Queries::Grids::Filters::GridFilter < Queries::Filters::Base
self.model = Grid
def human_name
Grid.human_attribute_name(name)
end
end

@ -0,0 +1,56 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
class Queries::Grids::Filters::PageFilter < Queries::Grids::Filters::GridFilter
def allowed_values
# TODO: generalize
[OpenProject::StaticRouting::StaticUrlHelpers.new.my_page_path]
end
def type
:string
end
def self.key
:page
end
def where
# TODO: generalize
actual_values = [MyPageGrid.name]
operator_strategy.sql_for_field(actual_values,
self.class.model.table_name,
'type')
end
def available_operators
[::Queries::Operators::Equals]
end
end

@ -0,0 +1,39 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
class Queries::Grids::GridQuery < Queries::BaseQuery
def self.model
Grid
end
def default_scope
# TODO: introduce visible scope
#Grid.visible(User.current)
Grid.where(user_id: User.current.id)
end
end

@ -58,6 +58,7 @@ class Grids::SetAttributesService
end
def set_attributes(attributes)
grid.attributes = attributes
#if attributes.key?(:attachment_ids)
# work_package.attachments_replacements = Attachment.where(id: attributes[:attachment_ids])
#end

@ -10,14 +10,11 @@ class CreateGrid < ActiveRecord::Migration[5.1]
create_table :grids do |t|
t.integer :row_count, null: false
t.integer :column_count, null: false
t.string :page, null: false
t.string :type
t.references :user
t.timestamps
t.index :page
end
end

@ -0,0 +1,39 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
module API
module V3
module Grids
class GridCollectionRepresenter < ::API::Decorators::OffsetPaginatedCollection
element_decorator ::API::V3::Grids::GridRepresenter
end
end
end
end

@ -34,8 +34,14 @@ module API
resource_link :page,
getter: ->(*) {
path = if represented.is_a?(::MyPageGrid)
my_page_path
else
raise "undefined error"
end
{
href: represented.page,
href: path,
type: 'text/html'
}
},

@ -32,46 +32,23 @@ module API
class GridsAPI < ::API::OpenProjectAPI
resources :grids do
helpers do
def bogus_grid
OpenStruct.new(
row_count: 4,
column_count: 5,
widgets: [
OpenStruct.new(
identifier: 'work_packages_assigned',
start_row: 4,
end_row: 5,
start_column: 1,
end_column: 2
),
OpenStruct.new(
identifier: 'work_packages_created',
start_row: 1,
end_row: 2,
start_column: 1,
end_column: 2
),
OpenStruct.new(
identifier: 'work_packages_watched',
start_row: 2,
end_row: 4,
start_column: 4,
end_column: 5
),
OpenStruct.new(
identifier: 'work_packages_calendar',
start_row: 1,
end_row: 2,
start_column: 4,
end_column: 6
)
]
)
end
include API::Utilities::ParamsHelper
end
get do
query = ParamsToQueryService
.new(Grid, current_user)
.call(params)
if query.valid?
GridCollectionRepresenter.new(query.results,
api_v3_paths.time_entries,
page: to_i_or_nil(params[:offset]),
per_page: resolve_page_size(params[:pageSize]),
current_user: current_user)
else
raise ::API::Errors::InvalidQuery.new(query.errors.full_messages)
end
end
post do
@ -81,10 +58,15 @@ module API
.call(request_body)
.result
grid = OpenStruct.new(bogus_grid.to_h.merge(params))
# TODO: determine grid class based on the page parameter
call = ::Grids::SetAttributesService
.new(user: current_user,
grid: MyPageGrid.new_default(current_user),
contract_class: ::Grids::CreateContract)
.call(params)
status 201
GridRepresenter.create(grid,
GridRepresenter.create(call.result,
current_user: current_user,
embed_links: true)
end
@ -94,7 +76,8 @@ module API
route_param :id do
get do
# TODO: replace mock with actual fetching
GridRepresenter.new(bogus_grid, current_user: current_user)
GridRepresenter.new(MyPageGrid.new_default(current_user),
current_user: current_user)
end
patch do
@ -104,9 +87,14 @@ module API
.call(request_body)
.result
grid = OpenStruct.new(bogus_grid.to_h.merge(params))
# TODO: determine grid class based on the page parameter
call = ::Grids::SetAttributesService
.new(user: current_user,
grid: MyPageGrid.new_default(current_user),
contract_class: ::Grids::UpdateContract)
.call(params)
GridRepresenter.create(grid,
GridRepresenter.create(call.result,
current_user: current_user,
embed_links: true)
end

@ -40,12 +40,6 @@ shared_examples_for 'grid attributes' do
.to eql 5
end
it '#page' do
instance.page = 'some_page/at/a/url'
expect(instance.page)
.to eql 'some_page/at/a/url'
end
it '#widgets' do
widgets = [
GridWidget.new(start_row: 2),

@ -0,0 +1,70 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Queries::Grids::Filters::PageFilter, type: :model do
include_context 'filter tests'
let(:values) { ['/my/page'] }
let(:user) { FactoryBot.build_stubbed(:user) }
let(:model) { Grid }
before do
login_as(user)
end
it_behaves_like 'basic query filter' do
let(:class_key) { :page }
let(:type) { :string }
let(:model) { Grid.where(user_id: user.id) }
let(:values) { ['/my/page'] }
describe '#allowed_values' do
it 'is /my/page' do
expect(instance.allowed_values)
.to match_array values
end
end
end
describe '#scope' do
context 'for "="' do
let(:operator) { '=' }
context 'for /my/page do' do
it 'is the same as handwriting the query' do
expected = model.where("grids.type IN ('MyPageGrid')")
expect(instance.scope.to_sql).to eql expected.to_sql
end
end
end
end
end

@ -0,0 +1,73 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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
#
# 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 docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Queries::Grids::GridQuery, type: :model do
let(:user) { FactoryBot.build_stubbed(:user) }
let(:base_scope) { Grid.where(user_id: user.id) }
let(:instance) { described_class.new }
before do
login_as(user)
end
context 'without a filter' do
describe '#results' do
it 'is the same as getting all the grids visible to the user' do
expect(instance.results.to_sql).to eql base_scope.to_sql
end
end
end
context 'with a page filter' do
before do
instance.where('page', '=', ['/my/page'])
end
describe '#results' do
it 'is the same as handwriting the query' do
expected = base_scope
.where(['grids.type IN (?)', ['MyPageGrid']])
expect(instance.results.to_sql).to eql expected.to_sql
end
end
describe '#valid?' do
it 'is true' do
expect(instance).to be_valid
end
it 'is invalid if the filter is invalid' do
instance.where('page', '!', ['/some/other/page'])
expect(instance).to be_invalid
end
end
end
end

@ -41,10 +41,99 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
login_as(current_user)
end
let(:path) { api_v3_paths.grid(42) }
subject(:response) { last_response }
describe '#get INDEX' do
let(:path) { api_v3_paths.grids }
shared_let(:my_page_grid) do
MyPageGrid.new_default(current_user).save
end
shared_let(:other_user) do
FactoryBot.create(:user)
end
shared_let(:other_my_page_grid) do
MyPageGrid.new_default(other_user).save
end
let(:stored_grids) do
my_page_grid
other_my_page_grid
end
before do
stored_grids
get path
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends a collection of grids but only those visible to the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
context 'with a filter on the page attribute' do
shared_let(:other_grid) do
grid = Grid.new(row_count: 20,
column_count: 20)
grid.save
Grid.where(id: grid.id).update_all(user_id: current_user.id)
grid
end
let(:stored_grids) do
my_page_grid
other_my_page_grid
other_grid
end
let(:path) do
filter = [{ 'page' =>
{
'operator' => '=',
'values' => [my_page_path]
} }]
"#{api_v3_paths.grids}?#{{ filters: filter.to_json }.to_query}"
end
it 'responds with 200 OK' do
expect(subject.status).to eq(200)
end
it 'sends only the my page of the current user' do
expect(subject.body)
.to be_json_eql('Collection'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_embedded/elements/0/_type')
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('total')
end
end
end
describe '#get' do
let(:path) { api_v3_paths.grid(42) }
before do
get path
end
@ -67,6 +156,8 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
end
describe '#patch' do
let(:path) { api_v3_paths.grid(42) }
let(:params) do
{
"rowCount": 10,

Loading…
Cancel
Save