Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@
['name' => 'Context#transfer', 'url' => '/api/2/contexts/{contextId}/transfer', 'verb' => 'PUT'],
['name' => 'Context#updateContentOrder', 'url' => '/api/2/contexts/{contextId}/pages/{pageId}', 'verb' => 'PUT'],

['name' => 'RowOCS#createRow', 'url' => '/api/2/{nodeCollection}/{nodeId}/rows', 'verb' => 'POST', 'requirements' => ['nodeCollection' => '(tables|views)', 'nodeId' => '(\d+)']],

['name' => 'Config#getTableConfig', 'url' => '/api/2/config/table/{id}', 'verb' => 'GET'],
['name' => 'Config#getViewConfig', 'url' => '/api/2/config/view/{id}', 'verb' => 'GET'],
['name' => 'Config#setValue', 'url' => '/api/2/config/{key}', 'verb' => 'POST'],
Expand Down
1 change: 1 addition & 0 deletions lib/Constants/FilterOperator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum FilterOperator: string {
case BEGINS_WITH = 'begins-with';
case ENDS_WITH = 'ends-with';
case CONTAINS = 'contains';
case CONTAINS_ITEM = 'contains-item';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to move that into separate PR?

case DOES_NOT_CONTAIN = 'does-not-contain';
case IS_EQUAL = 'is-equal';
case IS_NOT_EQUAL = 'is-not-equal';
Expand Down
138 changes: 124 additions & 14 deletions lib/Controller/PublicRowOCSController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@

namespace OCA\Tables\Controller;

use OCA\Tables\AppInfo\Application;
use OCA\Tables\Db\Row2Mapper;
use OCA\Tables\Db\RowQuery;
use OCA\Tables\Errors\BadRequestError;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Helper\ConversionHelper;
use OCA\Tables\Middleware\Attribute\AssertShareAccessIsAccessible;
use OCA\Tables\Model\RowDataInput;
use OCA\Tables\ResponseDefinitions;
Expand All @@ -26,6 +25,7 @@
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
Expand All @@ -52,8 +52,12 @@ public function __construct(
* [api v2] Fetch all rows from a link share
*
* @param string $token The share token
* @param int|null $limit Optional: maximum number of results, capped at 500
* @param int|null $offset Optional: the offset for this operation
* @psalm-param ?int<1,500> $limit Number of rows to return between 1 and 500, fetches all by default (optional)
* @psalm-param ?int<0,max> $offset Offset of the rows to be returned (optional)
* @param string|null $filter Optional: a JSON encoded filter parameter
* @param string|null $sort Optional: a JSON encoded sort parameter
* @param string|null $search Optional: a search string
* @param string|null $rowIds Optional: a JSON encoded list of row IDs
* @return DataResponse<Http::STATUS_OK, list<TablesPublicRow>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 200: Rows are returned
Expand All @@ -67,7 +71,7 @@ public function __construct(
#[ApiRoute(verb: 'GET', url: '/api/2/public/{token}/rows', requirements: ['token' => '[a-zA-Z0-9]{16}'])]
#[OpenAPI]
#[AnonRateLimit(limit: 20, period: 30)]
public function getRows(string $token, ?int $limit, ?int $offset): DataResponse {
public function getRows(string $token, ?int $limit, ?int $offset, ?string $filter = null, ?string $sort = null, ?string $search = null, ?string $rowIds = null): DataResponse {
try {
$shareToken = new ShareToken($token);
$share = $this->shareService->findByToken($shareToken);
Expand All @@ -76,16 +80,19 @@ public function getRows(string $token, ?int $limit, ?int $offset): DataResponse
return $this->handlePermissionError(new PermissionError('No read permission on this share'));
}

$limit = $limit !== null ? max(0, min(500, $limit)) : null;
$offset = $offset !== null ? max(0, $offset) : null;

$nodeType = ConversionHelper::stringNodeType2Const($share->getNodeType());
if ($nodeType === Application::NODE_TYPE_TABLE) {
$rows = $this->rowService->findAllByTable($share->getNodeId(), '', $limit, $offset);
} elseif ($nodeType === Application::NODE_TYPE_VIEW) {
$rows = $this->rowService->findAllByView($share->getNodeId(), '', $limit, $offset);
}
$queryData = RowQuery::buildFromInput(
nodeType: $share->getNodeType(),
nodeId: $share->getNodeId(),
limit: $limit,
offset: $offset,
filter: $filter,
sort: $sort,
search: $search,
rowIds: $rowIds,
normalizePagination: true,
);

$rows = $this->rowService->findAllByQuery($queryData);
$formattedRows = $this->rowService->formatRowsForPublicShare($rows);
return new DataResponse($formattedRows);
} catch (PermissionError $e) {
Expand All @@ -99,6 +106,108 @@ public function getRows(string $token, ?int $limit, ?int $offset): DataResponse
}
}

/**
* [api v2] Count rows from a link share
*
* @param string $token The share token
* @param string|null $filter Optional: a JSON encoded filter parameter
* @param string|null $sort Optional: a JSON encoded sort parameter
* @param string|null $search Optional: a search string
* @return DataResponse<Http::STATUS_OK, array{count: int}, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 200: Count is returned
* 400: Invalid request parameters
* 403: No permissions
* 404: Not found
* 500: Internal error
*/
#[PublicPage]
#[AssertShareAccessIsAccessible]
#[ApiRoute(verb: 'GET', url: '/api/2/public/{token}/rows/count', requirements: ['token' => '[a-zA-Z0-9]{16}'])]
#[OpenAPI]
#[AnonRateLimit(limit: 20, period: 30)]
public function countRows(string $token, ?string $filter = null, ?string $sort = null, ?string $search = null): DataResponse {
try {
$shareToken = new ShareToken($token);
$share = $this->shareService->findByToken($shareToken);

if (!$share->getPermissionRead()) {
return $this->handlePermissionError(new PermissionError('No read permission on this share'));
}

$queryData = RowQuery::buildFromInput(
nodeType: $share->getNodeType(),
nodeId: $share->getNodeId(),
filter: $filter,
sort: $sort,
search: $search,
);

$count = $this->rowService->countByQuery($queryData);
return new DataResponse(['count' => $count]);
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError $e) {
return $this->handleError($e);
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (BadRequestError $e) {
return $this->handleBadRequestError($e);
}
}

/**
* [api v2] Export all rows from a link share as a CSV file
*
* @param string $token The share token
* @param ?string $filter Optional: a JSON encoded filter parameter
* @param ?string $sort Optional: a JSON encoded sort parameter
* @param ?string $search Optional: a search string
* @param ?string $rowIds Optional: a JSON encoded list of row IDs to export
* @return DataDownloadResponse<Http::STATUS_OK, 'text/csv', array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 200: CSV file is returned
* 400: Invalid request parameters
* 403: No permissions
* 404: Not found
* 500: Internal error
*/
#[PublicPage]
#[AssertShareAccessIsAccessible]
#[ApiRoute(verb: 'GET', url: '/api/2/public/{token}/rows/export', requirements: ['token' => '[a-zA-Z0-9]{16}'])]
#[OpenAPI]
#[AnonRateLimit(limit: 20, period: 30)]
public function exportRows(string $token, ?string $filter = null, ?string $sort = null, ?string $search = null, ?string $rowIds = null): DataDownloadResponse|DataResponse {
try {
$shareToken = new ShareToken($token);
$share = $this->shareService->findByToken($shareToken);

if (!$share->getPermissionRead()) {
return $this->handlePermissionError(new PermissionError('No read permission on this share'));
}

$queryData = RowQuery::buildFromInput(
nodeType: $share->getNodeType(),
nodeId: $share->getNodeId(),
filter: $filter,
sort: $sort,
search: $search,
rowIds: $rowIds,
);

$csv = $this->rowService->exportCsv($queryData);
return new DataDownloadResponse($csv, 'export.csv', 'text/csv');
} catch (PermissionError $e) {
return $this->handlePermissionError($e);
} catch (InternalError $e) {
return $this->handleError($e);
} catch (NotFoundError $e) {
return $this->handleNotFoundError($e);
} catch (BadRequestError $e) {
return $this->handleBadRequestError($e);
}
}

/**
* [api v2] Create a row in a link share
*
Expand Down Expand Up @@ -259,4 +368,5 @@ public function deleteRow(string $token, int $rowId): DataResponse {
return $this->handleError($e);
}
}

}
Loading
Loading