From 7bc43c41b568a63e1500260a8e9047fc4b9cd285 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Mon, 11 Nov 2024 14:20:09 +0100 Subject: [PATCH] feat: Add OpenAPI Signed-off-by: provokateurin --- .eslintrc.js | 11 + .github/workflows/openapi.yml | 94 ++ README.md | 30 +- REUSE.toml | 6 + composer.json | 3 +- lib/AppInfo/Capabilities.php | 8 + lib/Command/FolderCommand.php | 4 +- lib/Controller/DelegationController.php | 26 +- lib/Controller/FolderController.php | 161 ++- lib/DAV/GroupFoldersHome.php | 8 +- lib/Folder/FolderManager.php | 74 +- lib/Mount/MountProvider.php | 6 +- lib/ResponseDefinitions.php | 55 + lib/Service/FoldersFilter.php | 12 +- lib/Trash/TrashBackend.php | 6 +- lib/Versions/GroupVersionsExpireManager.php | 5 +- lib/Versions/VersionsBackend.php | 6 +- openapi.json | 1415 +++++++++++++++++++ package-lock.json | 289 +++- package.json | 4 +- src/settings/AdminGroupSelect.tsx | 9 +- src/settings/Api.ts | 57 +- src/settings/App.tsx | 13 +- src/settings/FolderGroups.tsx | 10 +- src/settings/SubAdminGroupSelect.tsx | 9 +- src/types/index.ts | 14 + src/types/openapi/openapi.ts | 928 ++++++++++++ vendor-bin/openapi-extractor/composer.json | 5 + vendor-bin/openapi-extractor/composer.lock | 240 ++++ 29 files changed, 3357 insertions(+), 151 deletions(-) create mode 100644 .github/workflows/openapi.yml create mode 100644 lib/ResponseDefinitions.php create mode 100644 openapi.json create mode 100644 src/types/index.ts create mode 100644 src/types/openapi/openapi.ts create mode 100644 vendor-bin/openapi-extractor/composer.json create mode 100644 vendor-bin/openapi-extractor/composer.lock diff --git a/.eslintrc.js b/.eslintrc.js index b4cf54e72..a58941204 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,4 +6,15 @@ module.exports = { extends: [ '@nextcloud/eslint-config/typescript', ], + overrides: [ + { + files: ['src/types/openapi/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + quotes: 'off', + 'no-multiple-empty-lines': 'off', + 'no-use-before-define': 'off', + }, + }, + ], } diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml new file mode 100644 index 000000000..cf6d2778c --- /dev/null +++ b/.github/workflows/openapi.yml @@ -0,0 +1,94 @@ +# This workflow is provided via the organization template repository +# +# https://github.com/nextcloud/.github +# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization +# +# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-FileCopyrightText: 2024 Arthur Schiwon +# SPDX-License-Identifier: MIT + +name: OpenAPI + +on: pull_request + +permissions: + contents: read + +concurrency: + group: openapi-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + openapi: + runs-on: ubuntu-latest + + if: ${{ github.repository_owner != 'nextcloud-gmbh' }} + + steps: + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Get php version + id: php_versions + uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 + + - name: Set up php + uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1 + with: + php-version: ${{ steps.php_versions.outputs.php-available }} + extensions: xml + coverage: none + ini-file: development + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check Typescript OpenApi types + id: check_typescript_openapi + uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 + with: + files: "src/types/openapi/openapi*.ts" + + - name: Read package.json node and npm engines version + if: steps.check_typescript_openapi.outputs.files_exists == 'true' + uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 + id: node_versions + # Continue if no package.json + continue-on-error: true + with: + fallbackNode: '^20' + fallbackNpm: '^10' + + - name: Set up node ${{ steps.node_versions.outputs.nodeVersion }} + if: ${{ steps.node_versions.outputs.nodeVersion }} + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: ${{ steps.node_versions.outputs.nodeVersion }} + + - name: Set up npm ${{ steps.node_versions.outputs.npmVersion }} + if: ${{ steps.node_versions.outputs.nodeVersion }} + run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}' + + - name: Install dependencies & build + if: ${{ steps.node_versions.outputs.nodeVersion }} + env: + CYPRESS_INSTALL_BINARY: 0 + PUPPETEER_SKIP_DOWNLOAD: true + run: | + npm ci + + - name: Set up dependencies + run: composer i + + - name: Regenerate OpenAPI + run: composer run openapi + + - name: Check openapi*.json and typescript changes + run: | + bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please run \"composer run openapi\" and commit the openapi*.json files and (if applicable) src/types/openapi/openapi*.ts, see the section \"Show changes on failure\" for details' && exit 1)" + + - name: Show changes on failure + if: failure() + run: | + git status + git --no-pager diff + exit 1 # make it red to grab attention diff --git a/README.md b/README.md index d3a9a5ce4..171725def 100644 --- a/README.md +++ b/README.md @@ -126,35 +126,7 @@ To disable the advanced permissions feature for a group folder, use `occ groupfo ### REST API -Group folders can be configured externally through REST APIs. - -The following REST API's are supported: - -- `GET apps/groupfolders/folders`: Returns a list of all configured folders and their settings -- `POST apps/groupfolders/folders`: Create a new group folder - - `mountpoint`: The name for the new folder -- `GET apps/groupfolders/folders/$folderId`: Return a specific configured folder and its settings -- `DELETE apps/groupfolders/folders/$folderId`: Delete a group folder -- `POST apps/groupfolders/folders/$folderId/groups`: Give a group access to a folder - - `group`: The id of the group to be given access to the folder -- `DELETE apps/groupfolders/folders/$folderId/groups/$groupId`: Remove access from a group to a folder -- `POST apps/groupfolders/folders/$folderId/acl`: Enable/Disable folder advanced permissions - - `acl` 1 for enable, 0 for disable. -- `POST apps/groupfolders/folders/$folderId/manageACL`: Grants/Removes a group or user the ability to manage a groupfolders' advanced permissions - - `$mappingId`: the id of the group/user to be granted/removed access to/from the folder - - `$mappingType`: 'group' or 'user' - - `$manageAcl`: true to grants ability to manage a groupfolders' advanced permissions, false to remove -- `POST apps/groupfolders/folders/$folderId/groups/$groupId`: Set the permissions a group has in a folder - - `permissions` The new permissions for the group as bitmask of [permissions constants](https://github.com/nextcloud/server/blob/b4f36d44c43aac0efdc6c70ff8e46473341a9bfe/lib/public/Constants.php#L65) -- `POST apps/groupfolders/folders/$folderId/quota`: Set the quota for a folder - - `quota`: The new quota for the folder in bytes, user `-3` for unlimited -- `POST apps/groupfolders/folders/$folderId/mountpoint`: Change the name of a folder - - `mountpoint`: The new name for the folder - -For all `POST` calls the required parameters are listed. - -Non-admins can access the `GET` requests to retrieve info about group folders they have access to. -Admins can add `applicable=1` as a parameter to the group folder list request to get the same filtered results of only folders they have direct access to. +See the [OpenAPI specification](openapi.json) to learn about all available API endpoints: https://petstore.swagger.io/?url=https://raw.githubusercontent.com/nextcloud/groupfolders/master/openapi.json ### WebDAV API diff --git a/REUSE.toml b/REUSE.toml index 3ad49a1f1..91ec72aad 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -76,3 +76,9 @@ path = ["tests/stubs/icewind_streams_**"] precedence = "aggregate" SPDX-FileCopyrightText = "none" SPDX-License-Identifier = "MIT" + +[[annotations]] +path = ["openapi.json", "src/types/openapi/openapi.ts"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/composer.json b/composer.json index e790aa28f..85d9e99a8 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "psalm:fix": "psalm --no-cache --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType", "test:unit": "phpunit -c tests/phpunit.xml", "test:unit:coverage": "XDEBUG_MODE=coverage phpunit -c tests/phpunit.xml", - "rector": "rector && composer cs:fix" + "rector": "rector && composer cs:fix", + "openapi": "generate-spec && npm run typescript:generate" }, "config": { "allow-plugins": { diff --git a/lib/AppInfo/Capabilities.php b/lib/AppInfo/Capabilities.php index bed591df2..32a2dbb14 100644 --- a/lib/AppInfo/Capabilities.php +++ b/lib/AppInfo/Capabilities.php @@ -21,6 +21,14 @@ public function __construct( ) { } + /** + * @return array{ + * groupfolders?: array{ + * appVersion: string, + * hasGroupFolders: bool, + * }, + * } + */ public function getCapabilities(): array { $user = $this->userSession->getUser(); if (!$user) { diff --git a/lib/Command/FolderCommand.php b/lib/Command/FolderCommand.php index 05cde733d..9a9800196 100644 --- a/lib/Command/FolderCommand.php +++ b/lib/Command/FolderCommand.php @@ -17,6 +17,8 @@ /** * Base command for commands asking the user for a folder id. + * + * @psalm-import-type InternalFolderOut from FolderManager */ abstract class FolderCommand extends Base { @@ -29,7 +31,7 @@ public function __construct( } /** - * @psalm-return ?array{id: mixed, mount_point: string, groups: array|mixed, quota: int, size: int|mixed, acl: bool} + * @return ?InternalFolderOut */ protected function getFolder(InputInterface $input, OutputInterface $output): ?array { $folderId = (int)$input->getArgument('folder_id'); diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index a0f13ebcc..1c5dc1842 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -9,9 +9,11 @@ use OCA\Circles\CirclesManager; use OCA\GroupFolders\Attribute\RequireGroupFolderAdmin; +use OCA\GroupFolders\ResponseDefinitions; use OCA\GroupFolders\Service\DelegationService; use OCA\Settings\Service\AuthorizedGroupService; use OCP\App\IAppManager; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\DataResponse; @@ -23,6 +25,10 @@ use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; +/** + * @psalm-import-type GroupFoldersDelegationGroup from ResponseDefinitions + * @psalm-import-type GroupFoldersDelegationCircle from ResponseDefinitions + */ class DelegationController extends OCSController { public function __construct( string $appName, @@ -39,6 +45,10 @@ public function __construct( /** * Returns the list of all groups + * + * @return DataResponse, array{}> + * + * 200: All groups returned */ #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -61,6 +71,10 @@ public function getAllGroups(): DataResponse { /** * Returns the list of all visible circles + * + * @return DataResponse, array{}> + * + * 200: All circles returned */ #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -97,10 +111,14 @@ public function getAllCircles(): DataResponse { /** * Get the list Groups related to classname. - * If the classname is - * - OCA\GroupFolders\Settings\Admin : It's reference to fields in Admin Priveleges. - * - OCA\GroupFolders\Controller\DelegationController : It's just to specific the subadmins. - * They can only manage groupfolders in which they are added in the Advanced Permissions (groups only) + * @param string $classname If the classname is + * - OCA\GroupFolders\Settings\Admin : It's reference to fields in Admin Privileges. + * - OCA\GroupFolders\Controller\DelegationController : It's just to specific the subadmins. + * They can only manage groupfolders in which they are added in the Advanced Permissions (groups only) + * + * @return DataResponse, array{}> + * + * 200: Authorized groups returned */ #[RequireGroupFolderAdmin] #[NoAdminRequired] diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index ff1d52855..1344f08c0 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -11,6 +11,7 @@ use OCA\GroupFolders\Attribute\RequireGroupFolderAdmin; use OCA\GroupFolders\Folder\FolderManager; use OCA\GroupFolders\Mount\MountProvider; +use OCA\GroupFolders\ResponseDefinitions; use OCA\GroupFolders\Service\DelegationService; use OCA\GroupFolders\Service\FoldersFilter; use OCP\AppFramework\Http; @@ -27,6 +28,12 @@ use OCP\IUser; use OCP\IUserSession; +/** + * @psalm-import-type GroupFoldersGroup from ResponseDefinitions + * @psalm-import-type GroupFoldersUser from ResponseDefinitions + * @psalm-import-type GroupFoldersFolder from ResponseDefinitions + * @psalm-import-type InternalFolderOut from FolderManager + */ class FolderController extends OCSController { private ?IUser $user; @@ -49,6 +56,9 @@ public function __construct( /** * Regular users can access their own folders, but they only get to see the permission for their own groups + * + * @param GroupFoldersFolder $folder + * @return null|GroupFoldersFolder */ private function filterNonAdminFolder(array $folder): ?array { if ($this->user === null) { @@ -65,8 +75,8 @@ private function filterNonAdminFolder(array $folder): ?array { } /** - * @param array{acl: bool, groups: array, id: int, manage: array, mount_point: mixed, quota: int, size: int} $folder - * @return array{acl: bool, group_details: array, groups: array, id: int, manage: array, mount_point: mixed, quota: int, size: int} + * @param InternalFolderOut $folder + * @return GroupFoldersFolder */ private function formatFolder(array $folder): array { // keep compatibility with the old 'groups' field @@ -76,6 +86,15 @@ private function formatFolder(array $folder): array { return $folder; } + /** + * Gets all Groupfolders + * + * @param bool $applicable Filter by applicable groups + * @return DataResponse, array{}> + * @throws OCSNotFoundException Storage not found + * + * 200: Groupfolders returned + */ #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders')] public function getFolders(bool $applicable = false): DataResponse { @@ -84,7 +103,7 @@ public function getFolders(bool $applicable = false): DataResponse { throw new OCSNotFoundException(); } - $folders = $this->manager->getAllFoldersWithSize($storageId); + $folders = array_values($this->manager->getAllFoldersWithSize($storageId)); $folders = array_map($this->formatFolder(...), $folders); $isAdmin = $this->delegationService->isAdminNextcloud() || $this->delegationService->isDelegatedAdmin(); if ($isAdmin && !$applicable) { @@ -96,21 +115,24 @@ public function getFolders(bool $applicable = false): DataResponse { } if ($applicable || !$this->delegationService->hasApiAccess()) { - $folders = array_map($this->filterNonAdminFolder(...), $folders); - $folders = array_filter($folders); + $folders = array_values(array_filter(array_map($this->filterNonAdminFolder(...), $folders))); } return new DataResponse($folders); } + /** + * Gets a Groupfolder by ID + * + * @param int $id ID of the Groupfolder + * @return DataResponse + * @throws OCSNotFoundException Groupfolder not found + * + * 200: Groupfolder returned + */ #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders/{id}')] public function getFolder(int $id): DataResponse { - $response = $this->checkFolderExists($id); - if ($response) { - return $response; - } - $storageId = $this->getRootFolderStorageId(); if ($storageId === null) { throw new OCSNotFoundException(); @@ -120,6 +142,7 @@ public function getFolder(int $id): DataResponse { if ($folder === null) { throw new OCSNotFoundException(); } + $folder = $this->formatFolder($folder); if (!$this->delegationService->hasApiAccess()) { $folder = $this->filterNonAdminFolder($folder); @@ -128,9 +151,12 @@ public function getFolder(int $id): DataResponse { } } - return new DataResponse($this->formatFolder($folder)); + return new DataResponse($folder); } + /** + * @return DataResponse, array{}>|null + */ private function checkFolderExists(int $id): ?DataResponse { $storageId = $this->getRootFolderStorageId(); if ($storageId === null) { @@ -150,7 +176,13 @@ private function getRootFolderStorageId(): ?int { } /** - * @throws OCSNotFoundException + * Add a new Groupfolder + * + * @param string $mountpoint Mountpoint of the new Groupfolder + * @return DataResponse + * @throws OCSNotFoundException Groupfolder not found + * + * 200: Groupfolder added successfully */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] @@ -169,9 +201,19 @@ public function addFolder(string $mountpoint): DataResponse { throw new OCSNotFoundException(); } - return new DataResponse($folder); + return new DataResponse($this->formatFolder($folder)); } + /** + * Remove a Groupfolder + * + * @param int $id ID of the Groupfolder + * @return DataResponse|DataResponse, array{}> + * @throws OCSNotFoundException Groupfolder not found + * + * 200: Groupfolder removed successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -193,6 +235,15 @@ public function removeFolder(int $id): DataResponse { return new DataResponse(['success' => true]); } + /** + * Set the mount point of a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param string $mountPoint New mount point path + * @return DataResponse + * + * 200: Mount point changed successfully + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -202,6 +253,16 @@ public function setMountPoint(int $id, string $mountPoint): DataResponse { return new DataResponse(['success' => true]); } + /** + * Add access of a group for a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param string $group Group to add access for + * @return DataResponse|DataResponse, array{}> + * + * 200: Group access added successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -217,6 +278,16 @@ public function addGroup(int $id, string $group): DataResponse { return new DataResponse(['success' => true]); } + /** + * Remove access of a group from a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param string $group Group to remove access from + * @return DataResponse|DataResponse, array{}> + * + * 200: Group access removed successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -232,6 +303,17 @@ public function removeGroup(int $id, string $group): DataResponse { return new DataResponse(['success' => true]); } + /** + * Set the permissions of a group for a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param string $group Group for which the permissions will be set + * @param int $permissions New permissions + * @return DataResponse|DataResponse, array{}> + * + * 200: Permissions updated successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -248,7 +330,16 @@ public function setPermissions(int $id, string $group, int $permissions): DataRe } /** - * @throws \OCP\DB\Exception + * Updates an ACL mapping + * + * @param int $id ID of the Groupfolder + * @param string $mappingType Type of the ACL mapping + * @param string $mappingId ID of the ACL mapping + * @param bool $manageAcl Whether to enable or disable the ACL mapping + * @return DataResponse|DataResponse, array{}> + * + * 200: ACL mapping updated successfully + * 404: Groupfolder not found */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] @@ -265,6 +356,16 @@ public function setManageACL(int $id, string $mappingType, string $mappingId, bo return new DataResponse(['success' => true]); } + /** + * Set a new quota for a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param int $quota New quota in bytes + * @return DataResponse|DataResponse, array{}> + * + * 200: New quota set successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -280,6 +381,16 @@ public function setQuota(int $id, int $quota): DataResponse { return new DataResponse(['success' => true]); } + /** + * Toggle the ACL for a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param bool $acl Whether ACL should be enabled or not + * @return DataResponse|DataResponse, array{}> + * + * 200: ACL toggled successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -295,6 +406,16 @@ public function setACL(int $id, bool $acl): DataResponse { return new DataResponse(['success' => true]); } + /** + * Rename a Groupfolder + * + * @param int $id ID of the Groupfolder + * @param string $mountpoint New Mountpoint of the Groupfolder + * @return DataResponse|DataResponse, array{}> + * + * 200: Groupfolder renamed successfully + * 404: Groupfolder not found + */ #[PasswordConfirmationRequired] #[RequireGroupFolderAdmin] #[NoAdminRequired] @@ -345,9 +466,19 @@ private function folderDataForXML(array $data): array { return $data; } + /** + * Searches for matching ACL mappings + * + * @param int $id The ID of the Groupfolder + * @param string $search String to search by + * @return DataResponse, groups: list}, array{}> + * @throws OCSForbiddenException Not allowed to search + * + * 200: ACL Mappings returned + */ #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders/{id}/search')] - public function aclMappingSearch(int $id, ?int $fileId, string $search = ''): DataResponse { + public function aclMappingSearch(int $id, string $search = ''): DataResponse { $users = []; $groups = []; diff --git a/lib/DAV/GroupFoldersHome.php b/lib/DAV/GroupFoldersHome.php index d7c894d8a..ea5595280 100644 --- a/lib/DAV/GroupFoldersHome.php +++ b/lib/DAV/GroupFoldersHome.php @@ -10,7 +10,6 @@ use OC\Files\Filesystem; use OCA\GroupFolders\Folder\FolderManager; -use OCP\Files\Cache\ICacheEntry; use OCP\Files\IRootFolder; use OCP\IUser; use RuntimeException; @@ -18,6 +17,9 @@ use Sabre\DAV\Exception\NotFound; use Sabre\DAV\ICollection; +/** + * @psalm-import-type InternalFolder from FolderManager + */ class GroupFoldersHome implements ICollection { public function __construct( private array $principalInfo, @@ -49,7 +51,7 @@ public function createDirectory($name): never { } /** - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry}|null + * @return ?InternalFolder */ private function getFolder(string $name): ?array { $storageId = $this->rootFolder->getMountPoint()->getNumericStorageId(); @@ -68,7 +70,7 @@ private function getFolder(string $name): ?array { } /** - * @param array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry} $folder + * @param InternalFolder $folder */ private function getDirectoryForFolder(array $folder): GroupFolderNode { $userHome = '/' . $this->user->getUID() . '/files'; diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index e47fe631a..94a1e872b 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -13,6 +13,8 @@ use OCA\Circles\Exceptions\CircleNotFoundException; use OCA\Circles\Model\Probes\CircleProbe; use OCA\GroupFolders\Mount\GroupMountPoint; +use OCA\GroupFolders\ResponseDefinitions; +use OCA\GroupFolders\Settings\Admin; use OCP\AutoloadNotAllowedException; use OCP\Constants; use OCP\DB\Exception; @@ -32,6 +34,34 @@ use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; +/** + * @psalm-import-type GroupFoldersGroup from ResponseDefinitions + * @psalm-import-type GroupFoldersUser from ResponseDefinitions + * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions + * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions + * @psalm-type InternalFolder = array{ + * folder_id: int, + * mount_point: string, + * permissions: int, + * quota: int, + * acl: bool, + * rootCacheEntry: ?CacheEntry, + * } + * @psalm-type InternalFolderOut = array{ + * id: int, + * mount_point: string, + * groups: array, + * quota: int, + * size: int, + * acl: bool, + * manage: list, + * } + * @psalm-type InternalFolderMapping = array{ + * folder_id: int, + * mapping_type: 'user'|'group', + * mapping_id: string, + * } + */ class FolderManager { public const SPACE_DEFAULT = -4; @@ -46,7 +76,7 @@ public function __construct( } /** - * @return array>, id: int, mount_point: mixed, quota: int, size: 0}> + * @return array * @throws Exception */ public function getAllFolders(): array { @@ -98,7 +128,7 @@ private function joinQueryWithFileCache(IQueryBuilder $query, int $rootStorageId } /** - * @return array, id: int, manage: array, mount_point: mixed, quota: int, size: int}> + * @return array * @throws Exception */ public function getAllFoldersWithSize(int $rootStorageId): array { @@ -133,7 +163,7 @@ public function getAllFoldersWithSize(int $rootStorageId): array { } /** - * @return array, id: int, manage: array, mount_point: mixed, quota: int, size: int}> + * @return array * @throws Exception */ public function getAllFoldersForUserWithSize(int $rootStorageId, IUser $user): array { @@ -176,7 +206,7 @@ public function getAllFoldersForUserWithSize(int $rootStorageId, IUser $user): a } /** - * @return array> + * @return array> * @throws Exception */ private function getAllFolderMappings(): array { @@ -202,7 +232,7 @@ private function getAllFolderMappings(): array { } /** - * @return array> + * @return array * @throws Exception */ private function getFolderMappings(int $id): array { @@ -215,10 +245,11 @@ private function getFolderMappings(int $id): array { } /** - * @return array{type: 'user'|'group', id: string, displayname: string}[] + * @param InternalFolderMapping[] $mappings + * @return list */ private function getManageAcl(array $mappings): array { - return array_filter(array_map(function (array $entry): ?array { + return array_values(array_filter(array_map(function (array $entry): ?array { if ($entry['mapping_type'] === 'user') { $user = Server::get(IUserManager::class)->get($entry['mapping_id']); if ($user === null) { @@ -242,11 +273,11 @@ private function getManageAcl(array $mappings): array { 'id' => $group->getGID(), 'displayname' => $group->getDisplayName() ]; - }, $mappings)); + }, $mappings))); } /** - * @return ?array{id: mixed, mount_point: mixed, groups: array, quota: int, size: int, acl: bool} + * @return ?InternalFolderOut * @throws Exception */ public function getFolder(int $id, int $rootStorageId = 0): ?array { @@ -306,7 +337,7 @@ public function getFolderByPath(string $path): int { } /** - * @return array>> + * @return array> * @throws Exception */ private function getAllApplicable(): array { @@ -327,7 +358,7 @@ private function getAllApplicable(): array { } if (!$row['circle_id']) { - $entityId = $row['group_id']; + $entityId = (string)$row['group_id']; $entry = [ 'displayName' => $row['group_id'], @@ -335,7 +366,7 @@ private function getAllApplicable(): array { 'type' => 'group' ]; } else { - $entityId = $row['circle_id']; + $entityId = (string)$row['circle_id']; try { $circle = $queryHelper?->extractCircle($row); } catch (CircleNotFoundException) { @@ -357,6 +388,7 @@ private function getAllApplicable(): array { /** * @throws Exception + * @return list */ private function getGroups(int $id): array { $groups = $this->getAllApplicable()[$id] ?? []; @@ -365,7 +397,7 @@ private function getGroups(int $id): array { return array_map(fn (IGroup $group): array => [ 'gid' => $group->getGID(), 'displayname' => $group->getDisplayName() - ], array_filter($groups)); + ], array_values(array_filter($groups))); } /** @@ -416,6 +448,7 @@ public function canManageACL(int $folderId, IUser $user): bool { /** * @throws Exception + * @return list */ public function searchGroups(int $id, string $search = ''): array { $groups = $this->getGroups($id); @@ -423,11 +456,12 @@ public function searchGroups(int $id, string $search = ''): array { return $groups; } - return array_filter($groups, fn (array $group): bool => (stripos($group['gid'], $search) !== false) || (stripos($group['displayname'], $search) !== false)); + return array_values(array_filter($groups, fn (array $group): bool => (stripos($group['gid'], $search) !== false) || (stripos($group['displayname'], $search) !== false))); } /** * @throws Exception + * @return list */ public function searchUsers(int $id, string $search = '', int $limit = 10, int $offset = 0): array { $groups = $this->getGroups($id); @@ -451,7 +485,7 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $ } /** - * @return list + * @return list * @throws Exception */ public function getFoldersForGroup(string $groupId, int $rootStorageId = 0): array { @@ -501,7 +535,7 @@ public function getFoldersForGroup(string $groupId, int $rootStorageId = 0): arr /** * @param string[] $groupIds - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?CacheEntry}[] + * @return list * @throws Exception */ public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): array { @@ -551,11 +585,11 @@ public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): ar 'quota' => $this->getRealQuota((int)$folder['quota']), 'acl' => (bool)$folder['acl'], 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null - ], $result); + ], array_values($result)); } /** - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?CacheEntry}[] + * @return list * @throws Exception */ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId = 0): array { @@ -611,7 +645,7 @@ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId 'quota' => $this->getRealQuota((int)$folder['quota']), 'acl' => (bool)$folder['acl'], 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null - ], $query->executeQuery()->fetchAll()); + ], array_values($query->executeQuery()->fetchAll())); } @@ -836,7 +870,7 @@ public function setFolderACL(int $folderId, bool $acl): void { } /** - * @return list + * @return list * @throws Exception */ public function getFoldersForUser(IUser $user, int $rootStorageId = 0): array { diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index 5d966caa9..ba6b448e6 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -6,7 +6,6 @@ namespace OCA\GroupFolders\Mount; -use OC\Files\Cache\CacheEntry; use OC\Files\Storage\Wrapper\Jail; use OC\Files\Storage\Wrapper\PermissionsMask; use OCA\GroupFolders\ACL\ACLManager; @@ -30,6 +29,9 @@ use OCP\IUser; use OCP\IUserSession; +/** + * @psalm-import-type InternalFolder from FolderManager + */ class MountProvider implements IMountProvider { private ?Folder $root = null; private ?int $rootStorageId = null; @@ -64,7 +66,7 @@ private function getRootStorageId(): int { } /** - * @return list + * @return list */ public function getFoldersForUser(IUser $user): array { return $this->folderManager->getFoldersForUser($user, $this->getRootStorageId()); diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php new file mode 100644 index 000000000..ddfa678fb --- /dev/null +++ b/lib/ResponseDefinitions.php @@ -0,0 +1,55 @@ +, + * groups: array, + * quota: int, + * size: int, + * acl: bool, + * manage: list, + * } + */ +class ResponseDefinitions { +} diff --git a/lib/Service/FoldersFilter.php b/lib/Service/FoldersFilter.php index ee587942a..dcb6fb0d6 100644 --- a/lib/Service/FoldersFilter.php +++ b/lib/Service/FoldersFilter.php @@ -7,9 +7,13 @@ namespace OCA\GroupFolders\Service; +use OCA\GroupFolders\ResponseDefinitions; use OCP\IGroupManager; use OCP\IUserSession; +/** + * @psalm-import-type GroupFoldersFolder from ResponseDefinitions + */ class FoldersFilter { public function __construct( private IUserSession $userSession, @@ -18,8 +22,8 @@ public function __construct( } /** - * @param array $folders List of all folders - * @return array List of folders that the api user can access + * @param list $folders List of all folders + * @return list */ public function getForApiUser(array $folders): array { $user = $this->userSession->getUser(); @@ -27,7 +31,7 @@ public function getForApiUser(array $folders): array { return []; } - return array_filter($folders, function (array $folder) use ($user): bool { + return array_values(array_filter($folders, function (array $folder) use ($user): bool { foreach ($folder['manage'] as $manager) { if ($manager['type'] === 'group') { if ($this->groupManager->isInGroup($user->getUid(), $manager['id'])) { @@ -39,6 +43,6 @@ public function getForApiUser(array $folders): array { } return false; - }); + })); } } diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 7912135a6..4fa5412fd 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -16,7 +16,6 @@ use OCA\GroupFolders\Mount\MountProvider; use OCA\GroupFolders\Versions\VersionsBackend; use OCP\Constants; -use OCP\Files\Cache\ICacheEntry; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; @@ -28,6 +27,9 @@ use OCP\IUserSession; use Psr\Log\LoggerInterface; +/** + * @psalm-import-type InternalFolder from FolderManager + */ class TrashBackend implements ITrashBackend { private ?VersionsBackend $versionsBackend = null; @@ -308,7 +310,7 @@ private function getTrashFolder(int $folderId): Folder { } /** - * @param array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry}[] $folders + * @param list $folders * @return list */ private function getTrashForFolders(IUser $user, array $folders): array { diff --git a/lib/Versions/GroupVersionsExpireManager.php b/lib/Versions/GroupVersionsExpireManager.php index 6ba1a35bf..12dbf5ecb 100644 --- a/lib/Versions/GroupVersionsExpireManager.php +++ b/lib/Versions/GroupVersionsExpireManager.php @@ -19,6 +19,9 @@ use OCP\Files\FileInfo; use OCP\IUser; +/** + * @psalm-import-type InternalFolderOut from FolderManager + */ class GroupVersionsExpireManager { public function __construct( private FolderManager $folderManager, @@ -45,7 +48,7 @@ public function expireFolders(array $folders): void { } /** - * @param array{acl: bool, groups: array>, id: int, mount_point: mixed, quota: int, size: 0} $folder + * @param InternalFolderOut $folder */ public function expireFolder(array $folder): void { $view = new View('/__groupfolders/versions/' . $folder['id']); diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 471f5f968..a326b1df7 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -16,6 +16,7 @@ use OCA\Files_Versions\Versions\IVersion; use OCA\Files_Versions\Versions\IVersionBackend; use OCA\Files_Versions\Versions\IVersionsImporterBackend; +use OCA\GroupFolders\Folder\FolderManager; use OCA\GroupFolders\Mount\GroupFolderStorage; use OCA\GroupFolders\Mount\GroupMountPoint; use OCA\GroupFolders\Mount\MountProvider; @@ -32,6 +33,9 @@ use OCP\IUserSession; use Psr\Log\LoggerInterface; +/** + * @psalm-import-type InternalFolderOut from FolderManager + */ class VersionsBackend implements IVersionBackend, IMetadataVersionBackend, IDeletableVersionBackend, INeedSyncVersionBackend, IVersionsImporterBackend { public function __construct( private IRootFolder $rootFolder, @@ -240,7 +244,7 @@ public function getVersionFile(IUser $user, FileInfo $sourceFile, $revision): Fi } /** - * @param array{id: int, mount_point: string, groups: array|mixed, quota: mixed, size: int, acl: bool} $folder + * @param InternalFolderOut $folder * @return array */ public function getAllVersionedFiles(array $folder): array { diff --git a/openapi.json b/openapi.json new file mode 100644 index 000000000..7233e88aa --- /dev/null +++ b/openapi.json @@ -0,0 +1,1415 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "groupfolders", + "version": "0.0.1", + "description": "Admin configured folders shared with everyone in a group", + "license": { + "name": "agpl" + } + }, + "components": { + "securitySchemes": { + "basic_auth": { + "type": "http", + "scheme": "basic" + }, + "bearer_auth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "AclManage": { + "type": "object", + "required": [ + "displayname", + "id", + "type" + ], + "properties": { + "displayname": { + "type": "string" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "user", + "group" + ] + } + } + }, + "Applicable": { + "type": "object", + "required": [ + "displayName", + "permissions", + "type" + ], + "properties": { + "displayName": { + "type": "string" + }, + "permissions": { + "type": "integer", + "format": "int64" + }, + "type": { + "type": "string", + "enum": [ + "group", + "circle" + ] + } + } + }, + "Capabilities": { + "type": "object", + "properties": { + "groupfolders": { + "type": "object", + "required": [ + "appVersion", + "hasGroupFolders" + ], + "properties": { + "appVersion": { + "type": "string" + }, + "hasGroupFolders": { + "type": "boolean" + } + } + } + } + }, + "DelegationCircle": { + "type": "object", + "required": [ + "singleId", + "displayName" + ], + "properties": { + "singleId": { + "type": "string" + }, + "displayName": { + "type": "string" + } + } + }, + "DelegationGroup": { + "type": "object", + "required": [ + "gid", + "displayName" + ], + "properties": { + "gid": { + "type": "string" + }, + "displayName": { + "type": "string" + } + } + }, + "Folder": { + "type": "object", + "required": [ + "id", + "mount_point", + "group_details", + "groups", + "quota", + "size", + "acl", + "manage" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "mount_point": { + "type": "string" + }, + "group_details": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Applicable" + } + }, + "groups": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "quota": { + "type": "integer", + "format": "int64" + }, + "size": { + "type": "integer", + "format": "int64" + }, + "acl": { + "type": "boolean" + }, + "manage": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AclManage" + } + } + } + }, + "Group": { + "type": "object", + "required": [ + "gid", + "displayname" + ], + "properties": { + "gid": { + "type": "string" + }, + "displayname": { + "type": "string" + } + } + }, + "User": { + "type": "object", + "required": [ + "uid", + "displayname" + ], + "properties": { + "uid": { + "type": "string" + }, + "displayname": { + "type": "string" + } + } + } + } + }, + "paths": { + "/index.php/apps/groupfolders/delegation/groups": { + "get": { + "operationId": "delegation-get-all-groups", + "summary": "Returns the list of all groups", + "tags": [ + "delegation" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "All groups returned", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DelegationGroup" + } + } + } + } + } + } + } + }, + "/index.php/apps/groupfolders/delegation/circles": { + "get": { + "operationId": "delegation-get-all-circles", + "summary": "Returns the list of all visible circles", + "tags": [ + "delegation" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "All circles returned", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DelegationCircle" + } + } + } + } + } + } + } + }, + "/index.php/apps/groupfolders/delegation/authorized-groups": { + "get": { + "operationId": "delegation-get-authorized-groups", + "summary": "Get the list Groups related to classname.", + "tags": [ + "delegation" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "classname", + "in": "query", + "description": "If the classname is - OCA\\GroupFolders\\Settings\\Admin : It's reference to fields in Admin Privileges. - OCA\\GroupFolders\\Controller\\DelegationController : It's just to specific the subadmins. They can only manage groupfolders in which they are added in the Advanced Permissions (groups only)", + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Authorized groups returned", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DelegationGroup" + } + } + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders": { + "get": { + "operationId": "folder-get-folders", + "summary": "Gets all Groupfolders", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "applicable", + "in": "query", + "description": "Filter by applicable groups", + "schema": { + "type": "integer", + "default": 0, + "enum": [ + 0, + 1 + ] + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Groupfolders returned", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Folder" + } + } + } + } + }, + "404": { + "description": "Storage not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "operationId": "folder-add-folder", + "summary": "Add a new Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "mountpoint" + ], + "properties": { + "mountpoint": { + "type": "string", + "description": "Mountpoint of the new Groupfolder" + } + } + } + } + } + }, + "parameters": [ + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Groupfolder added successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Folder" + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}": { + "get": { + "operationId": "folder-get-folder", + "summary": "Gets a Groupfolder by ID", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Groupfolder returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Folder" + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "delete": { + "operationId": "folder-remove-folder", + "summary": "Remove a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Groupfolder removed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "put": { + "operationId": "folder-set-mount-point", + "summary": "Set the mount point of a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "mountPoint" + ], + "properties": { + "mountPoint": { + "type": "string", + "description": "New mount point path" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Mount point changed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/groups": { + "post": { + "operationId": "folder-add-group", + "summary": "Add access of a group for a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "group" + ], + "properties": { + "group": { + "type": "string", + "description": "Group to add access for" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Group access added successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/groups/{group}": { + "delete": { + "operationId": "folder-remove-group", + "summary": "Remove access of a group from a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "group", + "in": "path", + "description": "Group to remove access from", + "required": true, + "schema": { + "type": "string", + "pattern": "^.+$" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Group access removed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "operationId": "folder-set-permissions", + "summary": "Set the permissions of a group for a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "permissions" + ], + "properties": { + "permissions": { + "type": "integer", + "format": "int64", + "description": "New permissions" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "group", + "in": "path", + "description": "Group for which the permissions will be set", + "required": true, + "schema": { + "type": "string", + "pattern": "^.+$" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Permissions updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/manageACL": { + "post": { + "operationId": "folder-set-manageacl", + "summary": "Updates an ACL mapping", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "mappingType", + "mappingId", + "manageAcl" + ], + "properties": { + "mappingType": { + "type": "string", + "description": "Type of the ACL mapping" + }, + "mappingId": { + "type": "string", + "description": "ID of the ACL mapping" + }, + "manageAcl": { + "type": "boolean", + "description": "Whether to enable or disable the ACL mapping" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "ACL mapping updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/quota": { + "post": { + "operationId": "folder-set-quota", + "summary": "Set a new quota for a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "quota" + ], + "properties": { + "quota": { + "type": "integer", + "format": "int64", + "description": "New quota in bytes" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "New quota set successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/acl": { + "post": { + "operationId": "folder-setacl", + "summary": "Toggle the ACL for a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "acl" + ], + "properties": { + "acl": { + "type": "boolean", + "description": "Whether ACL should be enabled or not" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "ACL toggled successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/mountpoint": { + "post": { + "operationId": "folder-rename-folder", + "summary": "Rename a Groupfolder", + "description": "This endpoint requires password confirmation", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "mountpoint" + ], + "properties": { + "mountpoint": { + "type": "string", + "description": "New Mountpoint of the Groupfolder" + } + } + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Groupfolder renamed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "404": { + "description": "Groupfolder not found", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/index.php/apps/groupfolders/folders/{id}/search": { + "get": { + "operationId": "folder-acl-mapping-search", + "summary": "Searches for matching ACL mappings", + "tags": [ + "folder" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The ID of the Groupfolder", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "search", + "in": "query", + "description": "String to search by", + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "ACL Mappings returned", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "users", + "groups" + ], + "properties": { + "users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Group" + } + } + } + } + } + } + }, + "403": { + "description": "Not allowed to search", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "tags": [] +} diff --git a/package-lock.json b/package-lock.json index f4438fc58..241b9e08a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "jest": "^29.4.1", "jest-environment-jsdom": "^29.4.1", "module-replace-webpack-plugin": "0.0.12", + "openapi-typescript": "^7.4.1", "react-hot-loader": "4.13.1", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", @@ -4645,6 +4646,174 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/config": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.12.1.tgz", + "integrity": "sha512-RW3rSirfsPdr0uvATijRDU3f55SuZV3m7/ppdTDvGw4IB0cmeZRkFmqTrchxMqWP50Gfg1tpHnjdxUCNo0E2qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.25.4.tgz", + "integrity": "sha512-qnpr4Z1rzfXdtxQxt/lfGD0wW3UVrm3qhrTpzLG5R/Ze+z+1u8sSRiQHp9N+RT3IuMjh00wq59nop9x9PPa1jQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.12.1", + "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.4", + "js-levenshtein": "^1.1.6", + "js-yaml": "^4.1.0", + "lodash.isequal": "^4.5.0", + "minimatch": "^5.0.1", + "node-fetch": "^2.6.1", + "pluralize": "^8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=14.19.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@redocly/openapi-core/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@redocly/openapi-core/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@redocly/openapi-core/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@redocly/openapi-core/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/@shikijs/core": { "version": "1.17.6", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.17.6.tgz", @@ -7797,6 +7966,13 @@ "node": ">=0.8.0" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/char-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", @@ -13518,6 +13694,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -16212,6 +16401,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -16604,6 +16803,13 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -18526,6 +18732,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-typescript": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.4.1.tgz", + "integrity": "sha512-HrRoWveViADezHCNgQqZmPKmQ74q7nuH/yg9ursFucZaYQNUqsX38fE/V2sKBHVM+pws4tAHpuh/ext2UJ/AoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.25.3", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.1.0", + "supports-color": "^9.4.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/openapi-typescript/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/openapi-typescript/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -20150,7 +20421,6 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -23322,6 +23592,13 @@ "punycode": "^2.1.0" } }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, "node_modules/url": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", @@ -24505,6 +24782,13 @@ "node": ">= 6" } }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -24523,11 +24807,12 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } diff --git a/package.json b/package.json index 54f2c8c0f..f042473a2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix", "cypress": "npm run cypress:e2e", "cypress:e2e": "cypress run --e2e", - "cypress:gui": "cypress open" + "cypress:gui": "cypress open", + "typescript:generate": "npx openapi-typescript ./openapi.json -t -o src/types/openapi/openapi.ts" }, "engines": { "node": "^20.0.0", @@ -46,6 +47,7 @@ "jest": "^29.4.1", "jest-environment-jsdom": "^29.4.1", "module-replace-webpack-plugin": "0.0.12", + "openapi-typescript": "^7.4.1", "react-hot-loader": "4.13.1", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", diff --git a/src/settings/AdminGroupSelect.tsx b/src/settings/AdminGroupSelect.tsx index f6c2824fa..82dbe0634 100644 --- a/src/settings/AdminGroupSelect.tsx +++ b/src/settings/AdminGroupSelect.tsx @@ -8,12 +8,13 @@ import Select from 'react-select' import { CLASS_NAME_ADMIN_DELEGATION } from '../Constants.js' import { Component } from 'react' import { getCurrentUser } from '@nextcloud/auth' -import { Group, Api } from './Api' +import { Api } from './Api' +import type { DelegationGroup } from '../types' interface AdminGroupSelectProps { - groups: Group[], - allGroups: Group[], - delegatedAdminGroups: Group[], + groups: DelegationGroup[], + allGroups: DelegationGroup[], + delegatedAdminGroups: DelegationGroup[], } class AdminGroupSelect extends Component { diff --git a/src/settings/Api.ts b/src/settings/Api.ts index bd3ffc8e7..ebd319508 100644 --- a/src/settings/Api.ts +++ b/src/settings/Api.ts @@ -7,42 +7,7 @@ import axios from '@nextcloud/axios' import { confirmPassword } from '@nextcloud/password-confirmation' // eslint-disable-next-line n/no-unpublished-import import type { OCSResponse } from '@nextcloud/typings/lib/ocs' - -export interface Group { - gid: string; - displayName: string; -} - -export interface Circle { - singleId: string; - displayName: string; -} - -export interface OCSUser { - uid: string; - displayname: string; -} - -export interface OCSGroup { - gid: string; - displayname: string; -} - -export interface ManageRuleProps { - type: string; - id: string; - displayname: string; -} - -export interface Folder { - id: number; - mount_point: string; - quota: number; - size: number; - groups: { [group: string]: number }; - acl: boolean; - manage: ManageRuleProps[]; -} +import type { Folder, Group, User, AclManage, DelegationCircle, DelegationGroup } from '../types' export class Api { @@ -56,25 +21,25 @@ export class Api { } // Returns all NC groups - async listGroups(): Promise { - const response = await axios.get>(this.getUrl('delegation/groups')) + async listGroups(): Promise { + const response = await axios.get>(this.getUrl('delegation/groups')) return response.data.ocs.data } // Returns all visible NC circles - async listCircles(): Promise { - const response = await axios.get>(this.getUrl('delegation/circles')) + async listCircles(): Promise { + const response = await axios.get>(this.getUrl('delegation/circles')) return response.data.ocs.data } // Returns all groups that have been granted delegated admin or subadmin rights on groupfolders - async listDelegatedGroups(classname: string): Promise { - const response = await axios.get>(this.getUrl('/delegation/authorized-groups'), { params: { classname } }) + async listDelegatedGroups(classname: string): Promise { + const response = await axios.get>(this.getUrl('/delegation/authorized-groups'), { params: { classname } }) return response.data.ocs.data.filter(g => g.gid !== 'admin') } // Updates the list of groups that have been granted delegated admin or subadmin rights on groupfolders - async updateDelegatedGroups(newGroups: Group[], classname: string): Promise { + async updateDelegatedGroups(newGroups: DelegationGroup[], classname: string): Promise { await confirmPassword() await axios.post(generateUrl('/apps/settings/') + '/settings/authorizedgroups/saveSettings', { @@ -143,10 +108,10 @@ export class Api { } async aclMappingSearch(folderId: number, search: string): Promise<{ - groups: ManageRuleProps[], - users: ManageRuleProps[] + groups: AclManage[], + users: AclManage[] }> { - const response = await axios.get>(this.getUrl(`folders/${folderId}/search`), { params: { search } }) + const response = await axios.get>(this.getUrl(`folders/${folderId}/search`), { params: { search } }) return { groups: Object.values(response.data.ocs.data.groups).map((item) => { return { diff --git a/src/settings/App.tsx b/src/settings/App.tsx index 3a80ee966..387859300 100644 --- a/src/settings/App.tsx +++ b/src/settings/App.tsx @@ -5,7 +5,8 @@ import * as React from 'react' import { Component, FormEvent } from 'react' -import { Api, Circle, Folder, Group, ManageRuleProps } from './Api' +import { Api } from './Api' +import type { Folder, AclManage, DelegationGroup, DelegationCircle } from '../types' import { FolderGroups } from './FolderGroups' import { QuotaSelect } from './QuotaSelect' import './App.scss' @@ -30,11 +31,11 @@ const defaultQuotaOptions = { export type SortKey = 'mount_point' | 'quota' | 'groups' | 'acl'; export interface AppState { - delegatedAdminGroups: Group[], - delegatedSubAdminGroups: Group[], + delegatedAdminGroups: DelegationGroup[], + delegatedSubAdminGroups: DelegationGroup[], folders: Folder[]; - groups: Group[], - circles: Circle[], + groups: DelegationGroup[], + circles: DelegationCircle[], newMountPoint: string; editingGroup: number; editingMountPoint: number; @@ -368,7 +369,7 @@ export class App extends Component implements OC.Plugin void; - onSearch: (name: string) => Promise<{ groups: ManageRuleProps[]; users: ManageRuleProps[]; }>; + onSearch: (name: string) => Promise<{ groups: AclManage[]; users: AclManage[]; }>; } // eslint-disable-next-line jsdoc/require-jsdoc diff --git a/src/settings/FolderGroups.tsx b/src/settings/FolderGroups.tsx index 64d3036d1..b0be77912 100644 --- a/src/settings/FolderGroups.tsx +++ b/src/settings/FolderGroups.tsx @@ -7,7 +7,7 @@ import * as React from 'react' import Select from 'react-select' import './FolderGroups.scss' -import { Circle, Group } from './Api' +import { DelegationCircle, DelegationGroup } from '../types' import { loadState } from '@nextcloud/initial-state' // eslint-disable-next-line jsdoc/require-jsdoc @@ -17,8 +17,8 @@ function hasPermissions(value: number, check: number): boolean { export interface FolderGroupsProps { groups: { [group: string]: number }, - allCircles?: Circle[], - allGroups?: Group[], + allCircles?: DelegationCircle[], + allGroups?: DelegationGroup[], onAddGroup: (name: string) => void; removeGroup: (name: string) => void; edit: boolean; @@ -111,8 +111,8 @@ export function FolderGroups({ groups, allGroups = [], allCircles = [], onAddGro } interface CircleGroupSelectProps { - allGroups: Group[]; - allCircles: Circle[]; + allGroups: DelegationGroup[]; + allCircles: DelegationCircle[]; onChange: (name: string) => void; } diff --git a/src/settings/SubAdminGroupSelect.tsx b/src/settings/SubAdminGroupSelect.tsx index 251705bc8..52643192a 100644 --- a/src/settings/SubAdminGroupSelect.tsx +++ b/src/settings/SubAdminGroupSelect.tsx @@ -6,13 +6,14 @@ import * as React from 'react' import Select from 'react-select' import { getCurrentUser } from '@nextcloud/auth' import { Component } from 'react' -import { Group, Api } from './Api' +import { Api } from './Api' +import type { DelegationGroup } from '../types' import { CLASS_NAME_SUBADMIN_DELEGATION } from '../Constants.js' interface SubAdminGroupSelectProps { - groups: Group[], - allGroups: Group[], - delegatedSubAdminGroups: Group[], + groups: DelegationGroup[], + allGroups: DelegationGroup[], + delegatedSubAdminGroups: DelegationGroup[], } class SubAdminGroupSelect extends Component { diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 000000000..945269912 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,14 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { components } from './openapi/openapi.ts' + +export type DelegationGroup = components['schemas']['DelegationGroup']; +export type DelegationCircle = components['schemas']['DelegationCircle']; +export type Folder = components['schemas']['Folder']; +export type Group = components['schemas']['Group']; +export type User = components['schemas']['User']; +export type AclManage = components['schemas']['AclManage']; +export type Applicable = components['schemas']['Applicable']; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts new file mode 100644 index 000000000..1fff7ad55 --- /dev/null +++ b/src/types/openapi/openapi.ts @@ -0,0 +1,928 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export type paths = { + "/index.php/apps/groupfolders/delegation/groups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Returns the list of all groups */ + get: operations["delegation-get-all-groups"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/delegation/circles": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Returns the list of all visible circles */ + get: operations["delegation-get-all-circles"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/delegation/authorized-groups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get the list Groups related to classname. */ + get: operations["delegation-get-authorized-groups"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Gets all Groupfolders */ + get: operations["folder-get-folders"]; + put?: never; + /** + * Add a new Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-add-folder"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Gets a Groupfolder by ID */ + get: operations["folder-get-folder"]; + /** + * Set the mount point of a Groupfolder + * @description This endpoint requires password confirmation + */ + put: operations["folder-set-mount-point"]; + post?: never; + /** + * Remove a Groupfolder + * @description This endpoint requires password confirmation + */ + delete: operations["folder-remove-folder"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/groups": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Add access of a group for a Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-add-group"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/groups/{group}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Set the permissions of a group for a Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-set-permissions"]; + /** + * Remove access of a group from a Groupfolder + * @description This endpoint requires password confirmation + */ + delete: operations["folder-remove-group"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/manageACL": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Updates an ACL mapping + * @description This endpoint requires password confirmation + */ + post: operations["folder-set-manageacl"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/quota": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Set a new quota for a Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-set-quota"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/acl": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Toggle the ACL for a Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-setacl"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/mountpoint": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Rename a Groupfolder + * @description This endpoint requires password confirmation + */ + post: operations["folder-rename-folder"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/index.php/apps/groupfolders/folders/{id}/search": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Searches for matching ACL mappings */ + get: operations["folder-acl-mapping-search"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +}; +export type webhooks = Record; +export type components = { + schemas: { + AclManage: { + displayname: string; + id: string; + /** @enum {string} */ + type: "user" | "group"; + }; + Applicable: { + displayName: string; + /** Format: int64 */ + permissions: number; + /** @enum {string} */ + type: "group" | "circle"; + }; + Capabilities: { + groupfolders?: { + appVersion: string; + hasGroupFolders: boolean; + }; + }; + DelegationCircle: { + singleId: string; + displayName: string; + }; + DelegationGroup: { + gid: string; + displayName: string; + }; + Folder: { + /** Format: int64 */ + id: number; + mount_point: string; + group_details: { + [key: string]: components["schemas"]["Applicable"]; + }; + groups: { + [key: string]: number; + }; + /** Format: int64 */ + quota: number; + /** Format: int64 */ + size: number; + acl: boolean; + manage: components["schemas"]["AclManage"][]; + }; + Group: { + gid: string; + displayname: string; + }; + User: { + uid: string; + displayname: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +}; +export type $defs = Record; +export interface operations { + "delegation-get-all-groups": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description All groups returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DelegationGroup"][]; + }; + }; + }; + }; + "delegation-get-all-circles": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description All circles returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DelegationCircle"][]; + }; + }; + }; + }; + "delegation-get-authorized-groups": { + parameters: { + query?: { + /** @description If the classname is - OCA\GroupFolders\Settings\Admin : It's reference to fields in Admin Privileges. - OCA\GroupFolders\Controller\DelegationController : It's just to specific the subadmins. They can only manage groupfolders in which they are added in the Advanced Permissions (groups only) */ + classname?: string; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Authorized groups returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DelegationGroup"][]; + }; + }; + }; + }; + "folder-get-folders": { + parameters: { + query?: { + /** @description Filter by applicable groups */ + applicable?: 0 | 1; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Groupfolders returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Folder"][]; + }; + }; + /** @description Storage not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-add-folder": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Mountpoint of the new Groupfolder */ + mountpoint: string; + }; + }; + }; + responses: { + /** @description Groupfolder added successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Folder"]; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-get-folder": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Groupfolder returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Folder"]; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-set-mount-point": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description New mount point path */ + mountPoint: string; + }; + }; + }; + responses: { + /** @description Mount point changed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + }; + }; + "folder-remove-folder": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Groupfolder removed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-add-group": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Group to add access for */ + group: string; + }; + }; + }; + responses: { + /** @description Group access added successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-set-permissions": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + /** @description Group for which the permissions will be set */ + group: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * Format: int64 + * @description New permissions + */ + permissions: number; + }; + }; + }; + responses: { + /** @description Permissions updated successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-remove-group": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + /** @description Group to remove access from */ + group: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Group access removed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-set-manageacl": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Type of the ACL mapping */ + mappingType: string; + /** @description ID of the ACL mapping */ + mappingId: string; + /** @description Whether to enable or disable the ACL mapping */ + manageAcl: boolean; + }; + }; + }; + responses: { + /** @description ACL mapping updated successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-set-quota": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * Format: int64 + * @description New quota in bytes + */ + quota: number; + }; + }; + }; + responses: { + /** @description New quota set successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-setacl": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description Whether ACL should be enabled or not */ + acl: boolean; + }; + }; + }; + responses: { + /** @description ACL toggled successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-rename-folder": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description New Mountpoint of the Groupfolder */ + mountpoint: string; + }; + }; + }; + responses: { + /** @description Groupfolder renamed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + success: true; + }; + }; + }; + /** @description Groupfolder not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + "folder-acl-mapping-search": { + parameters: { + query?: { + /** @description String to search by */ + search?: string; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + /** @description The ID of the Groupfolder */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description ACL Mappings returned */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + users: components["schemas"]["User"][]; + groups: components["schemas"]["Group"][]; + }; + }; + }; + /** @description Not allowed to search */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; +} diff --git a/vendor-bin/openapi-extractor/composer.json b/vendor-bin/openapi-extractor/composer.json new file mode 100644 index 000000000..71320cbc6 --- /dev/null +++ b/vendor-bin/openapi-extractor/composer.json @@ -0,0 +1,5 @@ +{ + "require-dev": { + "nextcloud/openapi-extractor": "^1.0" + } +} diff --git a/vendor-bin/openapi-extractor/composer.lock b/vendor-bin/openapi-extractor/composer.lock new file mode 100644 index 000000000..ccc036a82 --- /dev/null +++ b/vendor-bin/openapi-extractor/composer.lock @@ -0,0 +1,240 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6964fdd288b96adcca764f7b24d4d678", + "packages": [], + "packages-dev": [ + { + "name": "adhocore/cli", + "version": "v1.7.2", + "source": { + "type": "git", + "url": "https://github.com/adhocore/php-cli.git", + "reference": "57834cbaa4fb68cda849417ab86577fba2b15298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/adhocore/php-cli/zipball/57834cbaa4fb68cda849417ab86577fba2b15298", + "reference": "57834cbaa4fb68cda849417ab86577fba2b15298", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ahc\\Cli\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jitendra Adhikari", + "email": "jiten.adhikary@gmail.com" + } + ], + "description": "Command line interface library for PHP", + "keywords": [ + "argument-parser", + "argv-parser", + "cli", + "cli-action", + "cli-app", + "cli-color", + "cli-option", + "cli-writer", + "command", + "console", + "console-app", + "php-cli", + "php8", + "stream-input", + "stream-output" + ], + "support": { + "issues": "https://github.com/adhocore/php-cli/issues", + "source": "https://github.com/adhocore/php-cli/tree/v1.7.2" + }, + "funding": [ + { + "url": "https://paypal.me/ji10", + "type": "custom" + }, + { + "url": "https://github.com/adhocore", + "type": "github" + } + ], + "time": "2024-09-05T00:08:47+00:00" + }, + { + "name": "nextcloud/openapi-extractor", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-releases/openapi-extractor.git", + "reference": "d7c135e819d2297e7e9414a3f80464d68f164c94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-releases/openapi-extractor/zipball/d7c135e819d2297e7e9414a3f80464d68f164c94", + "reference": "d7c135e819d2297e7e9414a3f80464d68f164c94", + "shasum": "" + }, + "require": { + "adhocore/cli": "^1.7", + "ext-simplexml": "*", + "nikic/php-parser": "^5.0", + "php": "^8.1", + "phpstan/phpdoc-parser": "^1.28" + }, + "require-dev": { + "nextcloud/coding-standard": "^1.2", + "nextcloud/ocp": "dev-master" + }, + "bin": [ + "generate-spec", + "merge-specs" + ], + "type": "library", + "autoload": { + "psr-4": { + "OpenAPIExtractor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "description": "A tool for extracting OpenAPI specifications from Nextcloud source code", + "support": { + "issues": "https://github.com/nextcloud-releases/openapi-extractor/issues", + "source": "https://github.com/nextcloud-releases/openapi-extractor/tree/v1.1.0" + }, + "time": "2024-11-06T11:50:20+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.33.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" + }, + "time": "2024-10-13T11:25:22+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +}