Skip to content

Commit

Permalink
have all v3 end points accept zipped params
Browse files Browse the repository at this point in the history
  • Loading branch information
ulferts committed Dec 12, 2019
1 parent afdd266 commit 0cc165c
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 20 deletions.
4 changes: 4 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2686,6 +2686,10 @@ en:
estimated_hours: "Estimated hours cannot be set on parent work packages."
invalid_user_assigned_to_work_package: "The chosen user is not allowed to be '%{property}' for this work package."
start_date: "Start date cannot be set on parent work packages."
eprops:
invalid_gzip: "is invalid gzip: %{message}"
invalid_json: "is invalid json: %{message}"

resources:
schema: 'Schema'

Expand Down
7 changes: 7 additions & 0 deletions lib/api/v3/root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
module API
module V3
class Root < ::API::OpenProjectAPI
helpers ::API::V3::Utilities::EpropsConversion

# All endpoint accept query props as gzipped and base64 encoded json objects
before do
transform_eprops
end

mount ::API::V3::Activities::ActivitiesAPI
mount ::API::V3::Attachments::AttachmentsAPI
mount ::API::V3::Categories::CategoriesAPI
Expand Down
51 changes: 51 additions & 0 deletions lib/api/v3/utilities/eprops_conversion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#-- 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 Utilities
module EpropsConversion
def raise_invalid_eprops(error, i18n_key)
mapped_error = OpenStruct.new(params: [:eprops], message: I18n.t(i18n_key, message: error.message))
raise ::Grape::Exceptions::ValidationErrors.new errors: [mapped_error]
end

def transform_eprops
if params && params[:eprops]
props = ::JSON.parse(Zlib::Inflate.inflate(Base64.decode64(params[:eprops]))).with_indifferent_access
params.merge!(props)
end
rescue Zlib::DataError => e
raise_invalid_eprops(e, 'api_v3.errors.eprops.invalid_gzip')
rescue JSON::ParserError, NoMethodError => e
raise_invalid_eprops(e, 'api_v3.errors.eprops.invalid_json')
end
end
end
end
end
127 changes: 116 additions & 11 deletions spec/requests/api/v3/work_package_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,27 @@
login_as(current_user)
end

describe '#get list' do
describe 'GET /api/v3/work_packages' do
subject { last_response }

let(:path) { api_v3_paths.work_packages }
let(:other_work_package) { FactoryBot.create(:work_package) }
let(:work_packages) { [work_package, other_work_package] }

before(:each) do
work_package.save!
get api_v3_paths.work_packages
work_packages
get path
end

it 'succeeds' do
expect(subject.status).to eql 200
end

it 'returns visible work packages' do
FactoryBot.create(:work_package, project: project)
expect(subject.body).to be_json_eql(1.to_json).at_path('total')
end

it 'embedds the work package schemas' do
FactoryBot.create(:work_package, project: project)

expect(subject.body)
.to be_json_eql(api_v3_paths.work_package_schema(project.id, work_package.type.id).to_json)
.at_path('_embedded/schemas/_embedded/elements/0/_links/self/href')
Expand All @@ -104,7 +105,6 @@
end

it 'returns no work packages' do
FactoryBot.create(:work_package, project: project)
expect(subject.body).to be_json_eql(0.to_json).at_path('total')
end

Expand All @@ -114,9 +114,114 @@
it_behaves_like 'unauthorized access'
end
end

describe 'encoded query props' do
let(:props) do
eprops = {
filters: [{ id: { operator: '=', values: [work_package.id.to_s, other_visible_work_package.id.to_s] } }].to_json,
sortBy: [%w(id asc)].to_json,
pageSize: 1
}.to_json

{
eprops: Base64.encode64(Zlib::Deflate.deflate(eprops))
}.to_query
end
let(:path) { "#{api_v3_paths.work_packages}?#{props}" }
let(:other_visible_work_package) do
FactoryBot.create(:work_package,
project: project)
end
let(:another_visible_work_package) do
FactoryBot.create(:work_package,
project: project)
end

let(:work_packages) { [work_package, other_work_package, other_visible_work_package, another_visible_work_package] }

it 'succeeds' do
expect(subject.status)
.to eql 200
end

it 'returns visible and filtered work packages' do
expect(subject.body)
.to be_json_eql(2.to_json)
.at_path('total')

# because of the page size
expect(subject.body)
.to be_json_eql(1.to_json)
.at_path('count')

expect(subject.body)
.to be_json_eql(work_package.id.to_json)
.at_path('_embedded/elements/0/id')
end

context 'non zlibbed' do
let(:props) do
eprops = {
filters: [{ id: { operator: '=', values: [work_package.id.to_s, other_visible_work_package.id.to_s] } }].to_json,
sortBy: [%w(id asc)].to_json,
pageSize: 1
}.to_json

{
eprops: Base64.encode64(eprops)
}.to_query
end

it_behaves_like 'param validation error'
end

context 'non json encoded' do
let(:props) do
eprops = "some non json string"

{
eprops: Base64.encode64(Zlib::Deflate.deflate(eprops))
}.to_query
end

it_behaves_like 'param validation error'
end

context 'non base64 encoded' do
let(:props) do
eprops = {
filters: [{ id: { operator: '=', values: [work_package.id.to_s, other_visible_work_package.id.to_s] } }].to_json,
sortBy: [%w(id asc)].to_json,
pageSize: 1
}.to_json

{
eprops: Zlib::Deflate.deflate(eprops)
}.to_query
end

it_behaves_like 'param validation error'
end

context 'non hash' do
let(:props) do
eprops = [{
filters: [{ id: { operator: '=', values: [work_package.id.to_s, other_visible_work_package.id.to_s] } }].to_json,
sortBy: [%w(id asc)].to_json,
pageSize: 1
}].to_json

{
eprops: Base64.encode64(Zlib::Deflate.deflate(eprops))
}.to_query
end

it_behaves_like 'param validation error'
end
end
end

describe '#get' do
describe 'GET /api/v3/work_packages/:id' do
let(:get_path) { api_v3_paths.work_package work_package.id }

context 'when acting as a user with permission to view work package' do
Expand Down Expand Up @@ -211,7 +316,7 @@
end
end

describe '#patch' do
describe 'PATCH /api/v3/work_packages/:id' do
let(:patch_path) { api_v3_paths.work_package work_package.id }
let(:valid_params) do
{
Expand Down Expand Up @@ -979,7 +1084,7 @@
end
end

describe '#delete' do
describe 'DELETE /api/v3/work_packages/:id' do
let(:path) { api_v3_paths.work_package work_package.id }

before do
Expand Down Expand Up @@ -1026,7 +1131,7 @@
end
end

describe '#post' do
describe 'POST /api/v3/work_packages' do
let(:path) { api_v3_paths.work_packages }
let(:permissions) { %i[add_work_packages view_project] }
let(:status) { FactoryBot.build(:status, is_default: true) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@
let(:work_packages) do
[
FactoryBot.create(:work_package,
project: project,
priority: priority1,
estimated_hours: 1),
project: project,
priority: priority1,
estimated_hours: 1),
FactoryBot.create(:work_package,
project: project,
priority: priority2,
estimated_hours: 2),
project: project,
priority: priority2,
estimated_hours: 2),
FactoryBot.create(:work_package,
project: project,
priority: priority1,
estimated_hours: 3)
project: project,
priority: priority1,
estimated_hours: 3)
]
end
let(:expected_group1) do
Expand Down

0 comments on commit 0cc165c

Please sign in to comment.