Skip to content

Commit f9b6f9a

Browse files
authored
Merge pull request #123 from LibreCodeCoop/feat/implement-openapi
feat: implement openapi
2 parents 36d523f + 36da940 commit f9b6f9a

File tree

15 files changed

+1372
-1207
lines changed

15 files changed

+1372
-1207
lines changed

.github/workflows/openapi.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# This workflow is provided via the organization template repository
2+
#
3+
# https://github.com/nextcloud/.github
4+
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
5+
#
6+
# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
# SPDX-FileCopyrightText: 2024 Arthur Schiwon <blizzz@arthur-schiwon.de>
8+
# SPDX-License-Identifier: MIT
9+
10+
name: OpenAPI
11+
12+
on: pull_request
13+
14+
permissions:
15+
contents: read
16+
17+
concurrency:
18+
group: openapi-${{ github.head_ref || github.run_id }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
openapi:
23+
runs-on: ubuntu-latest
24+
25+
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
26+
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
30+
with:
31+
persist-credentials: false
32+
33+
- name: Get php version
34+
id: php_versions
35+
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
36+
37+
- name: Set up php
38+
uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
39+
with:
40+
php-version: ${{ steps.php_versions.outputs.php-available }}
41+
extensions: xml
42+
coverage: none
43+
ini-file: development
44+
env:
45+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
47+
- name: Check Typescript OpenApi types
48+
id: check_typescript_openapi
49+
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0
50+
with:
51+
files: "src/types/openapi/openapi*.ts"
52+
53+
- name: Read package.json node and npm engines version
54+
if: steps.check_typescript_openapi.outputs.files_exists == 'true'
55+
uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3
56+
id: node_versions
57+
# Continue if no package.json
58+
continue-on-error: true
59+
with:
60+
fallbackNode: '^20'
61+
fallbackNpm: '^10'
62+
63+
- name: Set up node ${{ steps.node_versions.outputs.nodeVersion }}
64+
if: ${{ steps.node_versions.outputs.nodeVersion }}
65+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
66+
with:
67+
node-version: ${{ steps.node_versions.outputs.nodeVersion }}
68+
69+
- name: Set up npm ${{ steps.node_versions.outputs.npmVersion }}
70+
if: ${{ steps.node_versions.outputs.nodeVersion }}
71+
run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}'
72+
73+
- name: Install dependencies
74+
if: ${{ steps.node_versions.outputs.nodeVersion }}
75+
env:
76+
CYPRESS_INSTALL_BINARY: 0
77+
PUPPETEER_SKIP_DOWNLOAD: true
78+
run: |
79+
npm ci
80+
81+
- name: Set up dependencies
82+
run: composer i
83+
84+
- name: Regenerate OpenAPI
85+
run: composer run openapi
86+
87+
- name: Check openapi*.json and typescript changes
88+
run: |
89+
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)"
90+
91+
- name: Show changes on failure
92+
if: failure()
93+
run: |
94+
git status
95+
git --no-pager diff
96+
exit 1 # make it red to grab attention

appinfo/routes.php

Lines changed: 0 additions & 19 deletions
This file was deleted.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -not -path './tests/integration/vendor/*' -print0 | xargs -0 -n1 php -l",
2929
"cs:check": "php-cs-fixer fix --dry-run --diff",
3030
"cs:fix": "php-cs-fixer fix",
31+
"openapi": "generate-spec --verbose && (npm run typescript:generate || echo 'Please manually regenerate the typescript OpenAPI models')",
3132
"psalm": "psalm --no-cache --threads=$(nproc)",
3233
"psalm:update-baseline": "psalm --threads=$(nproc) --update-baseline --set-baseline=tests/psalm-baseline.xml",
3334
"post-install-cmd": [

lib/Capabilities.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Extract;
10+
11+
use OCP\App\IAppManager;
12+
use OCP\Capabilities\IPublicCapability;
13+
use Override;
14+
15+
/**
16+
* @psalm-import-type ExtractCapabilities from ResponseDefinitions
17+
*/
18+
class Capabilities implements IPublicCapability {
19+
public const FEATURES = [
20+
'extract-zip',
21+
'extract-rar',
22+
'extract-tar',
23+
'extract-tar-gz',
24+
'extract-tar-bz2',
25+
'extract-tar-xz',
26+
'extract-7z',
27+
];
28+
29+
public function __construct(
30+
protected IAppManager $appManager,
31+
) {
32+
}
33+
34+
/**
35+
* @return array{
36+
* extract?: ExtractCapabilities,
37+
* }
38+
*/
39+
#[Override]
40+
public function getCapabilities(): array {
41+
$capabilities = [
42+
'features' => self::FEATURES,
43+
'config' => [
44+
],
45+
'version' => $this->appManager->getAppVersion('extract'),
46+
];
47+
48+
return [
49+
'extract' => $capabilities,
50+
];
51+
}
52+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Extract\Controller;
10+
11+
use OCP\AppFramework\OCSController;
12+
13+
abstract class AEnvironmentAwareController extends OCSController {
14+
protected int $apiVersion = 1;
15+
16+
public function setAPIVersion(int $apiVersion): void {
17+
$this->apiVersion = $apiVersion;
18+
}
19+
20+
public function getAPIVersion(): int {
21+
return $this->apiVersion;
22+
}
23+
}

lib/Controller/ExtractionController.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
// Only in order to access Filesystem::isFileBlacklisted().
66
use OC\Files\Filesystem;
7+
use OCA\Extract\ResponseDefinitions;
78
use OCA\Extract\Service\ExtractionService;
89

9-
use OCP\AppFramework\Controller;
1010
use OCP\AppFramework\Http;
11+
use OCP\AppFramework\Http\Attribute\ApiRoute;
12+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
1113
use OCP\AppFramework\Http\DataResponse;
1214
use OCP\Encryption\IManager;
1315
use OCP\Files\Folder;
@@ -22,7 +24,10 @@
2224
use OCP\IURLGenerator;
2325
use Psr\Log\LoggerInterface;
2426

25-
final class ExtractionController extends Controller {
27+
/**
28+
* @psalm-import-type ExtractFolder from ResponseDefinitions
29+
*/
30+
final class ExtractionController extends AEnvironmentAwareController {
2631

2732
/** @var IL10N */
2833
private $l;
@@ -123,11 +128,25 @@ private function postExtract(string $fileName, string $directory, string $extrac
123128
}
124129

125130
/**
126-
* The only AJAX callback. This is a hook for ordinary cloud-users, os no admin required.
131+
* The only AJAX callback. This is a hook for ordinary cloud-users, os no admin required
127132
*
128-
* @NoAdminRequired
133+
* @param string $nameOfFile Name of the file to be extracted
134+
* @param string $directory Directory where the file is located
135+
* @param bool $external Is the file located on an external storage?
136+
* @param string $mime MIME type of the file
137+
* @return DataResponse<Http::STATUS_OK, array{code?: 0|1, desc?: string, extracted?: ExtractFolder}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
138+
*
139+
* 200: OK
140+
* 404: Not found or invalid path
129141
*/
130-
public function extract(string $nameOfFile, string $directory, bool $external, string $mime) {
142+
#[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/extraction/execute', requirements: ['apiVersion' => '(v1)'])]
143+
#[NoAdminRequired]
144+
public function execute(
145+
string $nameOfFile,
146+
string $directory,
147+
bool $external,
148+
string $mime,
149+
): DataResponse {
131150
$type = $this->mimeTypes[$mime];
132151

133152
if ($this->encryptionManager->isEnabled()) {

lib/ResponseDefinitions.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Extract;
10+
11+
/**
12+
* @psalm-type ExtractFolder = array{
13+
* fileId: int,
14+
* source: string,
15+
* root: string,
16+
* owner: ?string,
17+
* permissions: int,
18+
* mtime: int,
19+
* mount-type: string,
20+
* owner-display-name: ?string,
21+
* }
22+
* @psalm-type ExtractCapabilities = array{
23+
* features: list<string>,
24+
* config: array{
25+
* },
26+
* version: string,
27+
* }
28+
*/
29+
class ResponseDefinitions {
30+
}

lib/Service/ExtractionService.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function __construct(
4646
/**
4747
* @return (bool|int|mixed)[]
4848
*
49-
* @psalm-return array{code: 0|bool, desc?: mixed}
49+
* @psalm-return array{code: 0|1, desc?: string}
5050
*/
5151
public function extractZip(string $file, string $extractTo): array {
5252
$response = [];
@@ -65,14 +65,14 @@ public function extractZip(string $file, string $extractTo): array {
6565

6666
$success = $zip->extractTo($extractTo);
6767
$zip->close();
68-
$response = array_merge($response, ['code' => $success]);
68+
$response = array_merge($response, ['code' => $success ? 1 : 0]);
6969
return $response;
7070
}
7171

7272
/**
7373
* @return (int|mixed)[]
7474
*
75-
* @psalm-return array{code: 0|1, desc?: mixed}
75+
* @psalm-return array{code: 0|1, desc?: string}
7676
*/
7777
public function extractRar(string $file, string $extractTo): array {
7878
$response = [];
@@ -100,7 +100,7 @@ public function extractRar(string $file, string $extractTo): array {
100100
/**
101101
* @return (int|mixed)[]
102102
*
103-
* @psalm-return array{code: 0|1, desc?: mixed}
103+
* @psalm-return array{code: 0|1, desc?: string}
104104
*/
105105
public function extractOther(string $file, string $extractTo): array {
106106
$response = [];

0 commit comments

Comments
 (0)