2024-01-23 16:48:37 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
|
|
# -- copyright
|
|
|
|
|
# OpenProject is an open source project management software.
|
2024-07-30 13:42:36 +02:00
|
|
|
# Copyright (C) the OpenProject GmbH
|
2024-01-23 16:48:37 +01:00
|
|
|
#
|
|
|
|
|
# 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 Queries::ParamsParser, type: :model do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
subject { described_class.parse(params.with_indifferent_access) }
|
|
|
|
|
|
|
|
|
|
describe ".parse" do
|
|
|
|
|
context "without any params" do
|
|
|
|
|
it "returns an empty array" do
|
|
|
|
|
expect(subject)
|
|
|
|
|
.to be_empty
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a single filter with a single value" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = t"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: ["t"] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a single filter with multiple values having single quotes" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = ['t', 'f']"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[t f] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a single filter with multiple values having double quotes" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = [\"t\", \"f\"]"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[t f] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-26 13:57:15 +01:00
|
|
|
context "with a single filter with a single value with , and &" do
|
2024-01-23 16:48:37 +01:00
|
|
|
let(:params) do
|
|
|
|
|
{
|
2024-01-26 13:57:15 +01:00
|
|
|
filters: "active = something, or another thing & something else"
|
2024-01-23 16:48:37 +01:00
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
2024-01-26 13:57:15 +01:00
|
|
|
# This returns invalid filters but they will then be marked as invalid
|
2024-01-23 16:48:37 +01:00
|
|
|
expect(subject[:filters])
|
2024-01-26 13:57:15 +01:00
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: ["something, or another thing "] },
|
|
|
|
|
{ attribute: "something", operator: "else", values: [""] })
|
2024-01-23 16:48:37 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with a single filter with a single value with " (escaped)' do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: 'active = "something, or another thing \" something else"'
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: ["something, or another thing \" something else"] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-26 13:57:15 +01:00
|
|
|
context "with a single filter with a single value with ' (escaped)" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = 'something, or another thing \\' something else'"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: ["something, or another thing ' something else"] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a single filter with no value" do
|
2024-01-23 16:48:37 +01:00
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "cf_512 !* "
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
2024-01-26 13:57:15 +01:00
|
|
|
.to contain_exactly({ attribute: "cf_512", operator: "!*", values: [""] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with multiple filters with the first having no value" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active !* & id = \"1\""
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "!*", values: [] },
|
|
|
|
|
{ attribute: "id", operator: "=", values: ["1"] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with multiple filters with ampersand as a filter value" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = \"t\" & name_and_identifier ~ \"abc & def\""
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: ["t"] },
|
|
|
|
|
{ attribute: "name_and_identifier", operator: "~", values: ["abc & def"] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a corrupt filter only having a key" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "", values: [""] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a corrupt filter having opening braces but no closing ones" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = [\"t\", \"f\""
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[t f] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a corrupt filter having opening double quotes but no closing ones" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: 'active = "t'
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[t] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with a corrupt filter having opening single quotes but no closing ones" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
filters: "active = 't"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[t] })
|
2024-01-23 16:48:37 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-04-19 14:02:46 +02:00
|
|
|
context "with an old (APIv3) style filter" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
filters: JSON.dump([{ active: { operator: "=", values: ["f"] } },
|
|
|
|
|
{ cf_32: { operator: "=", values: ["63"] } }, # rubocop:disable Naming/VariableNumber
|
|
|
|
|
{ cf_34: { operator: "=", values: ["2006"] } }]) # rubocop:disable Naming/VariableNumber
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:filters])
|
|
|
|
|
.to contain_exactly({ attribute: "active", operator: "=", values: %w[f] },
|
|
|
|
|
{ attribute: "cf_32", operator: "=", values: %w[63] },
|
|
|
|
|
{ attribute: "cf_34", operator: "=", values: %w[2006] })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-23 16:48:37 +01:00
|
|
|
context "with sortBy with a single value" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
sortBy: JSON.dump([%w[name asc]])
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:orders])
|
|
|
|
|
.to eql [{ attribute: "name", direction: "asc" }]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with sortBy with a multiple value" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
sortBy: JSON.dump([%w[name asc], %w[created_at desc]])
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the parsed filter" do
|
|
|
|
|
expect(subject[:orders])
|
|
|
|
|
.to eql [{ attribute: "name", direction: "asc" }, { attribute: "created_at", direction: "desc" }]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with an invalid sortBy" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
sortBy: "[sjfkdsjfkd}"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns an invalid sort order" do
|
|
|
|
|
expect(subject[:orders])
|
|
|
|
|
.to eql [{ attribute: "invalid", direction: "asc" }]
|
|
|
|
|
end
|
|
|
|
|
end
|
2024-02-01 18:02:49 +01:00
|
|
|
|
|
|
|
|
context "with multiple columns" do
|
|
|
|
|
let(:params) do
|
|
|
|
|
{
|
|
|
|
|
columns: "name cf_1 project_status"
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns an invalid sort order" do
|
2024-02-16 09:25:24 +01:00
|
|
|
expect(subject[:selects])
|
2024-02-01 18:02:49 +01:00
|
|
|
.to eql %w[name cf_1 project_status]
|
|
|
|
|
end
|
|
|
|
|
end
|
2024-01-23 16:48:37 +01:00
|
|
|
end
|
|
|
|
|
end
|