appwrite/app/controllers/api/databases.php

1394 lines
60 KiB
PHP
Raw Normal View History

2019-05-09 06:54:39 +00:00
<?php
2023-06-15 05:29:03 +00:00
use Appwrite\Auth\Auth;
use Appwrite\Detector\Detector;
use Appwrite\Event\Event;
2025-01-30 04:53:53 +00:00
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
2023-06-15 05:29:03 +00:00
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
use Utopia\App;
2021-08-15 21:09:40 +00:00
use Utopia\Audit\Audit;
2023-06-15 05:29:03 +00:00
use Utopia\Config\Config;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Database;
2025-02-25 05:51:11 +00:00
use Utopia\Database\DateTime;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Document;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
2024-11-04 07:57:08 +00:00
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
2024-09-09 07:17:47 +00:00
use Utopia\Database\Exception\Query as QueryException;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Query;
2021-08-12 01:05:19 +00:00
use Utopia\Database\Validator\Authorization;
2021-07-05 20:27:20 +00:00
use Utopia\Database\Validator\Permissions;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Validator\Queries;
2024-05-09 16:54:28 +00:00
use Utopia\Database\Validator\Query\Cursor;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
2021-07-05 20:27:20 +00:00
use Utopia\Database\Validator\UID;
2022-05-25 12:10:10 +00:00
use Utopia\Locale\Locale;
2023-06-15 05:29:03 +00:00
use Utopia\Validator\ArrayList;
use Utopia\Validator\JSON;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
2021-07-27 01:00:36 +00:00
App::post('/v1/databases/:databaseId/tables/:tableId/rows')
->alias('/v1/databases/:databaseId/collections/:tableId/documents')
->desc('Create row')
2020-06-25 18:32:12 +00:00
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create')
2020-01-31 22:34:07 +00:00
->label('scope', 'documents.write')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'row.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
2025-01-17 04:31:39 +00:00
->label(
'sdk',
[
new Method(
namespace: 'databases',
group: 'rows',
name: 'createRow',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/create-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
)
]
)
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define columns before creating rows.')
->param('data', [], new JSON(), 'Row data as JSON object.')
2023-10-13 13:43:23 +00:00
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
2020-12-26 15:05:04 +00:00
->inject('response')
->inject('dbForProject')
2021-03-21 22:17:20 +00:00
->inject('user')
2022-12-20 16:11:30 +00:00
->inject('queueForEvents')
2025-01-30 04:53:53 +00:00
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $rowId, string $tableId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
2020-06-29 21:43:34 +00:00
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
2020-06-29 21:43:34 +00:00
}
2020-01-31 22:34:07 +00:00
2020-06-29 21:43:34 +00:00
if (isset($data['$id'])) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead');
2020-06-29 21:43:34 +00:00
}
2025-01-02 07:56:14 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
2022-08-08 10:58:28 +00:00
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
2023-08-16 21:58:25 +00:00
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
2022-08-08 10:58:28 +00:00
}
$table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId));
2020-01-31 22:34:07 +00:00
if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
2023-08-16 21:58:25 +00:00
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2022-08-08 10:58:28 +00:00
}
2022-08-23 01:42:25 +00:00
$allowedPermissions = [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
];
2022-08-23 01:42:25 +00:00
// Map aggregate permissions to into the set of individual permissions they represent.
$permissions = Permission::aggregate($permissions, $allowedPermissions);
// Add permissions for current the user if none were provided.
2022-08-15 12:56:19 +00:00
if (\is_null($permissions)) {
$permissions = [];
2022-08-15 13:16:20 +00:00
if (!empty($user->getId())) {
2022-08-15 12:56:19 +00:00
foreach ($allowedPermissions as $permission) {
2022-08-15 13:16:20 +00:00
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
2022-08-15 12:56:19 +00:00
}
}
}
2022-08-14 05:24:50 +00:00
2022-08-15 12:56:19 +00:00
// Users can only manage their own roles, API keys and Admin users can manage any
2023-08-16 21:58:25 +00:00
if (!$isAPIKey && !$isPrivilegedUser) {
2022-08-15 12:56:19 +00:00
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
2022-08-16 11:26:38 +00:00
$permission = Permission::parse($permission);
if ($permission->getPermission() != $type) {
2022-08-15 12:56:19 +00:00
continue;
}
2022-08-16 11:26:38 +00:00
$role = (new Role(
$permission->getRole(),
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
2022-08-15 12:56:19 +00:00
if (!Authorization::isRole($role)) {
2023-08-22 03:25:55 +00:00
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
2022-08-15 12:56:19 +00:00
}
}
}
2022-08-08 10:58:28 +00:00
}
$data['$collection'] = $table->getId(); // Adding this param to make API easier for developers
$data['$id'] = $rowId == 'unique()' ? ID::unique() : $rowId;
$data['$permissions'] = $permissions;
$row = new Document($data);
2020-01-31 22:34:07 +00:00
2025-01-06 16:51:39 +00:00
$operations = 0;
$checkPermissions = function (Document $table, Document $row, string $permission) use (&$checkPermissions, $dbForProject, $database, &$operations) {
2025-01-06 16:51:39 +00:00
$operations++;
$documentSecurity = $table->getAttribute('documentSecurity', false);
2023-03-28 09:02:49 +00:00
$validator = new Authorization($permission);
$valid = $validator->isValid($table->getPermissionsByType($permission));
2023-03-28 09:02:49 +00:00
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($permission === Database::PERMISSION_UPDATE) {
$valid = $valid || $validator->isValid($row->getUpdate());
2023-03-28 09:02:49 +00:00
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
}
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
2023-03-28 09:02:49 +00:00
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
2023-03-28 09:02:49 +00:00
if (empty($related)) {
continue;
}
2023-04-14 10:03:16 +00:00
$isList = \is_array($related) && \array_values($related) === $related;
if ($isList) {
$relations = $related;
} else {
$relations = [$related];
}
2023-03-28 09:02:49 +00:00
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
2023-03-28 09:02:49 +00:00
);
foreach ($relations as &$relation) {
if (
2023-04-21 01:44:53 +00:00
\is_array($relation)
&& \array_values($relation) !== $relation
&& !isset($relation['$id'])
) {
$relation['$id'] = ID::unique();
$relation = new Document($relation);
}
2023-03-29 01:37:56 +00:00
if ($relation instanceof Document) {
2025-01-02 07:56:14 +00:00
$current = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(), $relation->getId())
2023-03-28 09:02:49 +00:00
);
if ($current->isEmpty()) {
$type = Database::PERMISSION_CREATE;
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
} else {
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
$relation->setAttribute('$collection', $relatedTable->getId());
$type = Database::PERMISSION_UPDATE;
}
2023-03-28 09:02:49 +00:00
$checkPermissions($relatedTable, $relation, $type);
2023-03-28 09:02:49 +00:00
}
}
2023-04-14 10:03:16 +00:00
if ($isList) {
$row->setAttribute($relationship->getAttribute('key'), \array_values($relations));
} else {
$row->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
2023-03-28 09:02:49 +00:00
}
};
$checkPermissions($table, $row, Database::PERMISSION_CREATE);
2023-03-28 09:02:49 +00:00
2024-03-06 17:34:21 +00:00
try {
$row = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $row);
2024-11-04 07:57:08 +00:00
} catch (StructureException $e) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage());
} catch (DuplicateException $e) {
2024-03-06 17:34:21 +00:00
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
2024-11-04 07:57:08 +00:00
} catch (NotFoundException $e) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2024-03-06 17:34:21 +00:00
}
2020-01-31 22:34:07 +00:00
2025-01-02 08:09:44 +00:00
// Add $tableId and $databaseId for all rows
$processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) {
$row->setAttribute('$databaseId', $database->getId());
$row->setAttribute('$collectionId', $table->getId());
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
continue;
}
if (!\is_array($related)) {
$related = [$related];
}
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
);
2023-03-29 01:37:56 +00:00
foreach ($related as $relation) {
2023-04-03 10:39:31 +00:00
if ($relation instanceof Document) {
$processRow($relatedTable, $relation);
2023-04-03 10:39:31 +00:00
}
}
}
};
$processRow($table, $row);
2020-01-31 22:34:07 +00:00
$queueForStatsUsage
2025-03-18 14:20:25 +00:00
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
2025-03-30 05:54:48 +00:00
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection
2025-01-02 14:45:21 +00:00
2025-01-06 15:19:39 +00:00
$response->addHeader('X-Debug-Operations', $operations);
2024-07-11 15:03:28 +00:00
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($row, Response::MODEL_DOCUMENT);
2024-07-11 15:03:28 +00:00
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
\array_filter(
$table->getAttribute('attributes', []),
2024-07-11 15:03:28 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
)
);
2022-12-20 16:11:30 +00:00
$queueForEvents
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('rowId', $row->getId())
->setContext('table', $table)
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setContext('database', $database)
2024-07-11 15:03:28 +00:00
->setPayload($response->getPayload(), sensitive: $relationships);
2020-12-26 15:05:04 +00:00
});
2020-01-31 22:34:07 +00:00
App::get('/v1/databases/:databaseId/tables/:tableId/rows')
->alias('/v1/databases/:databaseId/collections/:tableId/documents')
->desc('List rows')
2020-06-25 18:32:12 +00:00
->groups(['api', 'database'])
->label('scope', 'documents.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: 'rows',
name: 'listRows',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/list-documents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT_LIST,
)
],
contentType: ContentType::JSON
))
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
2023-03-29 19:38:39 +00:00
->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
2020-12-26 15:05:04 +00:00
->inject('response')
->inject('dbForProject')
2025-01-30 04:53:53 +00:00
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $tableId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
2024-03-06 17:34:21 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
2019-05-09 06:54:39 +00:00
2023-08-16 21:58:25 +00:00
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
$table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId));
2019-05-09 06:54:39 +00:00
if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
2021-12-28 17:57:24 +00:00
2024-02-08 16:10:25 +00:00
try {
2024-02-12 16:02:04 +00:00
$queries = Query::parseQueries($queries);
2024-02-08 16:10:25 +00:00
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
2020-06-29 21:43:34 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter($queries, function ($query) {
2023-12-06 14:10:40 +00:00
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
2023-08-22 03:25:55 +00:00
});
$cursor = \reset($cursor);
2022-08-30 11:55:23 +00:00
if ($cursor) {
2024-05-09 16:54:28 +00:00
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$rowId = $cursor->getValue();
2021-07-05 20:27:20 +00:00
$cursorRow = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId));
2022-08-24 13:24:19 +00:00
if ($cursorRow->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Row '{$rowId}' for the 'cursor' value not found.");
2021-08-05 19:01:00 +00:00
}
2022-08-11 23:53:52 +00:00
$cursor->setValue($cursorRow);
2022-08-11 23:53:52 +00:00
}
try {
$rows = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null.");
}
2025-01-06 16:01:44 +00:00
$operations = 0;
2025-01-02 08:09:44 +00:00
// Add $tableId and $databaseId for all rows
$processRow = (function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations): bool {
if ($row->isEmpty()) {
2023-03-28 09:02:49 +00:00
return false;
}
2025-01-06 16:01:44 +00:00
$operations++;
$row->removeAttribute('$collection');
$row->setAttribute('$databaseId', $database->getId());
$row->setAttribute('$collectionId', $table->getId());
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
2025-01-06 16:51:39 +00:00
if (\in_array(\gettype($related), ['array', 'object'])) {
$operations++;
}
continue;
}
2025-01-06 16:01:44 +00:00
if (!\is_array($related)) {
2023-03-28 09:02:49 +00:00
$relations = [$related];
} else {
$relations = $related;
}
$relatedTableId = $relationship->getAttribute('relatedCollection');
2025-01-02 08:09:44 +00:00
// todo: Use local cache for this getDocument
$relatedTable = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId));
2023-03-28 09:02:49 +00:00
foreach ($relations as $index => $doc) {
2023-03-31 04:56:25 +00:00
if ($doc instanceof Document) {
if (!$processRow($relatedTable, $doc)) {
2023-03-31 04:56:25 +00:00
unset($relations[$index]);
}
2023-03-28 09:02:49 +00:00
}
}
if (\is_array($related)) {
$row->setAttribute($relationship->getAttribute('key'), \array_values($relations));
2023-03-28 09:02:49 +00:00
} elseif (empty($relations)) {
$row->setAttribute($relationship->getAttribute('key'), null);
}
}
2023-03-28 09:02:49 +00:00
return true;
2023-08-16 21:58:25 +00:00
});
foreach ($rows as $row) {
$processRow($table, $row);
2023-08-16 21:58:25 +00:00
}
2021-12-27 10:45:24 +00:00
$queueForStatsUsage
2025-03-18 14:20:25 +00:00
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
2025-01-06 09:28:26 +00:00
2025-01-06 15:19:39 +00:00
$response->addHeader('X-Debug-Operations', $operations);
2025-01-06 14:50:23 +00:00
2024-02-24 10:55:33 +00:00
$select = \array_reduce($queries, function ($result, $query) {
return $result || ($query->getMethod() === Query::TYPE_SELECT);
}, false);
// Check if the SELECT query includes $databaseId and $collectionId
$hasDatabaseId = false;
$hasTableId = false;
2024-02-24 10:55:33 +00:00
if ($select) {
$hasDatabaseId = \array_reduce($queries, function ($result, $query) {
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues()));
}, false);
$hasTableId = \array_reduce($queries, function ($result, $query) {
2024-02-24 10:55:33 +00:00
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues()));
}, false);
}
if ($select) {
foreach ($rows as $row) {
2024-02-24 10:55:33 +00:00
if (!$hasDatabaseId) {
$row->removeAttribute('$databaseId');
2024-02-24 10:55:33 +00:00
}
if (!$hasTableId) {
$row->removeAttribute('$collectionId');
2024-02-24 10:55:33 +00:00
}
}
}
2021-07-25 14:47:18 +00:00
$response->dynamic(new Document([
2022-02-27 09:57:09 +00:00
'total' => $total,
'documents' => $rows,
2021-06-14 19:55:36 +00:00
]), Response::MODEL_DOCUMENT_LIST);
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId')
->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId')
->desc('Get row')
2020-06-25 18:32:12 +00:00
->groups(['api', 'database'])
->label('scope', 'documents.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: 'rows',
name: 'getRow',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/get-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
))
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('rowId', '', new UID(), 'Row ID.')
2024-02-02 10:54:34 +00:00
->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
2020-12-26 15:05:04 +00:00
->inject('response')
->inject('dbForProject')
2025-01-30 04:53:53 +00:00
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
2024-03-06 17:34:21 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
2019-05-09 06:54:39 +00:00
2023-08-16 21:58:25 +00:00
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
$table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId));
2019-05-09 06:54:39 +00:00
if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
2023-08-16 21:58:25 +00:00
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
2023-08-22 03:25:55 +00:00
try {
2024-02-12 16:02:04 +00:00
$queries = Query::parseQueries($queries);
$row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId, $queries);
2023-08-22 03:25:55 +00:00
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (QueryException $e) {
2024-02-12 16:02:04 +00:00
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
2022-08-08 10:58:28 +00:00
if ($row->isEmpty()) {
2022-08-16 09:07:30 +00:00
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
2022-08-08 10:58:28 +00:00
}
2025-01-06 16:01:44 +00:00
$operations = 0;
2025-01-02 08:09:44 +00:00
// Add $tableId and $databaseId for all rows
$processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database, &$operations) {
if ($row->isEmpty()) {
return;
2023-03-28 09:02:49 +00:00
}
2025-01-06 16:01:44 +00:00
$operations++;
$row->setAttribute('$databaseId', $database->getId());
$row->setAttribute('$collectionId', $table->getId());
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
2025-01-06 16:51:39 +00:00
if (\in_array(\gettype($related), ['array', 'object'])) {
$operations++;
}
continue;
}
2025-01-06 16:01:44 +00:00
if (!\is_array($related)) {
$related = [$related];
}
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
);
2023-03-29 01:37:56 +00:00
foreach ($related as $relation) {
2023-04-03 10:39:31 +00:00
if ($relation instanceof Document) {
$processRow($relatedTable, $relation);
2023-04-03 10:39:31 +00:00
}
}
}
};
$processRow($table, $row);
2021-12-27 10:45:24 +00:00
$queueForStatsUsage
2025-03-18 14:20:25 +00:00
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
2025-01-02 07:56:14 +00:00
2025-01-06 15:19:39 +00:00
$response->addHeader('X-Debug-Operations', $operations);
$response->dynamic($row, Response::MODEL_DOCUMENT);
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
App::get('/v1/databases/:databaseId/tables/:tableId/rows/:rowId/logs')
->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId/logs')
->desc('List row logs')
->groups(['api', 'database'])
->label('scope', 'documents.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
2025-03-31 05:48:17 +00:00
group: 'logs',
name: 'listRowLogs',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/get-document-logs.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_LOG_LIST,
)
],
contentType: ContentType::JSON,
))
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Collection ID.')
->param('rowId', '', new UID(), 'Row ID.')
2023-05-16 12:56:20 +00:00
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $databaseId, string $tableId, string $rowId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
2024-03-06 17:34:21 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
$table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$row = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId);
if ($row->isEmpty()) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
}
2024-02-12 16:02:04 +00:00
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
2025-02-25 08:03:37 +00:00
// Temp fix for logs
$queries[] = Query::or([
2025-02-26 01:00:16 +00:00
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
2025-02-25 08:03:37 +00:00
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
2025-02-25 05:51:11 +00:00
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/table/' . $tableId . '/row/' . $row->getId();
2025-02-25 07:26:00 +00:00
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
'userId' => $log['data']['userId'],
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
if ($record) {
2022-05-23 14:54:50 +00:00
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
2025-01-06 16:57:35 +00:00
2021-11-18 10:33:42 +00:00
$response->dynamic(new Document([
2025-02-25 07:26:00 +00:00
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
2021-11-18 10:33:42 +00:00
]), Response::MODEL_LOG_LIST);
});
App::patch('/v1/databases/:databaseId/tables/:tableId/rows/:rowId')
->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId')
->desc('Update row')
2020-06-25 18:32:12 +00:00
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].update')
->label('scope', 'documents.write')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'row.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: 'rows',
name: 'updateRow',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/update-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
))
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Collection ID.')
->param('rowId', '', new UID(), 'Row ID.')
->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true)
2023-10-13 13:43:23 +00:00
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('requestTimestamp')
2020-12-26 15:05:04 +00:00
->inject('response')
->inject('dbForProject')
2022-12-20 16:11:30 +00:00
->inject('queueForEvents')
2025-01-30 04:53:53 +00:00
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $tableId, string $rowId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
2019-05-09 06:54:39 +00:00
2022-08-08 10:58:28 +00:00
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
2022-08-14 05:21:33 +00:00
if (empty($data) && \is_null($permissions)) {
2022-08-16 09:07:30 +00:00
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD);
2022-08-08 10:58:28 +00:00
}
2025-01-02 07:56:14 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
2023-08-16 21:58:25 +00:00
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
$table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId));
2019-09-16 19:03:24 +00:00
if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
2023-08-16 21:58:25 +00:00
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2021-08-12 01:05:19 +00:00
}
// Read permission should not be required for update
/** @var Document $row */
$row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId));
if ($row->isEmpty()) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
2020-06-29 21:43:34 +00:00
}
2019-05-09 06:54:39 +00:00
2022-08-25 03:51:21 +00:00
// Map aggregate permissions into the multiple permissions they represent.
$permissions = Permission::aggregate($permissions, [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
]);
2022-08-15 12:56:19 +00:00
// Users can only manage their own roles, API keys and Admin users can manage any
2022-08-08 10:58:28 +00:00
$roles = Authorization::getRoles();
2023-08-16 21:58:25 +00:00
if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) {
2022-08-15 12:56:19 +00:00
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
2022-08-16 11:26:38 +00:00
$permission = Permission::parse($permission);
if ($permission->getPermission() != $type) {
2022-08-15 12:56:19 +00:00
continue;
}
2022-08-16 11:26:38 +00:00
$role = (new Role(
$permission->getRole(),
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
2022-08-15 12:56:19 +00:00
if (!Authorization::isRole($role)) {
2022-08-25 03:51:21 +00:00
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
2022-08-15 12:56:19 +00:00
}
2022-08-08 10:58:28 +00:00
}
}
}
if (\is_null($permissions)) {
$permissions = $row->getPermissions() ?? [];
}
$data['$id'] = $rowId;
$data['$permissions'] = $permissions;
$newRow = new Document($data);
2020-10-30 19:53:27 +00:00
2025-01-06 16:51:39 +00:00
$operations = 0;
2025-01-06 16:01:44 +00:00
$setTable = (function (Document $collection, Document $document) use (&$setTable, $dbForProject, $database, &$operations) {
2025-01-06 16:51:39 +00:00
$operations++;
2023-03-28 09:02:49 +00:00
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
2023-03-28 09:02:49 +00:00
);
foreach ($relationships as $relationship) {
$related = $document->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
continue;
}
2023-04-14 10:03:16 +00:00
$isList = \is_array($related) && \array_values($related) === $related;
if ($isList) {
$relations = $related;
} else {
$relations = [$related];
}
2023-03-28 09:02:49 +00:00
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
2025-01-02 07:56:14 +00:00
);
2023-03-28 09:02:49 +00:00
foreach ($relations as &$relation) {
// If the relation is an array it can be either update or create a child document.
if (
\is_array($relation)
&& \array_values($relation) !== $relation
&& !isset($relation['$id'])
) {
$relation['$id'] = ID::unique();
$relation = new Document($relation);
}
2023-03-29 01:37:56 +00:00
if ($relation instanceof Document) {
$oldRow = Authorization::skip(fn () => $dbForProject->getDocument(
'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId(),
2025-01-02 07:56:14 +00:00
$relation->getId()
));
2023-08-07 23:10:07 +00:00
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
// Attribute $collection is required for Utopia.
2025-01-02 07:56:14 +00:00
$relation->setAttribute(
'$collection',
'database_' . $database->getInternalId() . '_collection_' . $relatedTable->getInternalId()
2025-01-02 07:56:14 +00:00
);
if ($oldRow->isEmpty()) {
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
}
$setTable($relatedTable, $relation);
2023-03-28 09:02:49 +00:00
}
}
2023-04-14 10:03:16 +00:00
if ($isList) {
$document->setAttribute($relationship->getAttribute('key'), \array_values($relations));
} else {
$document->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
}
2023-08-17 13:34:41 +00:00
});
2023-03-28 09:02:49 +00:00
$setTable($table, $newRow);
2022-08-08 10:58:28 +00:00
$queueForStatsUsage
2025-03-18 14:20:25 +00:00
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations);
2025-01-06 09:28:26 +00:00
2025-01-06 15:19:39 +00:00
$response->addHeader('X-Debug-Operations', $operations);
2025-01-06 14:50:23 +00:00
2023-08-17 13:34:41 +00:00
try {
$row = $dbForProject->withRequestTimestamp(
2025-01-02 07:56:14 +00:00
$requestTimestamp,
fn () => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(),
$row->getId(),
$newRow
2025-01-02 07:56:14 +00:00
)
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
2022-08-08 10:58:28 +00:00
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
2024-11-04 07:57:08 +00:00
} catch (StructureException $e) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage());
} catch (NotFoundException $e) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2021-08-15 21:09:40 +00:00
}
2021-12-16 18:12:06 +00:00
// Add $tableId and $databaseId for all rows
$processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) {
$row->setAttribute('$databaseId', $database->getId());
$row->setAttribute('$collectionId', $table->getId());
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
2023-03-27 07:08:02 +00:00
continue;
}
if (!\is_array($related)) {
$related = [$related];
}
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
2025-01-02 07:56:14 +00:00
);
2023-03-27 07:08:02 +00:00
2023-03-29 01:37:56 +00:00
foreach ($related as $relation) {
2023-04-03 10:39:31 +00:00
if ($relation instanceof Document) {
$processRow($relatedTable, $relation);
2023-04-03 10:39:31 +00:00
}
}
}
};
$processRow($table, $row);
$response->dynamic($row, Response::MODEL_DOCUMENT);
2024-07-11 15:03:28 +00:00
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
\array_filter(
$table->getAttribute('attributes', []),
2024-07-11 15:03:28 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
)
);
2022-12-20 16:11:30 +00:00
$queueForEvents
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('rowId', $row->getId())
->setContext('table', $table)
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setContext('database', $database)
2024-07-11 15:03:28 +00:00
->setPayload($response->getPayload(), sensitive: $relationships);
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
App::delete('/v1/databases/:databaseId/tables/:tableId/rows/:rowId')
->alias('/v1/databases/:databaseId/collections/:tableId/documents/:rowId')
->desc('Delete row')
2020-06-25 18:32:12 +00:00
->groups(['api', 'database'])
->label('scope', 'documents.write')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete')
->label('audits.event', 'row.delete')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{request.rowId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: 'rows',
name: 'deleteRow',
2025-01-17 04:31:39 +00:00
description: '/docs/references/databases/delete-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE
))
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('rowId', '', new UID(), 'Row ID.')
->inject('requestTimestamp')
2020-12-26 15:05:04 +00:00
->inject('response')
->inject('dbForProject')
2022-12-20 16:11:30 +00:00
->inject('queueForEvents')
2025-01-30 04:53:53 +00:00
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $tableId, string $rowId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
2024-03-06 17:34:21 +00:00
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
2019-05-09 06:54:39 +00:00
2023-08-16 21:58:25 +00:00
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
$table = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId));
2019-05-09 06:54:39 +00:00
if ($table->isEmpty() || (!$table->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
2023-08-16 21:58:25 +00:00
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2021-08-12 01:05:19 +00:00
}
// Read permission should not be required for delete
$row = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(), $rowId));
if ($row->isEmpty()) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
2020-06-29 21:43:34 +00:00
}
2019-05-09 06:54:39 +00:00
2024-11-04 07:57:08 +00:00
try {
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $table, $rowId) {
2025-01-02 07:56:14 +00:00
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $table->getInternalId(),
$rowId
2025-01-02 07:56:14 +00:00
);
2024-11-04 07:57:08 +00:00
});
} catch (NotFoundException $e) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
2022-08-08 10:58:28 +00:00
// Add $tableId and $databaseId for all rows
$processRow = function (Document $table, Document $row) use (&$processRow, $dbForProject, $database) {
$row->setAttribute('$databaseId', $database->getId());
$row->setAttribute('$collectionId', $table->getId());
$relationships = \array_filter(
$table->getAttribute('attributes', []),
2024-03-06 17:34:21 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
foreach ($relationships as $relationship) {
$related = $row->getAttribute($relationship->getAttribute('key'));
if (empty($related)) {
continue;
}
if (!\is_array($related)) {
$related = [$related];
}
$relatedTableId = $relationship->getAttribute('relatedCollection');
$relatedTable = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId)
2025-01-02 07:56:14 +00:00
);
2023-03-29 03:30:47 +00:00
foreach ($related as $relation) {
2023-04-03 10:39:31 +00:00
if ($relation instanceof Document) {
$processRow($relatedTable, $relation);
2023-04-03 10:39:31 +00:00
}
}
}
};
$processRow($table, $row);
2021-12-27 10:45:24 +00:00
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
2025-03-30 05:54:48 +00:00
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection
2025-01-06 09:28:26 +00:00
2025-01-06 17:07:18 +00:00
$response->addHeader('X-Debug-Operations', 1);
2025-01-06 16:57:35 +00:00
2024-07-11 15:03:28 +00:00
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
\array_filter(
$table->getAttribute('attributes', []),
2024-07-11 15:03:28 +00:00
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
)
);
2022-12-20 16:11:30 +00:00
$queueForEvents
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('rowId', $row->getId())
->setContext('table', $table)
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
->setContext('database', $database)
->setPayload($response->output($row, Response::MODEL_DOCUMENT), sensitive: $relationships);
2020-06-29 21:43:34 +00:00
$response->noContent();
});
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
App::get('/v1/databases/usage')
2024-02-26 02:44:20 +00:00
->desc('Get databases usage stats')
->groups(['api', 'database', 'usage'])
2022-06-23 08:50:11 +00:00
->label('scope', 'collections.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: null,
2025-01-17 04:31:39 +00:00
name: 'getUsage',
description: '/docs/references/databases/get-usage.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USAGE_DATABASES,
)
],
contentType: ContentType::JSON
))
2023-11-08 09:09:32 +00:00
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
2022-06-23 08:50:11 +00:00
->inject('response')
->inject('dbForProject')
->action(function (string $range, Response $response, Database $dbForProject) {
2023-10-25 07:39:59 +00:00
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
$metrics = [
METRIC_DATABASES,
METRIC_COLLECTIONS,
METRIC_DOCUMENTS,
2025-01-23 13:24:02 +00:00
METRIC_DATABASES_STORAGE,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
2023-10-25 07:39:59 +00:00
];
2022-06-23 08:50:11 +00:00
2023-11-08 09:09:32 +00:00
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
$stats[$metric]['total'] = $result['value'] ?? 0;
2023-10-25 07:39:59 +00:00
$limit = $days['limit'];
$period = $days['period'];
2024-02-01 10:21:50 +00:00
$results = $dbForProject->find('stats', [
2023-10-25 07:39:59 +00:00
Query::equal('metric', [$metric]),
2023-10-25 12:06:54 +00:00
Query::equal('period', [$period]),
2023-10-25 07:39:59 +00:00
Query::limit($limit),
Query::orderDesc('time'),
]);
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'] = [];
2023-10-25 07:39:59 +00:00
foreach ($results as $result) {
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
2023-10-25 07:39:59 +00:00
];
2023-10-15 17:41:09 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
2023-10-15 17:41:09 +00:00
2023-10-25 07:39:59 +00:00
$format = match ($days['period']) {
'1h' => 'Y-m-d\TH:00:00.000P',
'1d' => 'Y-m-d\T00:00:00.000P',
};
2023-10-15 17:41:09 +00:00
2024-03-06 17:34:21 +00:00
foreach ($metrics as $metric) {
$usage[$metric]['total'] = $stats[$metric]['total'];
2024-03-06 17:34:21 +00:00
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
2022-06-23 08:50:11 +00:00
}
2023-10-25 07:39:59 +00:00
$response->dynamic(new Document([
'range' => $range,
'databasesTotal' => $usage[$metrics[0]]['total'],
2023-11-08 09:09:32 +00:00
'collectionsTotal' => $usage[$metrics[1]]['total'],
'documentsTotal' => $usage[$metrics[2]]['total'],
'storageTotal' => $usage[$metrics[3]]['total'],
2025-01-23 13:24:02 +00:00
'databasesReadsTotal' => $usage[$metrics[4]]['total'],
'databasesWritesTotal' => $usage[$metrics[5]]['total'],
'databases' => $usage[$metrics[0]]['data'],
2023-11-08 09:09:32 +00:00
'collections' => $usage[$metrics[1]]['data'],
'documents' => $usage[$metrics[2]]['data'],
'storage' => $usage[$metrics[3]]['data'],
2025-01-23 13:24:02 +00:00
'databasesReads' => $usage[$metrics[4]]['data'],
'databasesWrites' => $usage[$metrics[5]]['data'],
2023-10-25 07:39:59 +00:00
]), Response::MODEL_USAGE_DATABASES);
2022-06-23 08:50:11 +00:00
});
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
App::get('/v1/databases/:databaseId/usage')
2024-02-26 02:44:20 +00:00
->desc('Get database usage stats')
->groups(['api', 'database', 'usage'])
2022-06-23 08:50:11 +00:00
->label('scope', 'collections.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: null,
2025-01-17 04:31:39 +00:00
name: 'getDatabaseUsage',
description: '/docs/references/databases/get-database-usage.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USAGE_DATABASE,
)
],
contentType: ContentType::JSON,
))
2022-06-23 08:50:11 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
2023-11-08 09:09:32 +00:00
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true)
2022-06-23 08:50:11 +00:00
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) {
$database = $dbForProject->getDocument('databases', $databaseId);
2022-06-23 08:50:11 +00:00
2023-10-25 07:39:59 +00:00
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
2022-06-23 08:50:11 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
$metrics = [
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
2025-01-23 13:24:02 +00:00
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_READS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_WRITES)
2023-10-25 07:39:59 +00:00
];
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
2023-11-08 09:09:32 +00:00
$stats[$metric]['total'] = $result['value'] ?? 0;
2023-10-25 07:39:59 +00:00
$limit = $days['limit'];
$period = $days['period'];
2024-02-01 10:21:50 +00:00
$results = $dbForProject->find('stats', [
2023-10-25 07:39:59 +00:00
Query::equal('metric', [$metric]),
2023-10-25 12:06:54 +00:00
Query::equal('period', [$period]),
2023-10-25 07:39:59 +00:00
Query::limit($limit),
Query::orderDesc('time'),
]);
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'] = [];
2023-10-25 07:39:59 +00:00
foreach ($results as $result) {
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
2023-10-25 07:39:59 +00:00
];
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
$format = match ($days['period']) {
'1h' => 'Y-m-d\TH:00:00.000P',
'1d' => 'Y-m-d\T00:00:00.000P',
};
2024-03-06 17:34:21 +00:00
foreach ($metrics as $metric) {
$usage[$metric]['total'] = $stats[$metric]['total'];
2024-03-06 17:34:21 +00:00
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
2022-06-23 08:50:11 +00:00
}
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$response->dynamic(new Document([
'range' => $range,
'collectionsTotal' => $usage[$metrics[0]]['total'],
'documentsTotal' => $usage[$metrics[1]]['total'],
'storageTotal' => $usage[$metrics[2]]['total'],
2025-01-23 13:24:02 +00:00
'databaseReadsTotal' => $usage[$metrics[3]]['total'],
'databaseWritesTotal' => $usage[$metrics[4]]['total'],
'collections' => $usage[$metrics[0]]['data'],
'documents' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
'databaseReads' => $usage[$metrics[3]]['data'],
'databaseWrites' => $usage[$metrics[4]]['data'],
2023-10-25 07:39:59 +00:00
]), Response::MODEL_USAGE_DATABASE);
2022-06-23 08:50:11 +00:00
});
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
App::get('/v1/databases/:databaseId/tables/:tableId/usage')
->alias('/v1/databases/:databaseId/collections/:tableId/usage')
->desc('Get table usage stats')
->groups(['api', 'database', 'usage'])
2022-06-23 08:50:11 +00:00
->label('scope', 'collections.read')
2024-10-29 15:07:12 +00:00
->label('resourceType', RESOURCE_TYPE_DATABASES)
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getTableUsage',
description: '/docs/references/databases/get-collection-usage.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_USAGE_COLLECTION,
)
],
contentType: ContentType::JSON,
))
2022-06-23 08:50:11 +00:00
->param('databaseId', '', new UID(), 'Database ID.')
2023-11-08 09:09:32 +00:00
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->param('tableId', '', new UID(), 'Collection ID.')
2022-06-23 08:50:11 +00:00
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $range, string $tableId, Response $response, Database $dbForProject) {
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2024-03-06 17:34:21 +00:00
$database = $dbForProject->getDocument('databases', $databaseId);
$tableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId);
$table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $tableDocument->getInternalId());
2022-06-23 08:50:11 +00:00
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
2022-06-23 08:50:11 +00:00
}
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config::getParam('usage', []);
$stats = $usage = [];
$days = $periods[$range];
$metrics = [
str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $tableDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS),
2023-10-25 07:39:59 +00:00
];
2022-06-23 08:50:11 +00:00
2023-11-08 09:09:32 +00:00
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
$stats[$metric]['total'] = $result['value'] ?? 0;
2023-10-25 07:39:59 +00:00
$limit = $days['limit'];
$period = $days['period'];
2024-02-01 10:21:50 +00:00
$results = $dbForProject->find('stats', [
2023-10-25 07:39:59 +00:00
Query::equal('metric', [$metric]),
2023-10-25 12:06:54 +00:00
Query::equal('period', [$period]),
2023-10-25 07:39:59 +00:00
Query::limit($limit),
Query::orderDesc('time'),
]);
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'] = [];
2023-10-25 07:39:59 +00:00
foreach ($results as $result) {
2023-11-08 09:09:32 +00:00
$stats[$metric]['data'][$result->getAttribute('time')] = [
'value' => $result->getAttribute('value'),
2023-10-25 07:39:59 +00:00
];
2023-10-15 17:41:09 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
2022-06-23 08:50:11 +00:00
2023-10-25 07:39:59 +00:00
$format = match ($days['period']) {
'1h' => 'Y-m-d\TH:00:00.000P',
'1d' => 'Y-m-d\T00:00:00.000P',
};
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2024-03-06 17:34:21 +00:00
foreach ($metrics as $metric) {
$usage[$metric]['total'] = $stats[$metric]['total'];
2024-03-06 17:34:21 +00:00
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
$leap += $days['factor'];
$formatDate = date($format, $leap);
$usage[$metric]['data'][] = [
'value' => $stats[$metric]['data'][$formatDate]['value'] ?? 0,
'date' => $formatDate,
];
}
2022-06-23 08:50:11 +00:00
}
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$response->dynamic(new Document([
'range' => $range,
'documentsTotal' => $usage[$metrics[0]]['total'],
'documents' => $usage[$metrics[0]]['data'],
2023-10-25 07:39:59 +00:00
]), Response::MODEL_USAGE_COLLECTION);
2022-06-23 08:50:11 +00:00
});