Skip to content

Commit

Permalink
Merge pull request #39318 from nextcloud/feature/openapi/cloud_federa…
Browse files Browse the repository at this point in the history
…tion_api

cloud_federation_api: Add OpenAPI spec
  • Loading branch information
provokateurin committed Jul 13, 2023
2 parents 41540ad + 6843bcd commit b6e73c8
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 41 deletions.
Expand Up @@ -11,4 +11,5 @@
'OCA\\CloudFederationAPI\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
'OCA\\CloudFederationAPI\\Config' => $baseDir . '/../lib/Config.php',
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => $baseDir . '/../lib/Controller/RequestHandlerController.php',
'OCA\\CloudFederationAPI\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
);
Expand Up @@ -26,6 +26,7 @@ class ComposerStaticInitCloudFederationAPI
'OCA\\CloudFederationAPI\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
'OCA\\CloudFederationAPI\\Config' => __DIR__ . '/..' . '/../lib/Config.php',
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => __DIR__ . '/..' . '/../lib/Controller/RequestHandlerController.php',
'OCA\\CloudFederationAPI\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
);

public static function getInitializer(ClassLoader $loader)
Expand Down
16 changes: 16 additions & 0 deletions apps/cloud_federation_api/lib/Capabilities.php
Expand Up @@ -3,6 +3,7 @@
* @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
*
* @author Bjoern Schiessle <bjoern@schiessle.org>
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
*
Expand Down Expand Up @@ -36,6 +37,21 @@ public function __construct(IURLGenerator $urlGenerator) {

/**
* Function an app uses to return the capabilities
*
* @return array{
* ocm: array{
* enabled: bool,
* apiVersion: string,
* endPoint: string,
* resourceTypes: array{
* name: string,
* shareTypes: string[],
* protocols: array{
* webdav: string,
* },
* }[],
* },
* }
*/
public function getCapabilities() {
$url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare');
Expand Down
106 changes: 68 additions & 38 deletions apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
Expand Up @@ -5,6 +5,7 @@
* @author Bjoern Schiessle <bjoern@schiessle.org>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
*
Expand All @@ -25,6 +26,7 @@
namespace OCA\CloudFederationAPI\Controller;

use OCA\CloudFederationAPI\Config;
use OCA\CloudFederationAPI\ResponseDefinitions;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
Expand All @@ -44,11 +46,13 @@
use Psr\Log\LoggerInterface;

/**
* Class RequestHandlerController
*
* handle API between different Cloud instances
* Open-Cloud-Mesh-API
*
* @package OCA\CloudFederationAPI\Controller
*
* @psalm-import-type CloudFederationApiAddShare from ResponseDefinitions
* @psalm-import-type CloudFederationApiValidationError from ResponseDefinitions
* @psalm-import-type CloudFederationApiError from ResponseDefinitions
*/
class RequestHandlerController extends Controller {

Expand Down Expand Up @@ -100,26 +104,28 @@ public function __construct($appName,
}

/**
* add share
* Add share
*
* @NoCSRFRequired
* @PublicPage
* @BruteForceProtection(action=receiveFederatedShare)
*
* @param string $shareWith
* @param string $name resource name (e.g. document.odt)
* @param string $description share description (optional)
* @param string $providerId resource UID on the provider side
* @param string $owner provider specific UID of the user who owns the resource
* @param string $ownerDisplayName display name of the user who shared the item
* @param string $sharedBy provider specific UID of the user who shared the resource
* @param string $sharedByDisplayName display name of the user who shared the resource
* @param array $protocol (e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]])
* @param string $shareType ('group' or 'user' share)
* @param $resourceType ('file', 'calendar',...)
* @return Http\DataResponse|JSONResponse
* @param string $shareWith The user who the share will be shared with
* @param string $name The resource name (e.g. document.odt)
* @param string|null $description Share description
* @param string $providerId Resource UID on the provider side
* @param string $owner Provider specific UID of the user who owns the resource
* @param string|null $ownerDisplayName Display name of the user who shared the item
* @param string|null $sharedBy Provider specific UID of the user who shared the resource
* @param string|null $sharedByDisplayName Display name of the user who shared the resource
* @param array{name: string[], options: array<string, mixed>} $protocol e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]]
* @param string $shareType 'group' or 'user' share
* @param string $resourceType 'file', 'calendar',...
*
* Example: curl -H "Content-Type: application/json" -X POST -d '{"shareWith":"admin1@serve1","name":"welcome server2.txt","description":"desc","providerId":"2","owner":"admin2@http://localhost/server2","ownerDisplayName":"admin2 display","shareType":"user","resourceType":"file","protocol":{"name":"webdav","options":{"sharedSecret":"secret","permissions":"webdav-property"}}}' http://localhost/server/index.php/ocm/shares
* @return JSONResponse<Http::STATUS_CREATED, CloudFederationApiAddShare, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, CloudFederationApiValidationError, array{}>|JSONResponse<Http::STATUS_NOT_IMPLEMENTED, CloudFederationApiError, array{}>
* 201: The notification was successfully received. The display name of the recipient might be returned in the body
* 400: Bad request due to invalid parameters, e.g. when `shareWith` is not found or required properties are missing
* 501: Share type or the resource type is not supported
*/
public function addShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $protocol, $shareType, $resourceType) {

Expand All @@ -137,7 +143,10 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
!isset($protocol['options']['sharedSecret'])
) {
return new JSONResponse(
['message' => 'Missing arguments'],
[
'message' => 'Missing arguments',
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
}
Expand All @@ -158,7 +167,10 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $

if (!$this->userManager->userExists($shareWith)) {
$response = new JSONResponse(
['message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
[
'message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
$response->throttle();
Expand All @@ -169,7 +181,10 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
if ($shareType === 'group') {
if (!$this->groupManager->groupExists($shareWith)) {
$response = new JSONResponse(
['message' => 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
[
'message' => 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
$response->throttle();
Expand All @@ -192,20 +207,18 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
$share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
$share->setProtocol($protocol);
$provider->shareReceived($share);
} catch (ProviderDoesNotExistsException $e) {
} catch (ProviderDoesNotExistsException|ProviderCouldNotAddShareException $e) {
return new JSONResponse(
['message' => $e->getMessage()],
Http::STATUS_NOT_IMPLEMENTED
);
} catch (ProviderCouldNotAddShareException $e) {
return new JSONResponse(
['message' => $e->getMessage()],
$e->getCode()
);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
return new JSONResponse(
['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
[
'message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
}
Expand All @@ -222,19 +235,24 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
}

/**
* receive notification about existing share
* Send a notification about an existing share
*
* @NoCSRFRequired
* @PublicPage
* @BruteForceProtection(action=receiveFederatedShareNotification)
*
* @param string $notificationType (notification type, e.g. SHARE_ACCEPTED)
* @param string $resourceType (calendar, file, contact,...)
* @param string $providerId id of the share
* @param array $notification the actual payload of the notification
* @return JSONResponse
* @param string $notificationType Notification type, e.g. SHARE_ACCEPTED
* @param string $resourceType calendar, file, contact,...
* @param string|null $providerId ID of the share
* @param array<string, mixed>|null $notification The actual payload of the notification
*
* @return JSONResponse<Http::STATUS_CREATED, array<string, mixed>, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, CloudFederationApiValidationError, array{}>|JSONResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_IMPLEMENTED, CloudFederationApiError, array{}>
* 201: The notification was successfully received
* 400: Bad request due to invalid parameters, e.g. when `type` is invalid or missing
* 403: Getting resource is not allowed
* 501: The resource type is not supported
*/
public function receiveNotification($notificationType, $resourceType, $providerId, array $notification) {
public function receiveNotification($notificationType, $resourceType, $providerId, ?array $notification) {

// check if all required parameters are set
if ($notificationType === null ||
Expand All @@ -243,7 +261,10 @@ public function receiveNotification($notificationType, $resourceType, $providerI
!is_array($notification)
) {
return new JSONResponse(
['message' => 'Missing arguments'],
[
'message' => 'Missing arguments',
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
}
Expand All @@ -253,12 +274,18 @@ public function receiveNotification($notificationType, $resourceType, $providerI
$result = $provider->notificationReceived($notificationType, $providerId, $notification);
} catch (ProviderDoesNotExistsException $e) {
return new JSONResponse(
['message' => $e->getMessage()],
[
'message' => $e->getMessage(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
} catch (ShareNotFound $e) {
$response = new JSONResponse(
['message' => $e->getMessage()],
[
'message' => $e->getMessage(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
$response->throttle();
Expand All @@ -276,7 +303,10 @@ public function receiveNotification($notificationType, $resourceType, $providerI
return $response;
} catch (\Exception $e) {
return new JSONResponse(
['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
[
'message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl(),
'validationErrors' => [],
],
Http::STATUS_BAD_REQUEST
);
}
Expand Down
45 changes: 45 additions & 0 deletions apps/cloud_federation_api/lib/ResponseDefinitions.php
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);

/**
* @copyright Copyright (c) 2023 Kate Döen <kate.doeen@nextcloud.com>
*
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\CloudFederationAPI;

/**
* @psalm-type CloudFederationApiAddShare = array{
* recipientDisplayName: string,
* }
*
* @psalm-type CloudFederationApiError = array{
* message: string,
* }
*
* @psalm-type CloudFederationApiValidationError = CloudFederationApiError&array{
* validationErrors: array{
* name: string,
* message: string|null,
* }[],
* }
*/
class ResponseDefinitions {
}
6 changes: 3 additions & 3 deletions apps/cloud_federation_api/openapi.json
Expand Up @@ -351,8 +351,8 @@
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
Expand All @@ -370,7 +370,7 @@
}
},
"403": {
"description": "Getting resource not allowed",
"description": "Getting resource is not allowed",
"content": {
"application/json": {
"schema": {
Expand Down

0 comments on commit b6e73c8

Please sign in to comment.