From 42d9f822043e2d83605cc25f56a6fb327aa77b0c 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 | 12 + composer.json | 3 +- lib/AppInfo/Capabilities.php | 10 +- lib/Command/FolderCommand.php | 4 +- lib/Controller/DelegationController.php | 24 +- lib/Controller/FolderController.php | 139 +- lib/DAV/GroupFoldersHome.php | 10 +- lib/Folder/FolderManager.php | 156 +- lib/Mount/MountProvider.php | 5 +- lib/ResponseDefinitions.php | 55 + lib/Service/FoldersFilter.php | 14 +- lib/Trash/TrashBackend.php | 7 +- lib/Versions/GroupVersionsExpireManager.php | 5 +- lib/Versions/VersionsBackend.php | 8 +- openapi.json | 1415 +++++++++++++++++++ package-lock.json | 289 +++- package.json | 4 +- src/settings/AdminGroupSelect.tsx | 9 +- src/settings/Api.ts | 57 +- src/settings/App.tsx | 14 +- 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, 3360 insertions(+), 221 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 98565f1ca..c2c4ae9e3 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -35,6 +35,12 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2019 Nextcloud GmbH and Nextcloud contributors" SPDX-License-Identifier = "AGPL-3.0-or-later" +[[annotations]] +path = ["vendor-bin/**"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" + [[annotations]] path = ["cypress/tsconfig.json", "cypress/fixtures/example.json", "screenshots/acl.png", "screenshots/aclAdmin.png", "screenshots/edit.png", "screenshots/folders.png", "screenshots/permissions.png"] precedence = "aggregate" @@ -46,3 +52,9 @@ path = ["img/app-dark.svg", "img/app.svg", "img/deny.svg", "img/folder-group.svg precedence = "aggregate" SPDX-FileCopyrightText = "2018-2024 Google LLC" SPDX-License-Identifier = "Apache-2.0" + +[[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 a12c2fb9e..f4d00ad7b 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ "psalm:update-baseline": "psalm.phar --threads=1 --update-baseline", "psalm:clear": "psalm.phar --clear-cache && psalm.phar --clear-global-cache", "psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType", - "test:unit": "vendor/bin/phpunit -c tests/phpunit.xml" + "test:unit": "vendor/bin/phpunit -c tests/phpunit.xml", + "openapi": "generate-spec && npm run typescript:generate" }, "config": { "allow-plugins": { diff --git a/lib/AppInfo/Capabilities.php b/lib/AppInfo/Capabilities.php index 30f990c84..a7e2200ad 100644 --- a/lib/AppInfo/Capabilities.php +++ b/lib/AppInfo/Capabilities.php @@ -8,10 +8,10 @@ namespace OCA\GroupFolders\AppInfo; use OCA\GroupFolders\Folder\FolderManager; +use OCP\App\IAppManager; use OCP\Capabilities\ICapability; use OCP\IUser; use OCP\IUserSession; -use OCP\App\IAppManager; class Capabilities implements ICapability { private IUserSession $userSession; @@ -24,6 +24,14 @@ public function __construct(IUserSession $userSession, FolderManager $folderMana $this->appManager = $appManager; } + /** + * @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 c4ac76b60..e12bf3b07 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 { protected FolderManager $folderManager; @@ -31,7 +33,7 @@ public function __construct(FolderManager $folderManager, IRootFolder $rootFolde } /** - * @psalm-return array{id: mixed, mount_point: string, groups: array|mixed, quota: int, size: int|mixed, acl: bool}|false + * @return InternalFolderOut|false */ protected function getFolder(InputInterface $input, OutputInterface $output) { $folderId = (int)$input->getArgument('folder_id'); diff --git a/lib/Controller/DelegationController.php b/lib/Controller/DelegationController.php index f245f93fd..d8e01de29 100644 --- a/lib/Controller/DelegationController.php +++ b/lib/Controller/DelegationController.php @@ -8,9 +8,12 @@ namespace OCA\GroupFolders\Controller; 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\DataResponse; use OCP\AppFramework\OCSController; use OCP\IConfig; @@ -20,6 +23,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 +46,9 @@ public function __construct( * * @NoAdminRequired * @RequireGroupFolderAdmin + * @return DataResponse, array{}> + * + * 200: All groups returned */ public function getAllGroups(): DataResponse { // Get all groups @@ -61,6 +71,9 @@ public function getAllGroups(): DataResponse { * * @NoAdminRequired * @RequireGroupFolderAdmin + * @return DataResponse, array{}> + * + * 200: All circles returned */ public function getAllCircles(): DataResponse { $circlesEnabled = $this->appManager->isEnabledForUser('circles'); @@ -94,12 +107,15 @@ 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) * @NoAdminRequired * @RequireGroupFolderAdmin + * @return DataResponse, array{}> + * + * 200: Authorized groups returned */ public function getAuthorizedGroups(string $classname = ""): DataResponse { $data = []; diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index f3dcaca46..2182a0bea 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -10,11 +10,13 @@ use OC\AppFramework\OCS\V1Response; 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; use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\Files\IRootFolder; @@ -23,6 +25,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 FolderManager $manager; private MountProvider $mountProvider; @@ -60,8 +68,8 @@ public function __construct( /** * Regular users can access their own folders, but they only get to see the permission for their own groups * - * @param array $folder - * @return array|null + * @param GroupFoldersFolder $folder + * @return null|GroupFoldersFolder */ private function filterNonAdminFolder(array $folder): ?array { $userGroups = $this->groupManager->getUserGroupIds($this->user); @@ -76,8 +84,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 @@ -89,11 +97,22 @@ private function formatFolder(array $folder): array { } /** + * Gets all Groupfolders * @NoAdminRequired + * @param bool $applicable Filter by applicable groups + * @return DataResponse, array{}> + * @throws OCSNotFoundException Storage not found + * + * 200: Groupfolders returned */ public function getFolders(bool $applicable = false): DataResponse { - $folders = $this->manager->getAllFoldersWithSize($this->getRootFolderStorageId()); - $folders = array_map([$this, 'formatFolder'], $folders); + $storageId = $this->getRootFolderStorageId(); + if ($storageId === null) { + throw new OCSNotFoundException(); + } + + $folders = array_values($this->manager->getAllFoldersWithSize($storageId)); + $folders = array_map($this->formatFolder(...), $folders); $isAdmin = $this->delegationService->isAdminNextcloud() || $this->delegationService->isDelegatedAdmin(); if ($isAdmin && !$applicable) { return new DataResponse($folders); @@ -102,32 +121,40 @@ public function getFolders(bool $applicable = false): DataResponse { $folders = $this->foldersFilter->getForApiUser($folders); } 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 * @NoAdminRequired + * @param int $id ID of the Groupfolder + * @return DataResponse|DataResponse, array{}> + * @throws OCSNotFoundException Groupfolder not found + * + * 200: Groupfolder returned */ public function getFolder(int $id): DataResponse { - $response = $this->checkFolderExists($id); - if ($response) { - return $response; - } - $storageId = $this->getRootFolderStorageId(); $folder = $this->manager->getFolder($id, $storageId); + if ($folder === null) { + throw new OCSNotFoundException(); + } + $folder = $this->formatFolder($folder); + if (!$this->delegationService->hasApiAccess()) { $folder = $this->filterNonAdminFolder($folder); if (!$folder) { return new DataResponse([], Http::STATUS_NOT_FOUND); } } - 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) { @@ -145,9 +172,14 @@ private function getRootFolderStorageId(): ?int { } /** + * Add a new Groupfolder * @RequireGroupFolderAdmin * @NoAdminRequired - * @throws OCSNotFoundException + * @param string $mountpoint Mountpoint of the new Groupfolder + * @return DataResponse + * @throws OCSNotFoundException Groupfolder not found + * + * 200: Groupfolder added successfully */ #[PasswordConfirmationRequired] public function addFolder(string $mountpoint): DataResponse { @@ -156,12 +188,19 @@ public function addFolder(string $mountpoint): DataResponse { if ($folder === false) { throw new OCSNotFoundException(); } - return new DataResponse($folder); + return new DataResponse($this->formatFolder($folder)); } /** + * Remove a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function removeFolder(int $id): DataResponse { @@ -176,8 +215,14 @@ public function removeFolder(int $id): DataResponse { } /** + * Set the mount point of a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @param int $id ID of the Groupfolder + * @param string $mountPoint New mount point path + * @return DataResponse + * + * 200: Mount point changed successfully */ #[PasswordConfirmationRequired] public function setMountPoint(int $id, string $mountPoint): DataResponse { @@ -186,8 +231,15 @@ public function setMountPoint(int $id, string $mountPoint): DataResponse { } /** + * Add access of a group for a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function addGroup(int $id, string $group): DataResponse { @@ -200,8 +252,15 @@ public function addGroup(int $id, string $group): DataResponse { } /** + * Remove access of a group from a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function removeGroup(int $id, string $group): DataResponse { @@ -214,8 +273,16 @@ public function removeGroup(int $id, string $group): DataResponse { } /** + * Set the permissions of a group for a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function setPermissions(int $id, string $group, int $permissions): DataResponse { @@ -228,9 +295,17 @@ public function setPermissions(int $id, string $group, int $permissions): DataRe } /** + * Updates an ACL mapping * @NoAdminRequired * @RequireGroupFolderAdmin - * @throws \OCP\DB\Exception + * @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] public function setManageACL(int $id, string $mappingType, string $mappingId, bool $manageAcl): DataResponse { @@ -243,8 +318,15 @@ public function setManageACL(int $id, string $mappingType, string $mappingId, bo } /** + * Set a new quota for a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function setQuota(int $id, int $quota): DataResponse { @@ -257,8 +339,15 @@ public function setQuota(int $id, int $quota): DataResponse { } /** + * Toggle the ACL for a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function setACL(int $id, bool $acl): DataResponse { @@ -271,8 +360,15 @@ public function setACL(int $id, bool $acl): DataResponse { } /** + * Rename a Groupfolder * @NoAdminRequired * @RequireGroupFolderAdmin + * @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] public function renameFolder(int $id, string $mountpoint): DataResponse { @@ -322,9 +418,16 @@ private function folderDataForXML(array $data): array { } /** + * Searches for matching ACL mappings * @NoAdminRequired + * @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 */ - 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 5ccbb3f8f..0eac860d4 100644 --- a/lib/DAV/GroupFoldersHome.php +++ b/lib/DAV/GroupFoldersHome.php @@ -10,13 +10,15 @@ use OC\Files\Filesystem; use OCA\GroupFolders\Folder\FolderManager; -use OCP\Files\Cache\ICacheEntry; use OCP\Files\IRootFolder; use OCP\IUser; use Sabre\DAV\Exception\Forbidden; 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, @@ -57,8 +59,7 @@ public function createDirectory($name) { } /** - * @param string $name - * @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 { $folders = $this->folderManager->getFoldersForUser($this->user, $this->rootFolder->getMountPoint()->getNumericStorageId()); @@ -71,8 +72,7 @@ private function getFolder(string $name): ?array { } /** - * @param array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry} $folder - * @return GroupFolderNode + * @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 a8181775c..fe7294c1b 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -9,10 +9,11 @@ use OC\Files\Cache\Cache; use OC\Files\Node\Node; use OCA\Circles\CirclesManager; -use OCA\Circles\CirclesQueryHelper; 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 +33,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: ?ICacheEntry, + * } + * @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 ENTITY_GROUP = 1; public const ENTITY_CIRCLE = 2; @@ -48,9 +77,7 @@ public function __construct( } /** - * @return (array|bool|int|mixed)[][] - * - * @psalm-return array>, id: int, mount_point: mixed, quota: int, size: 0}> + * @return array * @throws Exception */ public function getAllFolders(): array { @@ -102,9 +129,7 @@ private function joinQueryWithFileCache(IQueryBuilder $query, int $rootStorageId } /** - * @return (array|bool|int|mixed)[][] - * - * @psalm-return array, id: int, manage: array, mount_point: mixed, quota: int, size: int}> + * @return array * @throws Exception */ public function getAllFoldersWithSize(int $rootStorageId): array { @@ -139,9 +164,7 @@ public function getAllFoldersWithSize(int $rootStorageId): array { } /** - * @return (array|bool|int|mixed)[][] - * - * @psalm-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 { @@ -184,9 +207,7 @@ public function getAllFoldersForUserWithSize(int $rootStorageId, IUser $user): a } /** - * @return array[] - * - * @psalm-return array> + * @return array> * @throws Exception */ private function getAllFolderMappings(): array { @@ -212,9 +233,7 @@ private function getAllFolderMappings(): array { } /** - * @return array[] - * - * @psalm-return array> + * @return array * @throws Exception */ private function getFolderMappings(int $id): array { @@ -227,10 +246,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 = \OC::$server->get(IUserManager::class)->get($entry['mapping_id']); if ($user === null) { @@ -244,20 +264,18 @@ private function getManageAcl(array $mappings): array { } $group = \OC::$server->get(IGroupManager::class)->get($entry['mapping_id']); if ($group === null) { - return []; + return null; } return [ 'type' => 'group', 'id' => (string)$group->getGID(), 'displayname' => (string)$group->getDisplayName() ]; - }, $mappings), function (?array $element): bool { - return $element !== null; - }); + }, $mappings))); } /** - * @return array{id: mixed, mount_point: mixed, groups: array, quota: int, size: int, acl: bool}|false + * @return InternalFolderOut|false * @throws Exception */ public function getFolder(int $id, int $rootStorageId = 0) { @@ -313,9 +331,7 @@ public function getFolderByPath(string $path): int { } /** - * @return int[][] - * - * @psalm-return array>> + * @return array> * @throws Exception */ private function getAllApplicable(): array { @@ -338,54 +354,39 @@ private function getAllApplicable(): array { $applicableMap[$id] = []; } - $entry = $this->generateApplicableMapEntry($row, $queryHelper, $entityId); - $applicableMap[$id][$entityId] = $entry; - } - - return $applicableMap; - } + if (!$row['circle_id']) { + $entityId = (string)$row['group_id']; + $entry = [ + 'displayName' => $row['group_id'], + 'permissions' => (int)$row['permissions'], + 'type' => 'group' + ]; + } else { + $entityId = (string)$row['circle_id']; + try { + $circle = $queryHelper?->extractCircle($row); + } catch (CircleNotFoundException) { + $circle = null; + } - /** - * @param array $row the row from database - * @param CirclesQueryHelper|null $queryHelper - * @param string|null $entityId the type of the entity - * - * @return array{displayName: string, permissions: int, type: 'circle'|'group'} - */ - private function generateApplicableMapEntry( - array $row, - ?CirclesQueryHelper $queryHelper = null, - ?string &$entityId = null - ): array { - if (!$row['circle_id']) { - $entityId = $row['group_id']; - - return [ - 'displayName' => $row['group_id'], - 'permissions' => (int)$row['permissions'], - 'type' => 'group' - ]; - } + $entry = [ + 'displayName' => $circle?->getDisplayName() ?? $row['circle_id'], + 'permissions' => (int)$row['permissions'], + 'type' => 'circle' + ]; + } - $entityId = $row['circle_id']; - try { - $circle = $queryHelper?->extractCircle($row); - } catch (CircleNotFoundException $e) { - $circle = null; + $applicableMap[$id][$entityId] = $entry; } - $displayName = $circle?->getDisplayName() ?? $row['circle_id']; - return [ - 'displayName' => $displayName, - 'permissions' => (int)$row['permissions'], - 'type' => 'circle' - ]; + return $applicableMap; } /** * @throws Exception + * @return list */ private function getGroups(int $id): array { $groups = $this->getAllApplicable()[$id] ?? []; @@ -397,7 +398,7 @@ private function getGroups(int $id): array { 'gid' => $group->getGID(), 'displayname' => $group->getDisplayName() ]; - }, array_filter($groups)); + }, array_values(array_filter($groups))); } /** @@ -447,19 +448,21 @@ public function canManageACL(int $folderId, IUser $user): bool { /** * @throws Exception + * @return list */ public function searchGroups(int $id, string $search = ''): array { $groups = $this->getGroups($id); if ($search === '') { return $groups; } - return array_filter($groups, function ($group) use ($search) { + return array_values(array_filter($groups, function ($group) use ($search) { return (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); @@ -482,9 +485,7 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $ } /** - * @param string $groupId - * @param int $rootStorageId - * @return list + * @return list * @throws Exception */ public function getFoldersForGroup(string $groupId, int $rootStorageId = 0): array { @@ -535,8 +536,7 @@ public function getFoldersForGroup(string $groupId, int $rootStorageId = 0): arr /** * @param string[] $groupIds - * @param int $rootStorageId - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry}[] + * @return list * @throws Exception */ public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): array { @@ -588,13 +588,11 @@ public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): ar 'acl' => (bool)$folder['acl'], 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null ]; - }, $result); + }, array_values($result)); } /** - * @param string[] $groupIds - * @param int $rootStorageId - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry}[] + * @return list * @throws Exception */ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId = 0): array { @@ -651,7 +649,7 @@ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId 'acl' => (bool)$folder['acl'], 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null ]; - }, $query->executeQuery()->fetchAll()); + }, array_values($query->executeQuery()->fetchAll())); } @@ -875,9 +873,7 @@ public function setFolderACL(int $folderId, bool $acl): void { } /** - * @param IUser $user - * @param int $rootStorageId - * @return array{folder_id: int, mount_point: string, permissions: int, quota: int, acl: bool, rootCacheEntry: ?ICacheEntry}[] + * @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 16a406140..b1bddcc57 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -31,6 +31,9 @@ use OCP\IUser; use OCP\IUserSession; +/** + * @psalm-import-type InternalFolder from FolderManager + */ class MountProvider implements IMountProvider { /** @var IGroupManager */ private $groupProvider; @@ -102,7 +105,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 dfcf50a78..99df216c3 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 { private IUserSession $userSession; private IGroupManager $groupManager; @@ -20,12 +24,12 @@ public function __construct(IUserSession $userSession, IGroupManager $groupManag } /** - * @param array $folders List of all folders - * @return array $folders 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(); - $folders = array_filter($folders, function (array $folder) use ($user) { + 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'])) { @@ -36,8 +40,6 @@ public function getForApiUser(array $folders): array { } } return false; - }); - - return $folders; + })); } } diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 6b908d0fe..2eb5cf2e0 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 { /** @var ?VersionsBackend */ private $versionsBackend = null; @@ -293,8 +295,7 @@ private function getTrashFolder(int $folderId): Folder { } /** - * @param IUser $user - * @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 b65747951..b7f566252 100644 --- a/lib/Versions/GroupVersionsExpireManager.php +++ b/lib/Versions/GroupVersionsExpireManager.php @@ -17,6 +17,9 @@ use OCP\Files\FileInfo; use OCP\IUser; +/** + * @psalm-import-type InternalFolderOut from FolderManager + */ class GroupVersionsExpireManager extends BasicEmitter { private $folderManager; private $expireManager; @@ -54,7 +57,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 a8004ea66..d9e498e1c 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; @@ -33,6 +34,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, @@ -242,8 +246,8 @@ 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 - * @return (FileInfo|null)[] [$fileId => FileInfo|null] + * @param InternalFolderOut $folder + * @return array */ public function getAllVersionedFiles(array $folder) { $versionsFolder = $this->getVersionsFolder($folder['id']); diff --git a/openapi.json b/openapi.json new file mode 100644 index 000000000..ab42e83b4 --- /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/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": {} + } + } + } + } + } + }, + "/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" + } + } + } + } + } + } + } + } + }, + "tags": [] +} diff --git a/package-lock.json b/package-lock.json index ff2bec767..73ffe90d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,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", @@ -4724,6 +4725,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.14.1", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.14.1.tgz", @@ -7727,6 +7896,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", @@ -13419,6 +13595,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", @@ -16106,6 +16295,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", @@ -16508,6 +16707,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", @@ -18446,6 +18652,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", @@ -20083,7 +20354,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" } @@ -23283,6 +23553,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", @@ -24488,6 +24765,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", @@ -24506,11 +24790,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 314cda46e..501728c1e 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", @@ -44,6 +45,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 f1ec4de91..b46790a83 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 3a4d33918..4d9a04084 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, OCSGroup, OCSUser } 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' @@ -13,7 +14,6 @@ import { SubmitInput } from './SubmitInput' import { SortArrow } from './SortArrow' import FlipMove from 'react-flip-move' import AsyncSelect from 'react-select/async' -import Thenable = JQuery.Thenable; import AdminGroupSelect from './AdminGroupSelect' import SubAdminGroupSelect from './SubAdminGroupSelect' import { loadState } from '@nextcloud/initial-state' @@ -30,11 +30,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 +368,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin void; - onSearch: (name: string) => Thenable<{ groups: OCSGroup[]; users: OCSUser[]; }>; + onSearch: (name: string) => Promise<{ groups: AclManage[]; users: AclManage[]; }>; } /** diff --git a/src/settings/FolderGroups.tsx b/src/settings/FolderGroups.tsx index d32295afc..ec782436c 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' function hasPermissions(value: number, check: number): boolean { @@ -16,8 +16,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; @@ -110,8 +110,8 @@ export function FolderGroups({groups, allGroups = [], allCircles = [], onAddGrou } 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..eab4ebb6a --- /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/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; + }; + "/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; + }; +}; +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 { + "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; + }; + }; + }; + }; + "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"][]; + }; + }; + }; + }; +} 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..df45fe707 --- /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.2.0", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-releases/openapi-extractor.git", + "reference": "b8b55ccb2407bb7a073a482293fbf0067186313c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-releases/openapi-extractor/zipball/b8b55ccb2407bb7a073a482293fbf0067186313c", + "reference": "b8b55ccb2407bb7a073a482293fbf0067186313c", + "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.2.0" + }, + "time": "2024-11-11T14:31:25+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" +}