Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storages new folder API endpoint #17375

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
description: |-
A valid request body to create a new folder on a external storage
value:
name: Uploads
parentId: "200"
13 changes: 13 additions & 0 deletions docs/api/apiv3/components/schemas/storage_folder_write_model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Schema: StorageFolderWriteModel
---
type: object
required:
- name
- parentId
properties:
name:
type: string
description: Name of the folder to be created
parentId:
type: string
description: Unique identifier of the parent folder in which the new folder should be created in
6 changes: 6 additions & 0 deletions docs/api/apiv3/openapi-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ paths:
"$ref": "./paths/storage_files.yml"
"/api/v3/storages/{id}/files/prepare_upload":
"$ref": "./paths/storage_files_prepare_upload.yml"
"/api/v3/storages/{id}/folders":
"$ref": "./paths/storage_folders.yml"
"/api/v3/storages/{id}/oauth_client_credentials":
"$ref": "./paths/storage_oauth_client_credentials.yml"
"/api/v3/storages/{id}/open":
Expand Down Expand Up @@ -545,6 +547,8 @@ components:
$ref: "./components/examples/status_response.yml"
StorageNextcloudResponse:
$ref: "./components/examples/storage-nextcloud-response.yml"
StorageCreateFolderRequestBody:
$ref: "./components/examples/storage-create-folder-request-body.yml"
StorageNextcloudResponseForCreation:
$ref: "./components/examples/storage-nextcloud-response-for-creation.yml"
StorageNextcloudUnauthorizedResponse:
Expand Down Expand Up @@ -825,6 +829,8 @@ components:
"$ref": "./components/schemas/storage_file_model.yml"
StorageFilesModel:
"$ref": "./components/schemas/storage_files_model.yml"
StorageFolderWriteModel:
"$ref": "./components/schemas/storage_folder_write_model.yml"
StorageFileUploadPreparationModel:
"$ref": "./components/schemas/storage_file_upload_preparation_model.yml"
StorageFileUploadLinkModel:
Expand Down
68 changes: 68 additions & 0 deletions docs/api/apiv3/paths/storage_folders.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# /api/v3/storages/{id}/folders
---
post:
summary: Creation of a new folder
operationId: create_storage_folder
tags:
- File links
description: Creates a new folder under the given parent
parameters:
- name: id
description: Storage id
in: path
required: true
schema:
type: integer
example: 1337
requestBody:
content:
application/json:
schema:
$ref: '../components/schemas/storage_folder_write_model.yml'
brunopagno marked this conversation as resolved.
Show resolved Hide resolved
examples:
'Valid example':
$ref: '../components/examples/storage-create-folder-request-body.yml'
responses:
'201':
description: Created
content:
application/hal+json:
schema:
$ref: '../components/schemas/storage_file_model.yml'
brunopagno marked this conversation as resolved.
Show resolved Hide resolved
'400':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidQuery
message: The given parent is not a directory.
description: |-
Returned if the request is missing a required parameter.
'403':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not authorized to access this resource.
description: |-
Returned if the client does not have sufficient permissions.

**Required permission:** manage file links
'404':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the storage does not exist or the client does not have sufficient permissions to see it.

**Required permission:** view file links
77 changes: 77 additions & 0 deletions modules/storages/app/services/storages/create_folder_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 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 Storages
class CreateFolderService < BaseService
using Peripherals::ServiceResultRefinements

def self.call(storage:, user:, name:, parent_id:)
new.call(storage:, user:, name:, parent_id:)
end

def call(storage:, user:, name:, parent_id:)
auth_strategy = Peripherals::Registry.resolve("#{storage}.authentication.user_bound").call(user: user)

Peripherals::Registry
.resolve("#{storage}.commands.create_folder")
.call(
storage:,
auth_strategy:,
folder_name: name,
parent_location: parent_path(parent_id, storage, user)
)
end

private

def parent_path(parent_id, storage, user)
case storage.short_provider_type
when "nextcloud"
location_from_file_info(parent_id, storage, user)
when "one_drive"
Peripherals::ParentFolder.new(parent_id)
else
raise "Unknown Storage Type"
end
end

def location_from_file_info(parent_id, storage, user)
StorageFileService
.call(storage: storage, user: user, file_id: parent_id)
.match(
on_success: lambda { |folder_info|
path = URI.decode_uri_component(folder_info.location)
Peripherals::ParentFolder.new(path)
},
on_failure: ->(error) { raise error }
)
end
end
end
66 changes: 66 additions & 0 deletions modules/storages/lib/api/v3/storage_files/storage_folders_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 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
module V3
module StorageFiles
class StorageFoldersAPI < ::API::OpenProjectAPI
using ::Storages::Peripherals::ServiceResultRefinements

helpers ::Storages::Peripherals::StorageErrorHelper

resources :folders do
params do
requires :name, type: String, desc: "Folder name"
brunopagno marked this conversation as resolved.
Show resolved Hide resolved
requires :parent_id, type: String, desc: "Id of the parent folder"
end

post do
::Storages::CreateFolderService.call(
storage: @storage,
user: current_user,
name: params["name"],
parent_id: params["parent_id"]
).match(
on_success: lambda { |storage_folder|
API::V3::StorageFiles::StorageFileRepresenter.new(
storage_folder,
@storage,
current_user:
)
},
on_failure: ->(error) { raise_error(error) }
)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions modules/storages/lib/api/v3/storages/storages_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class API::V3::Storages::StoragesAPI < API::OpenProjectAPI
mount API::V3::StorageFiles::StorageFilesAPI
mount API::V3::OAuthClient::OAuthClientCredentialsAPI
mount API::V3::Storages::StorageOpenAPI
mount API::V3::StorageFiles::StorageFoldersAPI
end
end
end
4 changes: 4 additions & 0 deletions modules/storages/lib/open_project/storages/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ def self.external_file_permissions
"#{storage_files(storage_id)}/#{file_id}"
end

add_api_path :storage_folders do |storage_id|
"#{storage(storage_id)}/folders"
end

add_api_path :prepare_upload do |storage_id|
"#{storage(storage_id)}/files/prepare_upload"
end
Expand Down
Loading
Loading