diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml new file mode 100644 index 00000000..16a0e7fd --- /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 openapi* `\" ]] || (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 openapi* + exit 1 # make it red to grab attention diff --git a/REUSE.toml b/REUSE.toml index da978a18..401033fd 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -58,3 +58,9 @@ path = ["vendor/geoip2/**", "vendor/GeoLite2-Country.mmdb"] precedence = "aggregate" SPDX-FileCopyrightText = "geoip2 contributors" SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = ["openapi.json", "openapi-**.json"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/appinfo/routes.php b/appinfo/routes.php index f0f956c2..3a8001a2 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -5,12 +5,22 @@ */ return [ - 'ocs-resources' => [ - 'terms' => [ - 'url' => '/terms' - ], - ], 'ocs' => [ + [ + 'name' => 'Terms#index', + 'url' => '/terms', + 'verb' => 'GET', + ], + [ + 'name' => 'Terms#create', + 'url' => '/terms', + 'verb' => 'POST', + ], + [ + 'name' => 'Terms#destroy', + 'url' => '/terms/{id}', + 'verb' => 'DELETE', + ], [ 'name' => 'Terms#getAdminFormData', 'url' => '/terms/admin', diff --git a/composer.json b/composer.json index d1c2f140..1334da30 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "scripts": { "bin": "echo 'bin not installed'", "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", + "openapi": "generate-spec --verbose", "post-install-cmd": [ "@composer bin all install --ansi", "composer dump-autoload" diff --git a/lib/Controller/SigningController.php b/lib/Controller/SigningController.php index 2a01e0e8..33055b3b 100644 --- a/lib/Controller/SigningController.php +++ b/lib/Controller/SigningController.php @@ -10,6 +10,10 @@ use OCA\TermsOfService\BackgroundJobs\CreateNotifications; use OCA\TermsOfService\Db\Entities\Signatory; use OCA\TermsOfService\Db\Mapper\SignatoryMapper; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\BackgroundJob\IJobList; @@ -67,12 +71,14 @@ protected function resetAllSignaturesEvent(): SignaturesResetEvent { } /** - * @NoAdminRequired + * As a logged in user sign the terms * - * @param int $termId + * @param int $termId The terms the user signed + * @return DataResponse, array{}> * - * @return DataResponse + * 200: Signed successfully */ + #[NoAdminRequired] public function signTerms(int $termId): DataResponse { $signatory = new Signatory(); $signatory->setUserId($this->userId); @@ -95,12 +101,15 @@ public function signTerms(int $termId): DataResponse { /** - * @PublicPage + * As a guest sign the terms * - * @param int $termId - * @UseSession - * @return DataResponse + * @param int $termId The terms the user signed + * @return DataResponse, array{}> + * + * 200: Signed successfully */ + #[PublicPage] + #[UseSession] public function signTermsPublic(int $termId): DataResponse { $uuid = $this->config->getAppValue(Application::APPNAME, 'term_uuid', ''); $this->session->set('term_uuid', $uuid); @@ -110,7 +119,11 @@ public function signTermsPublic(int $termId): DataResponse { /** - * @return DataResponse + * Reset the signatories of all accounts + * + * @return DataResponse, array{}> + * + * 200: Reset successfully */ public function resetAllSignatories(): DataResponse { $this->signatoryMapper->deleteAllSignatories(); diff --git a/lib/Controller/TermsController.php b/lib/Controller/TermsController.php index 491255e8..a4878cb3 100644 --- a/lib/Controller/TermsController.php +++ b/lib/Controller/TermsController.php @@ -16,6 +16,8 @@ use OCA\TermsOfService\Db\Mapper\SignatoryMapper; use OCA\TermsOfService\Db\Mapper\TermsMapper; use OCA\TermsOfService\Exceptions\TermsNotFoundException; +use OCA\TermsOfService\ResponseDefinitions; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\OCSController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -26,6 +28,10 @@ use OCA\TermsOfService\Events\TermsCreatedEvent; use OCP\EventDispatcher\IEventDispatcher; +/** + * @psalm-import-type TermsOfServiceAdminFormData from ResponseDefinitions + * @psalm-import-type TermsOfServiceTerms from ResponseDefinitions + */ class TermsController extends OCSController { /** @var IFactory */ private $factory; @@ -73,9 +79,13 @@ public function __construct(string $appName, } /** - * @PublicPage - * @return DataResponse + * Get all available terms for the current country + * + * @return DataResponse, languages: array, hasSigned: bool}, array{}> + * + * 200: Get list successfully */ + #[PublicPage] public function index(): DataResponse { $currentCountry = $this->countryDetector->getCountry(); $countryTerms = $this->termsMapper->getTermsForCountryCode($currentCountry); @@ -86,7 +96,7 @@ public function index(): DataResponse { } $response = [ - 'terms' => $countryTerms, + 'terms' => array_map(static fn(Terms $terms): array => $terms->jsonSerialize(), $countryTerms), 'languages' => $this->languageMapper->getLanguages(), 'hasSigned' => $this->checker->currentUserHasSigned(), ]; @@ -94,22 +104,38 @@ public function index(): DataResponse { } /** - * @return DataResponse + * Get the form data for the admin interface + * + * @return DataResponse + * + * 200: Get form data successfully */ public function getAdminFormData(): DataResponse { + $forPublicShares = $this->config->getAppValue(Application::APPNAME, 'tos_on_public_shares', '0'); + if ($forPublicShares !== '0') { + $forPublicShares = '1'; + } + $forUsers = $this->config->getAppValue(Application::APPNAME, 'tos_for_users', '1'); + if ($forUsers !== '1') { + $forUsers = '0'; + } $response = [ - 'terms' => $this->termsMapper->getTerms(), + 'terms' => array_map(static fn(Terms $terms): array => $terms->jsonSerialize(), $this->termsMapper->getTerms()), 'countries' => $this->countryMapper->getCountries(), 'languages' => $this->languageMapper->getLanguages(), - 'tos_on_public_shares' => $this->config->getAppValue(Application::APPNAME, 'tos_on_public_shares', '0'), - 'tos_for_users' => $this->config->getAppValue(Application::APPNAME, 'tos_for_users', '1'), + 'tos_on_public_shares' => $forPublicShares, + 'tos_for_users' => $forUsers, ]; return new DataResponse($response); } /** - * @param int $id - * @return DataResponse + * Delete a given Term by id + * + * @param positive-int $id The terms which should be deleted + * @return DataResponse, array{}> + * + * 200: Deleted successfully */ public function destroy(int $id): DataResponse { $terms = new Terms(); @@ -120,15 +146,21 @@ public function destroy(int $id): DataResponse { return new DataResponse(); } + protected function createTermsCreatedEvent(): TermsCreatedEvent { return new TermsCreatedEvent(); } /** - * @param string $countryCode - * @param string $languageCode - * @param string $body - * @return DataResponse + * Create new terms + * + * @param string $countryCode One of the 2-letter region codes or `--` for "global" + * @param string $languageCode One of the 2-letter language codes + * @param string $body The actual terms and conditions text (can be markdown, using headers, basic text formating, lists and links) + * @return DataResponse|DataResponse, array{}> + * + * 200: Created successfully + * 417: Country or language code was not a valid option */ public function create(string $countryCode, string $languageCode, @@ -161,6 +193,6 @@ public function create(string $countryCode, $event = $this->createTermsCreatedEvent(); $this->eventDispatcher->dispatchTyped($event); - return new DataResponse($terms); + return new DataResponse($terms->jsonSerialize()); } } diff --git a/lib/Db/Entities/Terms.php b/lib/Db/Entities/Terms.php index c4cd45ff..52f93e4b 100644 --- a/lib/Db/Entities/Terms.php +++ b/lib/Db/Entities/Terms.php @@ -6,6 +6,7 @@ namespace OCA\TermsOfService\Db\Entities; +use OCA\TermsOfService\ResponseDefinitions; use OCP\AppFramework\Db\Entity; /** @@ -15,6 +16,8 @@ * @method void setLanguageCode(string $languageCode) * @method string getBody() * @method void setBody(string $body) + * + * @psalm-import-type TermsOfServiceTerms from ResponseDefinitions */ class Terms extends Entity implements \JsonSerializable { /** @var string */ diff --git a/lib/Db/Mapper/CountryMapper.php b/lib/Db/Mapper/CountryMapper.php index 1389cf1f..b91516e8 100644 --- a/lib/Db/Mapper/CountryMapper.php +++ b/lib/Db/Mapper/CountryMapper.php @@ -32,7 +32,7 @@ public function isValidCountry($countryCode): bool { /** * Gets the countries as well as the two-letter code * - * @return array + * @return array */ public function getCountries(): array { $countries = [ diff --git a/lib/Db/Mapper/LanguageMapper.php b/lib/Db/Mapper/LanguageMapper.php index b4342abe..941efe1c 100644 --- a/lib/Db/Mapper/LanguageMapper.php +++ b/lib/Db/Mapper/LanguageMapper.php @@ -26,6 +26,9 @@ public function isValidLanguage($languageCode): bool { return isset($this->getLanguages()[$languageCode]); } + /** + * @return array + */ public function getLanguages(): array { $mappedLanguages = [ 'aa' => $this->l->t('Afar'), diff --git a/lib/Db/Mapper/TermsMapper.php b/lib/Db/Mapper/TermsMapper.php index d7b7f2dd..262e1acd 100644 --- a/lib/Db/Mapper/TermsMapper.php +++ b/lib/Db/Mapper/TermsMapper.php @@ -27,7 +27,7 @@ public function __construct(IDBConnection $db) { * Returns all terms and conditions for the country code * * @param string $countryCode - * @return Terms[] + * @return list */ public function getTermsForCountryCode(string $countryCode): array { $query = $this->db->getQueryBuilder(); @@ -81,7 +81,7 @@ public function getTermsForCountryCodeAndLanguageCode(string $countryCode, strin /** * Returns all terms and conditions * - * @return Terms[] + * @return array */ public function getTerms(): array { $query = $this->db->getQueryBuilder(); @@ -91,7 +91,7 @@ public function getTerms(): array { $entities = []; $result = $query->execute(); while ($row = $result->fetch()){ - $entities[(int) $row['id']] = $this->mapRowToEntity($row); + $entities[$row['id']] = $this->mapRowToEntity($row); } $result->closeCursor(); diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php new file mode 100644 index 00000000..c048d2a7 --- /dev/null +++ b/lib/ResponseDefinitions.php @@ -0,0 +1,30 @@ +, + * countries: array, + * languages: array, + * tos_on_public_shares: '0'|'1', + * tos_for_users: '0'|'1', + * } + */ +class ResponseDefinitions { +} diff --git a/openapi-administration.json b/openapi-administration.json new file mode 100644 index 00000000..5d2d4f9d --- /dev/null +++ b/openapi-administration.json @@ -0,0 +1,439 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "terms_of_service-administration", + "version": "0.0.1", + "description": "Requires users to accept the terms of service before accessing data.", + "license": { + "name": "agpl" + } + }, + "components": { + "securitySchemes": { + "basic_auth": { + "type": "http", + "scheme": "basic" + }, + "bearer_auth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "AdminFormData": { + "type": "object", + "required": [ + "terms", + "countries", + "languages", + "tos_on_public_shares", + "tos_for_users" + ], + "properties": { + "terms": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Terms" + } + }, + "countries": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "languages": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "tos_on_public_shares": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "tos_for_users": { + "type": "string", + "enum": [ + "0", + "1" + ] + } + } + }, + "OCSMeta": { + "type": "object", + "required": [ + "status", + "statuscode" + ], + "properties": { + "status": { + "type": "string" + }, + "statuscode": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "totalitems": { + "type": "string" + }, + "itemsperpage": { + "type": "string" + } + } + }, + "Terms": { + "type": "object", + "required": [ + "id", + "countryCode", + "languageCode", + "body", + "renderedBody" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "minimum": 1 + }, + "countryCode": { + "type": "string" + }, + "languageCode": { + "type": "string" + }, + "body": { + "type": "string", + "minLength": 1 + }, + "renderedBody": { + "type": "string", + "minLength": 1 + } + } + } + } + }, + "paths": { + "/ocs/v2.php/apps/terms_of_service/terms": { + "post": { + "operationId": "terms-create", + "summary": "Create new terms", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "countryCode", + "languageCode", + "body" + ], + "properties": { + "countryCode": { + "type": "string", + "description": "One of the 2-letter region codes or `--` for \"global\"" + }, + "languageCode": { + "type": "string", + "description": "One of the 2-letter language codes" + }, + "body": { + "type": "string", + "description": "The actual terms and conditions text (can be markdown, using headers, basic text formating, lists and links)" + } + } + } + } + } + }, + "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": "Created successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/Terms" + } + } + } + } + } + } + } + }, + "417": { + "description": "Country or language code was not a valid option", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/terms/{id}": { + "delete": { + "operationId": "terms-destroy", + "summary": "Delete a given Term by id", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The terms which should be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 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": "Deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/terms/admin": { + "get": { + "operationId": "terms-get-admin-form-data", + "summary": "Get the form data for the admin interface", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "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": "Get form data successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/AdminFormData" + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/sign": { + "delete": { + "operationId": "signing-reset-all-signatories", + "summary": "Reset the signatories of all accounts", + "description": "This endpoint requires admin access", + "tags": [ + "signing" + ], + "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": "Reset successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + } + }, + "tags": [] +} diff --git a/openapi-full.json b/openapi-full.json new file mode 100644 index 00000000..49f2762b --- /dev/null +++ b/openapi-full.json @@ -0,0 +1,519 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "terms_of_service-full", + "version": "0.0.1", + "description": "Requires users to accept the terms of service before accessing data.", + "license": { + "name": "agpl" + } + }, + "components": { + "securitySchemes": { + "basic_auth": { + "type": "http", + "scheme": "basic" + }, + "bearer_auth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "AdminFormData": { + "type": "object", + "required": [ + "terms", + "countries", + "languages", + "tos_on_public_shares", + "tos_for_users" + ], + "properties": { + "terms": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Terms" + } + }, + "countries": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "languages": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "tos_on_public_shares": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "tos_for_users": { + "type": "string", + "enum": [ + "0", + "1" + ] + } + } + }, + "OCSMeta": { + "type": "object", + "required": [ + "status", + "statuscode" + ], + "properties": { + "status": { + "type": "string" + }, + "statuscode": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "totalitems": { + "type": "string" + }, + "itemsperpage": { + "type": "string" + } + } + }, + "Terms": { + "type": "object", + "required": [ + "id", + "countryCode", + "languageCode", + "body", + "renderedBody" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "minimum": 1 + }, + "countryCode": { + "type": "string" + }, + "languageCode": { + "type": "string" + }, + "body": { + "type": "string", + "minLength": 1 + }, + "renderedBody": { + "type": "string", + "minLength": 1 + } + } + } + } + }, + "paths": { + "/ocs/v2.php/apps/terms_of_service/terms": { + "post": { + "operationId": "terms-create", + "summary": "Create new terms", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "countryCode", + "languageCode", + "body" + ], + "properties": { + "countryCode": { + "type": "string", + "description": "One of the 2-letter region codes or `--` for \"global\"" + }, + "languageCode": { + "type": "string", + "description": "One of the 2-letter language codes" + }, + "body": { + "type": "string", + "description": "The actual terms and conditions text (can be markdown, using headers, basic text formating, lists and links)" + } + } + } + } + } + }, + "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": "Created successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/Terms" + } + } + } + } + } + } + } + }, + "417": { + "description": "Country or language code was not a valid option", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/sign": { + "delete": { + "operationId": "signing-reset-all-signatories", + "summary": "Reset the signatories of all accounts", + "description": "This endpoint requires admin access", + "tags": [ + "signing" + ], + "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": "Reset successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/sign_public": { + "post": { + "operationId": "signing-sign-terms-public", + "summary": "As a guest sign the terms", + "tags": [ + "signing" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "termId" + ], + "properties": { + "termId": { + "type": "integer", + "format": "int64", + "description": "The terms the user signed" + } + } + } + } + } + }, + "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": "Signed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/terms/{id}": { + "delete": { + "operationId": "terms-destroy", + "summary": "Delete a given Term by id", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The terms which should be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 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": "Deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/terms/admin": { + "get": { + "operationId": "terms-get-admin-form-data", + "summary": "Get the form data for the admin interface", + "description": "This endpoint requires admin access", + "tags": [ + "terms" + ], + "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": "Get form data successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/AdminFormData" + } + } + } + } + } + } + } + } + } + } + } + }, + "tags": [] +} diff --git a/openapi.json b/openapi.json new file mode 100644 index 00000000..49afea8f --- /dev/null +++ b/openapi.json @@ -0,0 +1,326 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "terms_of_service", + "version": "0.0.1", + "description": "Requires users to accept the terms of service before accessing data.", + "license": { + "name": "agpl" + } + }, + "components": { + "securitySchemes": { + "basic_auth": { + "type": "http", + "scheme": "basic" + }, + "bearer_auth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "OCSMeta": { + "type": "object", + "required": [ + "status", + "statuscode" + ], + "properties": { + "status": { + "type": "string" + }, + "statuscode": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "totalitems": { + "type": "string" + }, + "itemsperpage": { + "type": "string" + } + } + }, + "Terms": { + "type": "object", + "required": [ + "id", + "countryCode", + "languageCode", + "body", + "renderedBody" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "minimum": 1 + }, + "countryCode": { + "type": "string" + }, + "languageCode": { + "type": "string" + }, + "body": { + "type": "string", + "minLength": 1 + }, + "renderedBody": { + "type": "string", + "minLength": 1 + } + } + } + } + }, + "paths": { + "/ocs/v2.php/apps/terms_of_service/terms": { + "get": { + "operationId": "terms-index", + "summary": "Get all available terms for the current country", + "tags": [ + "terms" + ], + "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": "Get list successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "terms", + "languages", + "hasSigned" + ], + "properties": { + "terms": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Terms" + } + }, + "languages": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "hasSigned": { + "type": "boolean" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/sign": { + "post": { + "operationId": "signing-sign-terms", + "summary": "As a logged in user sign the terms", + "tags": [ + "signing" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "termId" + ], + "properties": { + "termId": { + "type": "integer", + "format": "int64", + "description": "The terms the user signed" + } + } + } + } + } + }, + "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": "Signed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/terms_of_service/sign_public": { + "post": { + "operationId": "signing-sign-terms-public", + "summary": "As a guest sign the terms", + "tags": [ + "signing" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "termId" + ], + "properties": { + "termId": { + "type": "integer", + "format": "int64", + "description": "The terms the user signed" + } + } + } + } + } + }, + "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": "Signed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + } + } + } + } + }, + "tags": [] +} diff --git a/vendor-bin/openapi-extractor/composer.json b/vendor-bin/openapi-extractor/composer.json new file mode 100644 index 00000000..a03f8d2d --- /dev/null +++ b/vendor-bin/openapi-extractor/composer.json @@ -0,0 +1,10 @@ +{ + "config": { + "platform": { + "php": "8.1" + } + }, + "require-dev": { + "nextcloud/openapi-extractor": "^1.0.1" + } +} diff --git a/vendor-bin/openapi-extractor/composer.lock b/vendor-bin/openapi-extractor/composer.lock new file mode 100644 index 00000000..a2a1e9d2 --- /dev/null +++ b/vendor-bin/openapi-extractor/composer.lock @@ -0,0 +1,243 @@ +{ + "_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": "19a2125f8a671edde7d81d5577839585", + "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.0.1", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-releases/openapi-extractor.git", + "reference": "654c44363e1afbc6dc5e5140f22f4ac0727cbf32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-releases/openapi-extractor/zipball/654c44363e1afbc6dc5e5140f22f4ac0727cbf32", + "reference": "654c44363e1afbc6dc5e5140f22f4ac0727cbf32", + "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.0.1" + }, + "time": "2024-10-12T04:42:57+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": {}, + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +}