Skip to content

Commit 33ba5a7

Browse files
authored
Merge pull request #905 from nextcloud/enh/openapi-spec
feat(openapi): add OpenAPI specification generation
2 parents b40f9a7 + 2c60c65 commit 33ba5a7

47 files changed

Lines changed: 19996 additions & 134 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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: 2021-2024 Nextcloud GmbH and Nextcloud contributors
7+
# SPDX-License-Identifier: MIT
8+
9+
name: OpenAPI Command
10+
on:
11+
issue_comment:
12+
types: [created]
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
init:
19+
runs-on: ubuntu-latest
20+
21+
# On pull requests and if the comment starts with `/openapi`
22+
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/openapi')
23+
24+
outputs:
25+
git_path: ${{ steps.git-path.outputs.path }}
26+
arg1: ${{ steps.command.outputs.arg1 }}
27+
arg2: ${{ steps.command.outputs.arg2 }}
28+
head_ref: ${{ steps.comment-branch.outputs.head_ref }}
29+
base_ref: ${{ steps.comment-branch.outputs.base_ref }}
30+
31+
steps:
32+
- name: Get repository from pull request comment
33+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
34+
id: get-repository
35+
with:
36+
github-token: ${{secrets.GITHUB_TOKEN}}
37+
script: |
38+
const pull = await github.rest.pulls.get({
39+
owner: context.repo.owner,
40+
repo: context.repo.repo,
41+
pull_number: context.issue.number
42+
});
43+
44+
const repositoryName = pull.data.head?.repo?.full_name
45+
console.log(repositoryName)
46+
return repositoryName
47+
48+
- name: Disabled on forks
49+
if: ${{ fromJSON(steps.get-repository.outputs.result) != github.repository }}
50+
run: |
51+
echo 'Can not execute /openapi on forks'
52+
exit 1
53+
54+
- name: Check actor permission
55+
uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0
56+
with:
57+
require: write
58+
59+
- name: Add reaction on start
60+
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
61+
with:
62+
token: ${{ secrets.COMMAND_BOT_PAT }}
63+
repository: ${{ github.event.repository.full_name }}
64+
comment-id: ${{ github.event.comment.id }}
65+
reactions: '+1'
66+
67+
- name: Parse command
68+
uses: skjnldsv/parse-command-comment@5c955203c52424151e6d0e58fb9de8a9f6a605a1 # v3.1
69+
id: command
70+
71+
# Init path depending on which command is run
72+
- name: Init path
73+
id: git-path
74+
run: |
75+
if ${{ startsWith(steps.command.outputs.arg1, '/') }}; then
76+
echo "path=${{steps.command.outputs.arg1}}" >> $GITHUB_OUTPUT
77+
else
78+
echo "path=${{steps.command.outputs.arg2}}" >> $GITHUB_OUTPUT
79+
fi
80+
81+
- name: Init branch
82+
uses: xt0rted/pull-request-comment-branch@e8b8daa837e8ea7331c0003c9c316a64c6d8b0b1 # v3.0.0
83+
id: comment-branch
84+
85+
- name: Add reaction on failure
86+
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
87+
if: failure()
88+
with:
89+
token: ${{ secrets.COMMAND_BOT_PAT }}
90+
repository: ${{ github.event.repository.full_name }}
91+
comment-id: ${{ github.event.comment.id }}
92+
reactions: '-1'
93+
94+
process:
95+
runs-on: ubuntu-latest
96+
needs: init
97+
98+
steps:
99+
- name: Restore cached git repository
100+
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
101+
with:
102+
path: .git
103+
key: git-repo
104+
105+
- name: Checkout ${{ needs.init.outputs.head_ref }}
106+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
107+
with:
108+
# Needed to allow force push later
109+
persist-credentials: true
110+
token: ${{ secrets.COMMAND_BOT_PAT }}
111+
fetch-depth: 0
112+
ref: ${{ needs.init.outputs.head_ref }}
113+
114+
- name: Setup git
115+
run: |
116+
git config --local user.email 'nextcloud-command@users.noreply.github.com'
117+
git config --local user.name 'nextcloud-command'
118+
119+
- name: Check Typescript OpenApi types
120+
id: check_typescript_openapi
121+
uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0
122+
with:
123+
files: "src/types/openapi/openapi*.ts"
124+
125+
- name: Read package.json node and npm engines version
126+
if: steps.check_typescript_openapi.outputs.files_exists == 'true'
127+
uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3
128+
id: node_versions
129+
# Continue if no package.json
130+
continue-on-error: true
131+
with:
132+
fallbackNode: '^24'
133+
fallbackNpm: '^11.3'
134+
135+
- name: Set up node ${{ steps.node_versions.outputs.nodeVersion }}
136+
if: ${{ steps.node_versions.outputs.nodeVersion }}
137+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
138+
with:
139+
node-version: ${{ steps.node_versions.outputs.nodeVersion }}
140+
141+
- name: Set up npm ${{ steps.node_versions.outputs.npmVersion }}
142+
if: ${{ steps.node_versions.outputs.nodeVersion }}
143+
run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}'
144+
145+
- name: Rebase to ${{ needs.init.outputs.base_ref }}
146+
if: ${{ contains(needs.init.outputs.arg1, 'rebase') }}
147+
run: |
148+
git fetch origin '${{ needs.init.outputs.base_ref }}:${{ needs.init.outputs.base_ref }}'
149+
git rebase 'origin/${{ needs.init.outputs.base_ref }}'
150+
151+
- name: Install dependencies
152+
env:
153+
CYPRESS_INSTALL_BINARY: 0
154+
PUPPETEER_SKIP_DOWNLOAD: true
155+
run: |
156+
npm ci
157+
158+
- name: Set up dependencies
159+
run: composer i
160+
161+
- name: Regenerate OpenAPI
162+
run: composer run openapi
163+
164+
- name: Commit default
165+
if: ${{ !contains(needs.init.outputs.arg1, 'fixup') && !contains(needs.init.outputs.arg1, 'amend') }}
166+
run: |
167+
git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}'
168+
git commit --signoff -m 'fix(openapi): Regenerate OpenAPI assets'
169+
170+
- name: Commit fixup
171+
if: ${{ contains(needs.init.outputs.arg1, 'fixup') }}
172+
run: |
173+
git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}'
174+
git commit --fixup=HEAD --signoff
175+
176+
- name: Commit amend
177+
if: ${{ contains(needs.init.outputs.arg1, 'amend') }}
178+
run: |
179+
git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}'
180+
git commit --amend --no-edit --signoff
181+
# Remove any [skip ci] from the amended commit
182+
git commit --amend -m "$(git log -1 --format='%B' | sed '/\[skip ci\]/d')"
183+
184+
- name: Push normally
185+
if: ${{ !contains(needs.init.outputs.arg1, 'rebase') && !contains(needs.init.outputs.arg1, 'amend') }}
186+
run: git push origin '${{ needs.init.outputs.head_ref }}'
187+
188+
- name: Force push
189+
if: ${{ contains(needs.init.outputs.arg1, 'rebase') || contains(needs.init.outputs.arg1, 'amend') }}
190+
run: git push --force-with-lease origin '${{ needs.init.outputs.head_ref }}'
191+
192+
- name: Add reaction on failure
193+
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
194+
if: failure()
195+
with:
196+
token: ${{ secrets.COMMAND_BOT_PAT }}
197+
repository: ${{ github.event.repository.full_name }}
198+
comment-id: ${{ github.event.comment.id }}
199+
reactions: '-1'

.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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
30+
with:
31+
persist-credentials: false
32+
33+
- name: Get php version
34+
id: php_versions
35+
uses: icewind1991/nextcloud-version-matrix@8a7bac6300b2f0f3100088b297995a229558ddba # v1.3.2
36+
37+
- name: Set up php
38+
uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # 2.37.1
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@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.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: '^24'
61+
fallbackNpm: '^11.3'
62+
63+
- name: Set up node ${{ steps.node_versions.outputs.nodeVersion }}
64+
if: ${{ steps.node_versions.outputs.nodeVersion }}
65+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.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

REUSE.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ precedence = "aggregate"
1717
SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors"
1818
SPDX-License-Identifier = "AGPL-3.0-or-later"
1919

20+
[[annotations]]
21+
path = ["openapi.json", "openapi-**.json"]
22+
precedence = "aggregate"
23+
SPDX-FileCopyrightText = "2024 Nextcloud GmbH and Nextcloud contributors"
24+
SPDX-License-Identifier = "AGPL-3.0-or-later"
25+
2026
# Workaround annotating the folders
2127
[[annotations]]
2228
path = ["docs/**", "js/**", ""]

appinfo/routes.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,14 @@
3434
// ExApps actions
3535
['name' => 'ExAppsPage#listCategories', 'url' => '/apps/categories', 'verb' => 'GET' , 'root' => ''],
3636
['name' => 'ExAppsPage#listApps', 'url' => '/apps/list', 'verb' => 'GET' , 'root' => ''],
37-
['name' => 'ExAppsPage#enableApp', 'url' => '/apps/enable/{appId}/{daemonId}', 'verb' => 'GET' , 'root' => ''],
37+
['name' => 'ExAppsPage#enableApp', 'url' => '/apps/enable/{appId}/{daemonId}', 'verb' => 'GET' , 'root' => '', 'postfix' => 'get'],
3838
['name' => 'ExAppsPage#enableApp', 'url' => '/apps/enable/{appId}/{daemonId}', 'verb' => 'POST' , 'root' => ''],
3939
['name' => 'ExAppsPage#getAppStatus', 'url' => '/apps/status/{appId}', 'verb' => 'GET' , 'root' => ''],
4040
['name' => 'ExAppsPage#getAppLogs', 'url' => '/apps/logs/{appId}', 'verb' => 'GET' , 'root' => ''],
4141
['name' => 'ExAppsPage#getAppDeployOptions', 'url' => '/apps/deploy-options/{appId}', 'verb' => 'GET' , 'root' => ''],
4242
['name' => 'ExAppsPage#disableApp', 'url' => '/apps/disable/{appId}', 'verb' => 'GET' , 'root' => ''],
4343
['name' => 'ExAppsPage#updateApp', 'url' => '/apps/update/{appId}', 'verb' => 'GET' , 'root' => ''],
4444
['name' => 'ExAppsPage#uninstallApp', 'url' => '/apps/uninstall/{appId}', 'verb' => 'GET' , 'root' => ''],
45-
['name' => 'ExAppsPage#viewApps', 'url' => '/apps/{category}', 'verb' => 'GET', 'defaults' => ['category' => ''] , 'root' => ''],
46-
['name' => 'ExAppsPage#viewApps', 'url' => '/apps/{category}/{id}', 'verb' => 'GET', 'defaults' => ['category' => '', 'id' => ''] , 'root' => ''],
4745
['name' => 'ExAppsPage#force', 'url' => '/apps/force', 'verb' => 'POST' , 'root' => ''],
4846

4947
// DaemonConfig actions
@@ -104,11 +102,6 @@
104102
// Notifications
105103
['name' => 'Notifications#sendNotification', 'url' => '/api/v1/notification', 'verb' => 'POST'],
106104

107-
// Events
108-
['name' => 'EventsListener#registerListener', 'url' => '/api/v1/events_listener', 'verb' => 'POST'],
109-
['name' => 'EventsListener#unregisterListener', 'url' => '/api/v1/events_listener', 'verb' => 'DELETE'],
110-
['name' => 'EventsListener#getListener', 'url' => '/api/v1/events_listener', 'verb' => 'GET'],
111-
112105
// Commands
113106
['name' => 'OccCommand#registerCommand', 'url' => '/api/v1/occ_command', 'verb' => 'POST'],
114107
['name' => 'OccCommand#unregisterCommand', 'url' => '/api/v1/occ_command', 'verb' => 'DELETE'],

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -print0 | xargs -0 -n1 php -l",
2828
"cs:check": "php-cs-fixer fix ./lib --dry-run --diff",
2929
"cs:fix": "php-cs-fixer fix ./lib",
30+
"openapi": "generate-spec --verbose",
3031
"psalm": "psalm.phar --threads=1",
3132
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
3233
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",

lib/Capabilities.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public function __construct(
2222
) {
2323
}
2424

25+
/**
26+
* @return array{app_api: array{loglevel: int, version: string}}
27+
*/
2528
public function getCapabilities(): array {
2629
$capabilities = [
2730
'loglevel' => intval($this->config->getSystemValue('loglevel', 2)),

0 commit comments

Comments
 (0)