[#44876] Implement API endpoint for creating upload link

https://community.openproject.org/work_packages/44876
pull/11677/head
Andreas Pfohl 2 years ago
parent 3fff136125
commit 02d38aab0c
No known key found for this signature in database
GPG Key ID: FF58F3B771328EB4
  1. 18
      modules/storages/app/common/storages/peripherals/storage_requests.rb
  2. 48
      modules/storages/lib/api/v3/storage_files/storage_files_api.rb
  3. 9
      modules/storages/lib/api/v3/storage_files/storage_upload_link_representer.rb
  4. 4
      modules/storages/lib/open_project/storages/engine.rb
  5. 27
      modules/storages/spec/lib/api/v3/storage_files/storage_upload_link_representer_spec.rb
  6. 65
      modules/storages/spec/requests/api/v3/storages/storages_spec.rb

@ -45,6 +45,24 @@ module Storages::Peripherals
.map { |query| query.method(:query).to_proc } .map { |query| query.method(:query).to_proc }
end end
# ToDo: Remove if implemented
# rubocop:disable Lint/UnusedMethodArgument
# rubocop:disable Lint/UnusedBlockArgument
def upload_link_query(user:, finalize_url:)
query_stub = ->(payload) do
ServiceResult.success(
result: Storages::UploadLink.new(
"https://example.com/upload/xyz123",
nil
)
)
end
ServiceResult.success(result: query_stub)
end
# rubocop:enable Lint/UnusedBlockArgument
# rubocop:enable Lint/UnusedMethodArgument
private private
def storage_queries(user) def storage_queries(user)

@ -54,16 +54,44 @@ module API::V3::StorageFiles
files_query files_query
.call(params[:parent]) .call(params[:parent])
.map do |files| .map do |files|
API::V3::StorageFiles::StorageFileCollectionRepresenter.new( API::V3::StorageFiles::StorageFileCollectionRepresenter.new(
files, files,
self_link: api_v3_paths.storage_files(@storage.id), self_link: api_v3_paths.storage_files(@storage.id),
current_user: current_user:
) )
end end
.match( .match(
on_success: ->(representer) { representer }, on_success: ->(representer) { representer },
on_failure: ->(error) { raise_error(error) } on_failure: ->(error) { raise_error(error) }
) )
},
on_failure: ->(error) { raise_error(error) }
)
end
# RequestBody:
# {
# "fileName": "ape.png",
# "parent": "/Pictures"
# }
post :prepare_upload do
raise ::API::Errors::NotFound unless OpenProject::FeatureDecisions.storage_file_upload_active?
Storages::Peripherals::StorageRequests
.new(storage: @storage)
.upload_link_query(
user: current_user,
finalize_url: nil # ToDo: api_v3_paths.finalize_upload(@storage.id)
)
.match(
on_success: ->(upload_link_query) {
upload_link_query
.call(request_body)
.map { |link| API::V3::StorageFiles::StorageUploadLinkRepresenter.new(link, current_user:) }
.match(
on_success: ->(representer) { representer },
on_failure: ->(error) { raise_error(error) }
)
}, },
on_failure: ->(error) { raise_error(error) } on_failure: ->(error) { raise_error(error) }
) )

@ -35,7 +35,8 @@ module API::V3::StorageFiles
link :destination do link :destination do
{ {
href: represented.destination, href: represented.destination,
method: :post method: :post,
title: 'Upload File'
} }
end end
@ -43,8 +44,10 @@ module API::V3::StorageFiles
next if represented.finalize.nil? next if represented.finalize.nil?
{ {
href: represented.finalize, href: represented.finalize.destination,
method: :post method: :post,
title: 'Conclude file upload',
payload: represented.finalize.payload
} }
end end

@ -121,6 +121,10 @@ module OpenProject::Storages
"#{root}/storages/#{storage_id}/files" "#{root}/storages/#{storage_id}/files"
end end
add_api_path :prepare_upload do |storage_id|
"#{root}/storages/#{storage_id}/files/prepare_upload"
end
add_api_path :file_links do |work_package_id| add_api_path :file_links do |work_package_id|
"#{work_package(work_package_id)}/file_links" "#{work_package(work_package_id)}/file_links"
end end

@ -34,7 +34,6 @@ describe API::V3::StorageFiles::StorageUploadLinkRepresenter, 'rendering' do
let(:user) { build_stubbed(:user) } let(:user) { build_stubbed(:user) }
let(:token) { 'xyz123' } let(:token) { 'xyz123' }
let(:destination) { "https://example.com/upload/#{token}" } let(:destination) { "https://example.com/upload/#{token}" }
let(:finalize) { "https://example.com/upload/finalize/#{token}" }
let(:upload_link) do let(:upload_link) do
Storages::UploadLink.new("https://example.com/upload/#{token}") Storages::UploadLink.new("https://example.com/upload/#{token}")
end end
@ -54,9 +53,10 @@ describe API::V3::StorageFiles::StorageUploadLinkRepresenter, 'rendering' do
describe 'without finalize link' do describe 'without finalize link' do
describe 'to destination' do describe 'to destination' do
it_behaves_like 'has an untitled link' do it_behaves_like 'has a titled link' do
let(:link) { 'destination' } let(:link) { 'destination' }
let(:href) { destination } let(:href) { destination }
let(:title) { 'Upload File' }
end end
end end
@ -66,28 +66,5 @@ describe API::V3::StorageFiles::StorageUploadLinkRepresenter, 'rendering' do
end end
end end
end end
describe 'with finalize link' do
let(:upload_link) do
Storages::UploadLink.new(
"https://example.com/upload/#{token}",
finalize
)
end
describe 'to destination' do
it_behaves_like 'has an untitled link' do
let(:link) { 'destination' }
let(:href) { destination }
end
end
describe 'to finalize' do
it_behaves_like 'has an untitled link' do
let(:link) { 'finalize' }
let(:href) { finalize }
end
end
end
end end
end end

@ -278,4 +278,69 @@ describe 'API v3 storages resource', content_type: :json do
end end
end end
end end
describe 'POST /api/v3/storages/:storage_id/files/prepare_upload', with_flag: { storage_file_upload: true } do
let(:path) { api_v3_paths.prepare_upload(storage.id) }
let(:upload_link) { Storages::UploadLink.new('https://example.com/upload/xyz123') }
let(:body) { { fileName: "ape.png", parent: "/Pictures" }.to_json }
subject(:last_response) do
post(path, body)
end
describe 'with successful response' do
before do
storage_requests = instance_double(Storages::Peripherals::StorageRequests)
uplaod_link_query = Proc.new { ServiceResult.success(result: upload_link) }
allow(storage_requests).to receive(:upload_link_query).and_return(ServiceResult.success(result: uplaod_link_query))
allow(Storages::Peripherals::StorageRequests).to receive(:new).and_return(storage_requests)
end
subject { last_response.body }
it { is_expected.to be_json_eql(Storages::UploadLink.name.split('::').last.to_json).at_path('_type') }
it do
expect(subject)
.to(be_json_eql("#{::API::V3::URN_PREFIX}storages:upload_link:no_link_provided".to_json)
.at_path('_links/self/href'))
end
it { is_expected.to be_json_eql(upload_link.destination.to_json).at_path('_links/destination/href') }
it { is_expected.to be_json_eql("post".to_json).at_path('_links/destination/method') }
it { is_expected.to be_json_eql("Upload File".to_json).at_path('_links/destination/title') }
end
describe 'with files query creation failed' do
let(:storage_requests) { instance_double(Storages::Peripherals::StorageRequests) }
before do
allow(Storages::Peripherals::StorageRequests).to receive(:new).and_return(storage_requests)
end
describe 'due to authorization failure' do
before do
allow(storage_requests).to receive(:upload_link_query).and_return(ServiceResult.failure(result: :not_authorized))
end
it { expect(last_response.status).to be(500) }
end
describe 'due to internal error' do
before do
allow(storage_requests).to receive(:upload_link_query).and_return(ServiceResult.failure(result: :error))
end
it { expect(last_response.status).to be(500) }
end
describe 'due to not found' do
before do
allow(storage_requests).to receive(:upload_link_query).and_return(ServiceResult.failure(result: :not_found))
end
it { expect(last_response.status).to be(404) }
end
end
end
end end

Loading…
Cancel
Save