Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-mfa

This commit is contained in:
Torsten Dittmann
2024-02-01 11:57:32 +01:00
72 changed files with 1954 additions and 870 deletions
+4 -4
View File
@@ -344,13 +344,13 @@ Things to remember when releasing SDKs:
## Debug
Appwrite uses [yasd](https://github.com/swoole/yasd) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code's [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension.
Appwrite uses [XDebug](https://github.com/xdebug/xdebug) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code's [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension.
If you are in PHP Storm you don't need any plugin. Below are the settings required for remote debugger connection:
1. Create an init file.
2. Duplicate **dev/yasd_init.php.stub** file and name it **dev/yasd_init.php**.
3. Set **DEBUG** build arg in **appwrite** service in **docker-compose.yml** file.
1. Set **DEBUG** build arg in **appwrite** service in **docker-compose.yml** file.
2. If needed edit the **dev/xdebug.ini** file to your needs.
3. Launch your Appwrite instance while your debugger is listening for connections.
### VS Code Launch Configuration
+5 -3
View File
@@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
FROM appwrite/base:0.4.3 as final
FROM appwrite/base:0.7.2 as final
LABEL maintainer="team@appwrite.io"
@@ -56,6 +56,7 @@ COPY ./public /usr/src/code/public
COPY ./bin /usr/local/bin
COPY ./docs /usr/src/code/docs
COPY ./src /usr/src/code/src
COPY ./dev /usr/src/code/dev
# Set Volumes
RUN mkdir -p /storage/uploads && \
@@ -126,9 +127,10 @@ RUN chmod +x /usr/local/bin/calc-tier-stats && \
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
RUN if [ "$DEBUG" == "true" ]; then printf "zend_extension=yasd \nyasd.debug_mode=remote \nyasd.init_file=/usr/src/code/dev/yasd_init.php \nyasd.remote_port=9005 \nyasd.log_level=-1" >> /usr/local/etc/php/conf.d/yasd.ini; fi
RUN if [ "$DEBUG" == "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" == "true" ]; then echo "opcache.enable=0" >> /usr/local/etc/php/conf.d/appwrite.ini; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so; fi
RUN echo "opcache.preload_user=www-data" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/appwrite.ini
+1 -1
View File
@@ -72,7 +72,7 @@ CLI::setResource('dbForConsole', function ($pools, $cache) {
$collections = Config::getParam('collections', [])['console'];
$last = \array_key_last($collections);
if (!($dbForConsole->exists($dbForConsole->getDefaultDatabase(), $last))) { /** TODO cache ready variable using registry */
if (!($dbForConsole->exists($dbForConsole->getDatabase(), $last))) { /** TODO cache ready variable using registry */
throw new Exception('Tables not ready yet.');
}
+11 -22
View File
@@ -1731,17 +1731,6 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('description'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('status'),
'type' => Database::VAR_STRING,
@@ -1902,17 +1891,6 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('description'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2048,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('total'),
'type' => Database::VAR_INTEGER,
@@ -2194,6 +2172,17 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('expired'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => false,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
+5
View File
@@ -816,6 +816,11 @@ return [
'description' => 'Provider with the requested ID is of the incorrect type.',
'code' => 400,
],
Exception::PROVIDER_MISSING_CREDENTIALS => [
'name' => Exception::PROVIDER_MISSING_CREDENTIALS,
'description' => 'Provider with the requested ID is missing credentials.',
'code' => 400,
],
/** Topics */
Exception::TOPIC_NOT_FOUND => [
+26 -25
View File
@@ -174,7 +174,8 @@ App::post('/v1/account')
]);
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
@@ -279,7 +280,7 @@ App::post('/v1/account/sessions/email')
$dbForProject->updateDocument('users', $user->getId(), $user);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
@@ -613,7 +614,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$currentDocument = $dbForProject->getDocument('sessions', $current);
if (!$currentDocument->isEmpty()) {
$dbForProject->deleteDocument('sessions', $currentDocument->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
}
}
@@ -876,7 +877,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$state['success']['query'] = URLParser::unparseQuery($query);
$state['success'] = URLParser::unparse($state['success']);
@@ -915,7 +916,7 @@ App::get('/v1/account/identities')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -1098,7 +1099,7 @@ App::post('/v1/account/tokens/magic-url')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
if (empty($url)) {
$url = $request->getProtocol() . '://' . $request->getHostname() . '/auth/magic-url';
@@ -1336,7 +1337,7 @@ App::post('/v1/account/tokens/email')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$subject = $locale->getText("emails.otpSession.subject");
$customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? [];
@@ -1509,9 +1510,9 @@ $createSession = function (string $userId, string $secret, Request $request, Res
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) {
$user->setAttribute('emailVerification', true);
@@ -1708,7 +1709,7 @@ App::post('/v1/account/tokens/phone')
]);
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
}
$secret = Auth::codeGenerator();
@@ -1734,7 +1735,7 @@ App::post('/v1/account/tokens/phone')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
@@ -1894,7 +1895,7 @@ App::post('/v1/account/sessions/anonymous')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2030,7 +2031,7 @@ App::post('/v1/account/targets/push')
} catch (Duplicate) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2408,7 +2409,7 @@ App::patch('/v1/account/email')
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
@@ -2489,7 +2490,7 @@ App::patch('/v1/account/phone')
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
}
@@ -2635,7 +2636,7 @@ App::delete('/v1/account/sessions/:sessionId')
;
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2714,7 +2715,7 @@ App::patch('/v1/account/sessions/:sessionId')
// Save changes
$dbForProject->updateDocument('sessions', $sessionId, $session);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2775,7 +2776,7 @@ App::delete('/v1/account/sessions')
}
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2860,7 +2861,7 @@ App::post('/v1/account/recovery')
Permission::delete(Role::user($profile->getId())),
]));
$dbForProject->deleteCachedDocument('users', $profile->getId());
$dbForProject->purgeCachedDocument('users', $profile->getId());
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]);
@@ -3036,7 +3037,7 @@ App::put('/v1/account/recovery')
* the recovery token but actually we don't need it anymore.
*/
$dbForProject->deleteDocument('tokens', $verifiedToken->getId());
$dbForProject->deleteCachedDocument('users', $profile->getId());
$dbForProject->purgeCachedDocument('users', $profile->getId());
$queueForEvents
->setParam('userId', $profile->getId())
@@ -3107,7 +3108,7 @@ App::post('/v1/account/verification')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]);
@@ -3256,7 +3257,7 @@ App::put('/v1/account/verification')
* the verification token but actually we don't need it anymore.
*/
$dbForProject->deleteDocument('tokens', $verifiedToken->getId());
$dbForProject->deleteCachedDocument('users', $profile->getId());
$dbForProject->purgeCachedDocument('users', $profile->getId());
$queueForEvents
->setParam('userId', $userId)
@@ -3329,7 +3330,7 @@ App::post('/v1/account/verification/phone')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
@@ -3425,7 +3426,7 @@ App::put('/v1/account/verification/phone')
* We act like we're updating and validating the verification token but actually we don't need it anymore.
*/
$dbForProject->deleteDocument('tokens', $verifiedToken->getId());
$dbForProject->deleteCachedDocument('users', $profile->getId());
$dbForProject->purgeCachedDocument('users', $profile->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -3865,7 +3866,7 @@ App::put('/v1/account/targets/:targetId/push')
$target->setAttribute('name', "{$device['deviceBrand']} {$device['deviceModel']}");
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
+2 -2
View File
@@ -116,7 +116,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Throwable $err) {
$index = 0;
do {
@@ -521,7 +521,7 @@ App::get('/v1/avatars/initials')
// if there is no space, try to split by `_` underscore
$words = (count($words) == 1) ? \explode('_', \strtoupper($name)) : $words;
$initials = null;
$initials = '';
$code = 0;
foreach ($words as $key => $w) {
+30 -33
View File
@@ -28,7 +28,6 @@ use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -153,13 +152,13 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded');
} catch (\Exception $e) {
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
throw $e;
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$twoWayKey = $options['twoWayKey'];
@@ -197,13 +196,13 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
$dbForProject->deleteDocument('attributes', $attribute->getId());
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded');
} catch (\Exception $e) {
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
throw $e;
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
}
$queueForDatabase
@@ -358,7 +357,7 @@ function updateAttribute(
$relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options);
$relatedAttribute->setAttribute('options', $relatedOptions);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute);
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
}
} else {
$dbForProject->updateAttribute(
@@ -371,7 +370,7 @@ function updateAttribute(
}
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collection->getId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId());
$queueForEvents
->setContext('collection', $collection)
@@ -496,7 +495,7 @@ App::get('/v1/databases')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -703,8 +702,8 @@ App::delete('/v1/databases/:databaseId')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB');
}
$dbForProject->deleteCachedDocument('databases', $database->getId());
$dbForProject->deleteCachedCollection('databases_' . $database->getInternalId());
$dbForProject->purgeCachedDocument('databases', $database->getId());
$dbForProject->purgeCachedCollection('databases_' . $database->getInternalId());
$queueForDatabase
->setType(DATABASE_TYPE_DELETE_DATABASE)
@@ -818,7 +817,7 @@ App::get('/v1/databases/:databaseId/collections')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -901,6 +900,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
$collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId());
@@ -1076,7 +1076,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB');
}
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$queueForDatabase
->setType(DATABASE_TYPE_DELETE_COLLECTION)
@@ -1672,7 +1672,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = \reset($cursor);
@@ -2248,8 +2248,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
$attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting'));
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $collection->getInternalId());
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $attribute->getAttribute('options');
@@ -2270,8 +2270,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting'));
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']);
$dbForProject->deleteCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
}
}
@@ -2330,7 +2330,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', null, new Key(), 'Index Key.')
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY]), 'Index type.')
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.')
->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.')
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true)
->inject('response')
@@ -2453,7 +2453,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
throw new Exception(Exception::INDEX_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$queueForDatabase
->setType(DATABASE_TYPE_CREATE_INDEX)
@@ -2509,7 +2509,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
@@ -2620,7 +2620,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
$index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting'));
}
$dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collectionId);
$queueForDatabase
->setType(DATABASE_TYPE_DELETE_INDEX)
@@ -2893,13 +2893,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('sdk.offline.model', '/databases/{databaseId}/collections/{collectionId}/documents')
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->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)
->param('queries', [], new JSON(), '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)
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) {
$queries = Query::parseQueries($queries);
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -2913,11 +2913,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = \reset($cursor);
@@ -2934,11 +2932,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$cursor->setValue($cursorDocument);
}
$filters = Query::groupByType($queries)['filters'];
try {
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filters, APP_LIMIT_COUNT);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (QueryException $e) {
@@ -3020,7 +3017,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('documentId', '', new UID(), 'Document ID.')
->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/databases#querying-documents). Only method allowed is select.', true)
->param('queries', [], new JSON(), '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)
->inject('response')
->inject('dbForProject')
->inject('mode')
+3 -3
View File
@@ -374,7 +374,7 @@ App::get('/v1/functions')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -1268,7 +1268,7 @@ App::get('/v1/functions/:functionId/deployments')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -1805,7 +1805,7 @@ App::get('/v1/functions/:functionId/executions')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
+430 -176
View File
@@ -35,6 +35,7 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\JSON;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use MaxMind\Db\Reader;
use Utopia\Database\DateTime;
@@ -61,30 +62,20 @@ App::post('/v1/messaging/providers/mailgun')
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128), 'Name set in the reply to field for the mail. Default value is sender name. Reply to name must have reply to email as well.', true)
->param('replyToEmail', '', new Text(128), 'Email set in the reply to field for the mail. Default value is sender email. Reply to email must have reply to name as well.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name. Reply to name must have reply to email as well.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email. Reply to email must have reply to name as well.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, string $apiKey, string $domain, ?bool $isEuRegion, ?bool $enabled, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $providerId, string $name, string $apiKey, string $domain, ?bool $isEuRegion, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
$options = [
'fromName' => $fromName,
'fromEmail' => $fromEmail,
];
if (!empty($replyToName) && !empty($replyToEmail)) {
$options['replyToName'] = $replyToName;
$options['replyToEmail'] = $replyToEmail;
}
$credentials = [];
if ($isEuRegion === true || $isEuRegion === false) {
if (!\is_null($isEuRegion)) {
$credentials['isEuRegion'] = $isEuRegion;
}
@@ -96,12 +87,19 @@ App::post('/v1/messaging/providers/mailgun')
$credentials['domain'] = $domain;
}
$options = [
'fromName' => $fromName,
'fromEmail' => $fromEmail,
'replyToName' => $replyToName,
'replyToEmail' => $replyToEmail,
];
if (
$enabled === true &&
\array_key_exists('isEuRegion', $credentials) &&
\array_key_exists('apiKey', $credentials) &&
\array_key_exists('domain', $credentials) &&
\array_key_exists('from', $options)
$enabled === true
&& !empty($fromEmail)
&& \array_key_exists('isEuRegion', $credentials)
&& \array_key_exists('apiKey', $credentials)
&& \array_key_exists('domain', $credentials)
) {
$enabled = true;
} else {
@@ -149,37 +147,34 @@ App::post('/v1/messaging/providers/sendgrid')
->param('providerId', '', new CustomId(), 'Provider 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('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Text(128), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, string $apiKey, ?bool $enabled, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $providerId, string $name, string $apiKey, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
$options = [
'fromName' => $fromName,
'fromEmail' => $fromEmail,
];
if (!empty($replyToName) && !empty($replyToEmail)) {
$options['replyToName'] = $replyToName;
$options['replyToEmail'] = $replyToEmail;
}
$credentials = [];
if (!empty($apiKey)) {
$credentials['apiKey'] = $apiKey;
}
$options = [
'fromName' => $fromName,
'fromEmail' => $fromEmail,
'replyToName' => $replyToName,
'replyToEmail' => $replyToEmail,
];
if (
$enabled === true
&& !empty($fromEmail)
&& \array_key_exists('apiKey', $credentials)
&& \array_key_exists('from', $options)
) {
$enabled = true;
} else {
@@ -210,6 +205,94 @@ App::post('/v1/messaging/providers/sendgrid')
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/smtp')
->desc('Create SMTP provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.create')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createSMTPProvider')
->label('sdk.description', '/docs/references/messaging/create-smtp-provider.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROVIDER)
->param('providerId', '', new CustomId(), 'Provider 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('name', '', new Text(128), 'Provider name.')
->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). You can also specify encryption type, for example: (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). Hosts will be tried in order.')
->param('port', 587, new Range(1, 65535), 'The default SMTP server port.', true)
->param('username', '', new Text(0), 'Authentication username.', true)
->param('password', '', new Text(0), 'Authentication password.', true)
->param('encryption', '', new WhiteList(['none', 'ssl', 'tls']), 'Encryption type. Can be omitted, \'ssl\', or \'tls\'', true)
->param('autoTLS', true, new Boolean(), 'Enable SMTP AutoTLS feature.', true)
->param('mailer', '', new Text(0), 'The value to use for the X-Mailer header.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, string $host, int $port, string $username, string $password, string $encryption, bool $autoTLS, string $mailer, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
$credentials = [
'port' => $port,
'username' => $username,
'password' => $password,
];
if (!empty($host)) {
$credentials['host'] = $host;
}
$options = [
'fromName' => $fromName,
'fromEmail' => $fromEmail,
'replyToName' => $replyToName,
'replyToEmail' => $replyToEmail,
'encryption' => $encryption === 'none' ? '' : $encryption,
'autoTLS' => $autoTLS,
'mailer' => $mailer,
];
if (
$enabled === true
&& !empty($fromEmail)
&& \array_key_exists('host', $credentials)
) {
$enabled = true;
} else {
$enabled = false;
}
$provider = new Document([
'$id' => $providerId,
'name' => $name,
'provider' => 'smtp',
'type' => MESSAGE_TYPE_EMAIL,
'enabled' => $enabled,
'credentials' => $credentials,
'options' => $options,
]);
try {
$provider = $dbForProject->createDocument('providers', $provider);
} catch (DuplicateException) {
throw new Exception(Exception::PROVIDER_ALREADY_EXISTS);
}
$queueForEvents
->setParam('providerId', $provider->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::post('/v1/messaging/providers/msg91')
->desc('Create Msg91 provider')
->groups(['api', 'messaging'])
@@ -616,9 +699,13 @@ App::post('/v1/messaging/providers/fcm')
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, ?array $serviceAccountJSON, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $providerId, string $name, array|string|null $serviceAccountJSON, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
$serviceAccountJSON = \is_string($serviceAccountJSON)
? \json_decode($serviceAccountJSON, true)
: $serviceAccountJSON;
$credentials = [];
if (!\is_null($serviceAccountJSON)) {
@@ -757,7 +844,7 @@ App::get('/v1/messaging/providers')
}
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -917,9 +1004,10 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'mailgun') {
$providerProvider = $provider->getAttribute('provider');
if ($providerProvider !== 'mailgun') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
@@ -949,7 +1037,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
$credentials = $provider->getAttribute('credentials');
if ($isEuRegion === true || $isEuRegion === false) {
if (!\is_null($isEuRegion)) {
$credentials['isEuRegion'] = $isEuRegion;
}
@@ -963,19 +1051,21 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true &&
\array_key_exists('isEuRegion', $credentials) &&
\array_key_exists('apiKey', $credentials) &&
\array_key_exists('domain', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('isEuRegion', $credentials) &&
\array_key_exists('apiKey', $credentials) &&
\array_key_exists('domain', $credentials) &&
\array_key_exists('fromEmail', $options)
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1054,17 +1144,138 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
]);
}
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('apiKey', $provider->getAttribute('credentials'))
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('apiKey', $provider->getAttribute('credentials')) &&
\array_key_exists('fromEmail', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$queueForEvents
->setParam('providerId', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/smtp/:providerId')
->desc('Update SMTP provider')
->groups(['api', 'messaging'])
->label('audits.event', 'provider.update')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateSMTPProvider')
->label('sdk.description', '/docs/references/messaging/update-smtp-provider.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROVIDER)
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). You can also specify encryption type, for example: (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). Hosts will be tried in order.', true)
->param('port', null, new Range(1, 65535), 'SMTP port.', true)
->param('username', '', new Text(0), 'Authentication username.', true)
->param('password', '', new Text(0), 'Authentication password.', true)
->param('encryption', '', new WhiteList(['none', 'ssl', 'tls']), 'Encryption type. Can be \'ssl\' or \'tls\'', true)
->param('autoTLS', null, new Boolean(), 'Enable SMTP AutoTLS feature.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128), 'Name set in the Reply To field for the mail. Default value is Sender Name.', true)
->param('replyToEmail', '', new Text(128), 'Email set in the Reply To field for the mail. Default value is Sender Email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, string $host, ?int $port, string $username, string $password, string $encryption, ?bool $autoTLS, string $fromName, string $fromEmail, string $replyToName, string $replyToEmail, ?bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$providerAttr = $provider->getAttribute('provider');
if ($providerAttr !== 'smtp') {
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
if (!empty($name)) {
$provider->setAttribute('name', $name);
}
$options = $provider->getAttribute('options');
if (!empty($fromName)) {
$options['fromName'] = $fromName;
}
if (!empty($fromEmail)) {
$options['fromEmail'] = $fromEmail;
}
if (!empty($replyToName)) {
$options['replyToName'] = $replyToName;
}
if (!empty($replyToEmail)) {
$options['replyToEmail'] = $replyToEmail;
}
$provider->setAttribute('options', $options);
$credentials = $provider->getAttribute('credentials');
if (!empty($host)) {
$credentials['host'] = $host;
}
if (!\is_null($port)) {
$credentials['port'] = $port;
}
if (!empty($username)) {
$credentials['username'] = $username;
}
if (!empty($password)) {
$credentials['password'] = $password;
}
if (!empty($encryption)) {
$credentials['encryption'] = $encryption === 'none' ? '' : $encryption;
}
if (!\is_null($autoTLS)) {
$credentials['autoTLS'] = $autoTLS;
}
$provider->setAttribute('credentials', $credentials);
if (!\is_null($enabled)) {
if ($enabled) {
if (
!empty($options['fromEmail'])
&& \array_key_exists('host', $credentials)
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1133,18 +1344,20 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('senderId', $credentials)
&& \array_key_exists('authKey', $credentials)
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('senderId', $credentials) &&
\array_key_exists('authKey', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1213,19 +1426,20 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('username', $credentials)
&& \array_key_exists('password', $credentials)
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('username', $credentials) &&
\array_key_exists('password', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1294,19 +1508,20 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('username', $credentials)
&& \array_key_exists('apiKey', $credentials)
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('username', $credentials) &&
\array_key_exists('apiKey', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1375,19 +1590,20 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('accountSid', $credentials)
&& \array_key_exists('authToken', $credentials)
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('accountSid', $credentials) &&
\array_key_exists('authToken', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1456,19 +1672,20 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('apiKey', $credentials)
&& \array_key_exists('apiSecret', $credentials)
&& \array_key_exists('from', $provider->getAttribute('options'))
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('apiKey', $credentials) &&
\array_key_exists('apiSecret', $credentials) &&
\array_key_exists('from', $provider->getAttribute('options'))
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1501,7 +1718,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, string $name, ?bool $enabled, ?array $serviceAccountJSON, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $providerId, string $name, ?bool $enabled, array|string|null $serviceAccountJSON, Event $queueForEvents, Database $dbForProject, Response $response) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
@@ -1518,17 +1735,25 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
}
if (!\is_null($serviceAccountJSON)) {
$provider->setAttribute('credentials', ['serviceAccountJSON' => $serviceAccountJSON]);
$serviceAccountJSON = \is_string($serviceAccountJSON)
? \json_decode($serviceAccountJSON, true)
: $serviceAccountJSON;
$provider->setAttribute('credentials', [
'serviceAccountJSON' => $serviceAccountJSON
]);
}
if ($enabled === true || $enabled === false) {
if ($enabled === true && \array_key_exists('serviceAccountJSON', $provider->getAttribute('credentials'))) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (\array_key_exists('serviceAccountJSON', $provider->getAttribute('credentials'))) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1601,20 +1826,21 @@ App::patch('/v1/messaging/providers/apns/:providerId')
$provider->setAttribute('credentials', $credentials);
if ($enabled === true || $enabled === false) {
if (
$enabled === true
&& \array_key_exists('authKey', $credentials)
&& \array_key_exists('authKeyId', $credentials)
&& \array_key_exists('teamId', $credentials)
&& \array_key_exists('bundleId', $credentials)
) {
$enabled = true;
if (!\is_null($enabled)) {
if ($enabled) {
if (
\array_key_exists('authKey', $credentials) &&
\array_key_exists('authKeyId', $credentials) &&
\array_key_exists('teamId', $credentials) &&
\array_key_exists('bundleId', $credentials)
) {
$provider->setAttribute('enabled', true);
} else {
throw new Exception(Exception::PROVIDER_MISSING_CREDENTIALS);
}
} else {
$enabled = false;
$provider->setAttribute('enabled', false);
}
$provider->setAttribute('enabled', $enabled);
}
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
@@ -1677,17 +1903,15 @@ App::post('/v1/messaging/topics')
->label('sdk.response.model', Response::MODEL_TOPIC)
->param('topicId', '', new CustomId(), 'Topic ID. Choose a custom Topic ID or a new Topic ID.')
->param('name', '', new Text(128), 'Topic Name.')
->param('description', '', new Text(2048), 'Topic Description.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, string $name, string $description, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $topicId, string $name, Event $queueForEvents, Database $dbForProject, Response $response) {
$topicId = $topicId == 'unique()' ? ID::unique() : $topicId;
$topic = new Document([
'$id' => $topicId,
'name' => $name,
'description' => $description
]);
try {
@@ -1727,7 +1951,7 @@ App::get('/v1/messaging/topics')
}
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -1874,11 +2098,10 @@ App::patch('/v1/messaging/topics/:topicId')
->label('sdk.response.model', Response::MODEL_TOPIC)
->param('topicId', '', new UID(), 'Topic ID.')
->param('name', '', new Text(128), 'Topic Name.', true)
->param('description', '', new Text(2048), 'Topic Description.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, string $name, string $description, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $topicId, string $name, Event $queueForEvents, Database $dbForProject, Response $response) {
$topic = $dbForProject->getDocument('topics', $topicId);
if ($topic->isEmpty()) {
@@ -1889,10 +2112,6 @@ App::patch('/v1/messaging/topics/:topicId')
$topic->setAttribute('name', $name);
}
if (!empty($description)) {
$topic->setAttribute('description', $description);
}
$topic = $dbForProject->updateDocument('topics', $topicId, $topic);
$queueForEvents
@@ -2054,7 +2273,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
\array_push($queries, Query::equal('topicInternalId', [$topic->getInternalId()]));
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -2277,7 +2496,6 @@ App::post('/v1/messaging/messages/email')
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('cc', [], new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
->param('bcc', [], new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
->param('description', '', new Text(256), 'Description for message.', true)
->param('status', MessageStatus::DRAFT, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
->param('html', false, new Boolean(), 'Is content of type HTML', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
@@ -2287,7 +2505,7 @@ App::post('/v1/messaging/messages/email')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, string $description, string $status, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, string $status, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$messageId = $messageId == 'unique()'
? ID::unique()
: $messageId;
@@ -2326,7 +2544,6 @@ App::post('/v1/messaging/messages/email')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'scheduledAt' => $scheduledAt,
'data' => [
'subject' => $subject,
@@ -2396,7 +2613,6 @@ App::post('/v1/messaging/messages/sms')
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('description', '', new Text(256), 'Description for Message.', true)
->param('status', MessageStatus::DRAFT, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('queueForEvents')
@@ -2405,7 +2621,7 @@ App::post('/v1/messaging/messages/sms')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, string $content, array $topics, array $users, array $targets, string $description, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, string $content, array $topics, array $users, array $targets, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$messageId = $messageId == 'unique()'
? ID::unique()
: $messageId;
@@ -2442,7 +2658,6 @@ App::post('/v1/messaging/messages/sms')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'data' => [
'content' => $content,
],
@@ -2508,7 +2723,6 @@ App::post('/v1/messaging/messages/push')
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('description', '', new Text(256), 'Description for Message.', true)
->param('data', null, new JSON(), 'Additional Data for push notification.', true)
->param('action', '', new Text(256), 'Action for push notification.', true)
->param('icon', '', new Text(256), 'Icon for push notification. Available only for Android and Web Platform.', true)
@@ -2524,7 +2738,7 @@ App::post('/v1/messaging/messages/push')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, string $description, ?array $data, string $action, string $icon, string $sound, string $color, string $tag, string $badge, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $icon, string $sound, string $color, string $tag, string $badge, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$messageId = $messageId == 'unique()'
? ID::unique()
: $messageId;
@@ -2571,7 +2785,6 @@ App::post('/v1/messaging/messages/push')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'scheduledAt' => $scheduledAt,
'data' => $pushData,
'status' => $status,
@@ -2639,7 +2852,7 @@ App::get('/v1/messaging/messages')
}
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -2782,7 +2995,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
$queries[] = Query::equal('$id', $targetIDs);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -2845,7 +3058,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('subject', null, new Text(998), 'Email Subject.', true)
->param('description', null, new Text(256), 'Description for Message.', true)
->param('content', null, new Text(64230), 'Email Content.', true)
->param('status', MessageStatus::DRAFT, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
->param('html', null, new Boolean(), 'Is content of type HTML', true)
@@ -2858,7 +3070,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $subject, ?string $description, ?string $content, ?string $status, ?bool $html, ?array $cc, ?array $bcc, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $subject, ?string $content, ?string $status, ?bool $html, ?array $cc, ?array $bcc, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$message = $dbForProject->getDocument('messages', $messageId);
if ($message->isEmpty()) {
@@ -2909,10 +3121,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
$message->setAttribute('data', $data);
if (!\is_null($description)) {
$message->setAttribute('description', $description);
}
if (!\is_null($status)) {
$message->setAttribute('status', $status);
}
@@ -2983,7 +3191,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('description', null, new Text(256), 'Description for Message.', true)
->param('content', null, new Text(64230), 'Email Content.', true)
->param('status', null, new WhiteList(['draft', 'cancelled', 'processing']), 'Message Status. Value must be either draft or cancelled or processing.', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
@@ -2993,7 +3200,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $description, ?string $content, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $content, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$message = $dbForProject->getDocument('messages', $messageId);
if ($message->isEmpty()) {
@@ -3032,10 +3239,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
$message->setAttribute('status', $status);
}
if (!\is_null($description)) {
$message->setAttribute('description', $description);
}
if (!\is_null($scheduledAt)) {
if (\is_null($message->getAttribute(('scheduleId')))) {
$schedule = $dbForConsole->createDocument('schedules', new Document([
@@ -3102,7 +3305,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('description', null, new Text(256), 'Description for Message.', true)
->param('title', null, new Text(256), 'Title for push notification.', true)
->param('body', null, new Text(64230), 'Body for push notification.', true)
->param('data', null, new JSON(), 'Additional Data for push notification.', true)
@@ -3120,7 +3322,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
->inject('project')
->inject('queueForMessaging')
->inject('response')
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $description, ?string $title, ?string $body, ?array $data, ?string $action, ?string $icon, ?string $sound, ?string $color, ?string $tag, ?int $badge, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $title, ?string $body, ?array $data, ?string $action, ?string $icon, ?string $sound, ?string $color, ?string $tag, ?int $badge, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
$message = $dbForProject->getDocument('messages', $messageId);
if ($message->isEmpty()) {
@@ -3191,10 +3393,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
$message->setAttribute('status', $status);
}
if (!\is_null($description)) {
$message->setAttribute('description', $description);
}
if (!\is_null($scheduledAt)) {
if (\is_null($message->getAttribute(('scheduleId')))) {
$schedule = $dbForConsole->createDocument('schedules', new Document([
@@ -3242,3 +3440,59 @@ App::patch('/v1/messaging/messages/push/:messageId')
$response
->dynamic($message, Response::MODEL_MESSAGE);
});
App::delete('/v1/messaging/messages/:messageId')
->desc('Delete a message')
->groups(['api', 'messaging'])
->label('audits.event', 'message.delete')
->label('audits.resource', 'message/{request.route.messageId}')
->label('event', 'messages.[messageId].delete')
->label('scope', 'messages.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/messaging/delete-message.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('messageId', '', new UID(), 'Message ID.')
->inject('dbForProject')
->inject('dbForConsole')
->inject('response')
->action(function (string $messageId, Database $dbForProject, Database $dbForConsole, Response $response) {
$message = $dbForProject->getDocument('messages', $messageId);
if ($message->isEmpty()) {
throw new Exception(Exception::MESSAGE_NOT_FOUND);
}
switch ($message->getAttribute('status')) {
case MessageStatus::PROCESSING:
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
case MessageStatus::SCHEDULED:
$scheduleId = $message->getAttribute('scheduleId');
$scheduledAt = $message->getAttribute('scheduledAt');
$now = DateTime::now();
$scheduledDate = DateTime::formatTz($scheduledAt);
if ($now > $scheduledDate) {
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
}
if (!empty($scheduleId)) {
try {
$dbForConsole->deleteDocument('schedules', $scheduleId);
} catch (Exception) {
// Ignore
}
}
break;
default:
break;
}
$dbForProject->deleteDocument('messages', $message->getId());
$response->noContent();
});
+1 -1
View File
@@ -392,7 +392,7 @@ App::get('/v1/migrations')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
+11 -11
View File
@@ -249,7 +249,7 @@ App::get('/v1/projects')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -825,7 +825,7 @@ App::post('/v1/projects/:projectId/webhooks')
$webhook = $dbForConsole->createDocument('webhooks', $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -952,7 +952,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
}
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
@@ -991,7 +991,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
$webhook->setAttribute('signatureKey', \bin2hex(\random_bytes(64)));
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
@@ -1028,7 +1028,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
$dbForConsole->deleteDocument('webhooks', $webhook->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->noContent();
});
@@ -1078,7 +1078,7 @@ App::post('/v1/projects/:projectId/keys')
$key = $dbForConsole->createDocument('keys', $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -1192,7 +1192,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->dynamic($key, Response::MODEL_KEY);
});
@@ -1229,7 +1229,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
$dbForConsole->deleteDocument('keys', $key->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->noContent();
});
@@ -1279,7 +1279,7 @@ App::post('/v1/projects/:projectId/platforms')
$platform = $dbForConsole->createDocument('platforms', $platform);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -1394,7 +1394,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
$dbForConsole->updateDocument('platforms', $platform->getId(), $platform);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->dynamic($platform, Response::MODEL_PLATFORM);
});
@@ -1431,7 +1431,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
$dbForConsole->deleteDocument('platforms', $platformId);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response->noContent();
});
+1 -1
View File
@@ -164,7 +164,7 @@ App::get('/v1/proxy/rules')
$queries[] = Query::equal('projectInternalId', [$project->getInternalId()]);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
+8 -4
View File
@@ -169,7 +169,7 @@ App::get('/v1/storage/buckets')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -451,7 +451,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
$idValidator = new UID();
if (!$idValidator->isValid($request->getHeader('x-appwrite-id'))) {
if (!$idValidator->isValid($fileId)) {
throw new Exception(Exception::STORAGE_INVALID_APPWRITE_ID);
}
@@ -745,7 +745,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -963,7 +963,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
break;
}
$image = new Image($source);
try {
$image = new Image($source);
} catch (ImagickException $e) {
throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED, $e->getMessage());
}
$image->crop((int) $width, (int) $height, $gravity);
+8 -8
View File
@@ -114,7 +114,7 @@ App::post('/v1/teams')
]);
$membership = $dbForProject->createDocument('memberships', $membership);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
}
$queueForEvents->setParam('teamId', $team->getId());
@@ -154,7 +154,7 @@ App::get('/v1/teams')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -537,7 +537,7 @@ App::post('/v1/teams/:teamId/memberships')
$team->setAttribute('total', $team->getAttribute('total', 0) + 1);
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
$dbForProject->deleteCachedDocument('users', $invitee->getId());
$dbForProject->purgeCachedDocument('users', $invitee->getId());
} else {
try {
$membership = $dbForProject->createDocument('memberships', $membership);
@@ -710,7 +710,7 @@ App::get('/v1/teams/:teamId/memberships')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -857,7 +857,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
/**
* Replace membership on profile
*/
$dbForProject->deleteCachedDocument('users', $profile->getId());
$dbForProject->purgeCachedDocument('users', $profile->getId());
$queueForEvents
->setParam('teamId', $team->getId())
@@ -974,13 +974,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
Permission::delete(Role::user($user->getId())),
]));
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
Authorization::setRole(Role::user($userId)->toString());
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
@@ -1059,7 +1059,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB');
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0));
+19 -15
View File
@@ -145,7 +145,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
}
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
@@ -508,7 +508,7 @@ App::post('/v1/users/:userId/targets')
} catch (Duplicate) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -544,7 +544,7 @@ App::get('/v1/users')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -839,7 +839,7 @@ App::get('/v1/users/:userId/targets')
$queries[] = Query::equal('userId', [$userId]);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
@@ -884,7 +884,7 @@ App::get('/v1/users/identities')
// Get cursor document if there was a cursor query
$cursor = \array_filter($queries, function ($query) {
return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
});
$cursor = reset($cursor);
if ($cursor) {
@@ -958,7 +958,7 @@ App::put('/v1/users/:userId/labels')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of user labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_LABELS_SIZE), 'Array of user labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_LABELS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -1184,7 +1184,7 @@ App::patch('/v1/users/:userId/email')
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
@@ -1246,7 +1246,7 @@ App::patch('/v1/users/:userId/phone')
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $number));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
}
@@ -1408,7 +1408,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
}
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -1657,7 +1657,7 @@ App::post('/v1/users/:userId/tokens')
]);
$token = $dbForProject->createDocument('tokens', $token);
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$token->setAttribute('secret', $secret);
@@ -1704,7 +1704,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
}
$dbForProject->deleteDocument('sessions', $session->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -1747,7 +1747,7 @@ App::delete('/v1/users/:userId/sessions')
//TODO: fix this
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -1815,10 +1815,10 @@ App::delete('/v1/users/:userId/targets/:targetId')
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->inject('queueForEvents')
->inject('queueForDeletes')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Event $queueForEvents, Response $response, Database $dbForProject) {
->action(function (string $userId, string $targetId, Event $queueForEvents, Delete $queueForDeletes, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
@@ -1836,7 +1836,11 @@ App::delete('/v1/users/:userId/targets/:targetId')
}
$dbForProject->deleteDocument('targets', $target->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForDeletes
->setType(DELETE_TYPE_TARGET)
->setDocument($target);
$queueForEvents
->setParam('userId', $user->getId())
+1 -1
View File
@@ -978,7 +978,7 @@ App::get('/v1/vcs/installations')
}
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
+3 -3
View File
@@ -510,7 +510,7 @@ App::init()
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
@@ -524,7 +524,7 @@ App::init()
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
}
}
@@ -753,7 +753,7 @@ App::error()
'code' => $code,
'file' => $file,
'line' => $line,
'trace' => $trace,
'trace' => \json_encode($trace, JSON_UNESCAPED_UNICODE) === false ? [] : $trace, // check for failing encode
'version' => $version,
'type' => $type,
] : [
+1 -1
View File
@@ -410,7 +410,7 @@ App::shutdown()
$session = array_shift($sessions);
$dbForProject->deleteDocument('sessions', $session->getId());
}
$dbForProject->deleteCachedDocument('users', $userId);
$dbForProject->purgeCachedDocument('users', $userId);
});
App::shutdown()
+1 -1
View File
@@ -148,7 +148,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$dbForConsole->createCollection($key, $attributes, $indexes);
}
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDefaultDatabase(), 'bucket_1')) {
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) {
Console::success('[Setup] - Creating default bucket...');
$dbForConsole->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
+16 -20
View File
@@ -34,6 +34,7 @@ use Appwrite\Network\Validator\Origin;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\URL\URL as AppwriteURL;
use Utopia\App;
use Utopia\Database\Adapter\SQL;
use Utopia\Logger\Logger;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Cache;
@@ -99,6 +100,7 @@ const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
@@ -174,6 +176,7 @@ const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_SCHEDULES = 'schedules';
const DELETE_TYPE_TOPIC = 'topic';
const DELETE_TYPE_TARGET = 'target';
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
// Mail Types
const MAIL_TYPE_VERIFICATION = 'verification';
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
@@ -817,10 +820,10 @@ $register->set('pools', function () {
$resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
// No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
));
@@ -842,12 +845,10 @@ $register->set('pools', function () {
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme");
break;
}
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
// Get Adapter
$adapter = null;
switch ($type) {
case 'database':
$adapter = match ($dsn->getScheme()) {
@@ -856,7 +857,7 @@ $register->set('pools', function () {
default => null
};
$adapter->setDefaultDatabase($dsn->getPath());
$adapter->setDatabase($dsn->getPath());
break;
case 'pubsub':
$adapter = $resource();
@@ -876,7 +877,6 @@ $register->set('pools', function () {
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
break;
}
return $adapter;
@@ -893,22 +893,18 @@ $register->set('pools', function () {
$register->set('db', function () {
// This is usually for our workers or CLI commands scope
$dbHost = App::getEnv('_APP_DB_HOST', '');
$dbPort = App::getEnv('_APP_DB_PORT', '');
$dbUser = App::getEnv('_APP_DB_USER', '');
$dbPass = App::getEnv('_APP_DB_PASS', '');
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
$dbHost = App::getEnv('_APP_DB_HOST', '');
$dbPort = App::getEnv('_APP_DB_PORT', '');
$dbUser = App::getEnv('_APP_DB_USER', '');
$dbPass = App::getEnv('_APP_DB_PASS', '');
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true,
));
return $pdo;
return new PDO(
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
$dbUser,
$dbPass,
SQL::getPDOAttributes()
);
});
$register->set('smtp', function () {
+8 -8
View File
@@ -43,20 +43,20 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.13.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.33.*",
"utopia-php/abuse": "0.36.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.35.*",
"utopia-php/audit": "0.38.*",
"utopia-php/cache": "0.9.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.45.*",
"utopia-php/database": "0.48.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.1.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/image": "0.5.*",
"utopia-php/image": "0.6.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.3.*",
"utopia-php/messaging": "0.8.*",
"utopia-php/messaging": "0.9.*",
"utopia-php/migration": "0.3.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.5.*",
@@ -75,7 +75,7 @@
"adhocore/jwt": "1.1.2",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.7.1"
"league/csv": "^9.14"
},
"repositories": [
{
@@ -97,7 +97,7 @@
},
"config": {
"platform": {
"php": "8.0"
"php": "8.2"
}
}
}
Generated
+73 -72
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6ccadcec8172eb800888228b5f653f08",
"content-hash": "3b43bf6f0fca50a3a2834e1bbaa90d63",
"packages": [
{
"name": "adhocore/jwt",
@@ -530,34 +530,39 @@
},
{
"name": "league/csv",
"version": "9.7.1",
"version": "9.14.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1"
"reference": "34bf0df7340b60824b9449b5c526fcc3325070d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1",
"reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/34bf0df7340b60824b9449b5c526fcc3325070d5",
"reference": "34bf0df7340b60824b9449b5c526fcc3325070d5",
"shasum": ""
},
"require": {
"ext-filter": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": "^7.3 || ^8.0"
"php": "^8.1.2"
},
"require-dev": {
"ext-curl": "*",
"doctrine/collections": "^2.1.4",
"ext-dom": "*",
"friendsofphp/php-cs-fixer": "^2.16",
"phpstan/phpstan": "^0.12.0",
"phpstan/phpstan-phpunit": "^0.12.0",
"phpstan/phpstan-strict-rules": "^0.12.0",
"phpunit/phpunit": "^9.5"
"ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^v3.22.0",
"phpbench/phpbench": "^1.2.15",
"phpstan/phpstan": "^1.10.50",
"phpstan/phpstan-deprecation-rules": "^1.1.4",
"phpstan/phpstan-phpunit": "^1.3.15",
"phpstan/phpstan-strict-rules": "^1.5.2",
"phpunit/phpunit": "^10.5.3",
"symfony/var-dumper": "^6.4.0"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes",
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
},
"type": "library",
@@ -587,7 +592,7 @@
}
],
"description": "CSV data manipulation made easy in PHP",
"homepage": "http://csv.thephpleague.com",
"homepage": "https://csv.thephpleague.com",
"keywords": [
"convert",
"csv",
@@ -610,7 +615,7 @@
"type": "github"
}
],
"time": "2021-04-17T16:32:08+00:00"
"time": "2023-12-29T07:34:53+00:00"
},
{
"name": "matomo/device-detector",
@@ -1246,23 +1251,23 @@
},
{
"name": "utopia-php/abuse",
"version": "0.33.0",
"version": "0.36.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "1ba8d5f2793885cbf779e3b5b9d886968af43d2c"
"reference": "d3d09b4fa0db75935110714ad4b2a87f3ace31ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/1ba8d5f2793885cbf779e3b5b9d886968af43d2c",
"reference": "1ba8d5f2793885cbf779e3b5b9d886968af43d2c",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/d3d09b4fa0db75935110714ad4b2a87f3ace31ed",
"reference": "d3d09b4fa0db75935110714ad4b2a87f3ace31ed",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": "0.45.*"
"utopia-php/database": "0.48.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1289,9 +1294,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.33.0"
"source": "https://github.com/utopia-php/abuse/tree/0.36.0"
},
"time": "2023-11-01T08:51:33+00:00"
"time": "2024-01-19T09:32:56+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1341,21 +1346,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.35.0",
"version": "0.38.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "ed9366ef05556da040de7a8b570f4160c7d8ea4a"
"reference": "a9067f4af76e8787f1d29850a8ec94fc32bb6539"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/ed9366ef05556da040de7a8b570f4160c7d8ea4a",
"reference": "ed9366ef05556da040de7a8b570f4160c7d8ea4a",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/a9067f4af76e8787f1d29850a8ec94fc32bb6539",
"reference": "a9067f4af76e8787f1d29850a8ec94fc32bb6539",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.45.*"
"utopia-php/database": "0.48.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1382,9 +1387,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.35.0"
"source": "https://github.com/utopia-php/audit/tree/0.38.0"
},
"time": "2023-11-01T08:51:29+00:00"
"time": "2024-01-19T09:33:05+00:00"
},
{
"name": "utopia-php/cache",
@@ -1538,16 +1543,16 @@
},
{
"name": "utopia-php/database",
"version": "0.45.5",
"version": "0.48.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "0b66a017f817a910acb83e6aea92bccea9571fe6"
"reference": "2651f41b9d3909dc123d26becfb6a3a44fb63077"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/0b66a017f817a910acb83e6aea92bccea9571fe6",
"reference": "0b66a017f817a910acb83e6aea92bccea9571fe6",
"url": "https://api.github.com/repos/utopia-php/database/zipball/2651f41b9d3909dc123d26becfb6a3a44fb63077",
"reference": "2651f41b9d3909dc123d26becfb6a3a44fb63077",
"shasum": ""
},
"require": {
@@ -1588,9 +1593,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.45.5"
"source": "https://github.com/utopia-php/database/tree/0.48.0"
},
"time": "2024-01-08T17:08:15+00:00"
"time": "2024-01-19T08:17:22+00:00"
},
{
"name": "utopia-php/domains",
@@ -1654,16 +1659,16 @@
},
{
"name": "utopia-php/dsn",
"version": "0.1.0",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dsn.git",
"reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea"
"reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dsn/zipball/17a5935eab1b89fb4b95600db50a1b6d5faa6cea",
"reference": "17a5935eab1b89fb4b95600db50a1b6d5faa6cea",
"url": "https://api.github.com/repos/utopia-php/dsn/zipball/c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7",
"reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7",
"shasum": ""
},
"require": {
@@ -1695,22 +1700,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/dsn/issues",
"source": "https://github.com/utopia-php/dsn/tree/0.1.0"
"source": "https://github.com/utopia-php/dsn/tree/0.2.0"
},
"time": "2022-10-26T10:06:20+00:00"
"time": "2023-11-02T12:01:43+00:00"
},
{
"name": "utopia-php/framework",
"version": "0.33.1",
"version": "0.33.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "b745607aa1875554a0ad52e28f6db918da1ce11c"
"reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/b745607aa1875554a0ad52e28f6db918da1ce11c",
"reference": "b745607aa1875554a0ad52e28f6db918da1ce11c",
"url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3",
"reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3",
"shasum": ""
},
"require": {
@@ -1740,22 +1745,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.1"
"source": "https://github.com/utopia-php/http/tree/0.33.2"
},
"time": "2024-01-17T16:48:32+00:00"
"time": "2024-01-31T10:35:59+00:00"
},
{
"name": "utopia-php/image",
"version": "0.5.4",
"version": "0.6.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/image.git",
"reference": "ca5f436f9aa22dedaa6648f24f3687733808e336"
"reference": "88f7209172bdabd81e76ac981c95fac117dc6e08"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/image/zipball/ca5f436f9aa22dedaa6648f24f3687733808e336",
"reference": "ca5f436f9aa22dedaa6648f24f3687733808e336",
"url": "https://api.github.com/repos/utopia-php/image/zipball/88f7209172bdabd81e76ac981c95fac117dc6e08",
"reference": "88f7209172bdabd81e76ac981c95fac117dc6e08",
"shasum": ""
},
"require": {
@@ -1763,6 +1768,8 @@
"php": ">=8.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpstan/phpstan": "1.9.x-dev",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.13.1"
},
@@ -1776,12 +1783,6 @@
"license": [
"MIT"
],
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
}
],
"description": "A simple Image manipulation library",
"keywords": [
"framework",
@@ -1792,9 +1793,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/image/issues",
"source": "https://github.com/utopia-php/image/tree/0.5.4"
"source": "https://github.com/utopia-php/image/tree/0.6.0"
},
"time": "2022-05-11T12:30:41+00:00"
"time": "2024-01-24T06:59:44+00:00"
},
{
"name": "utopia-php/locale",
@@ -1902,16 +1903,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.8.1",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "bfb5014d3a8752901e50da1ae21bf309a6af5006"
"reference": "df54ba51570e886724590edeb03dbd455bb0464d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/bfb5014d3a8752901e50da1ae21bf309a6af5006",
"reference": "bfb5014d3a8752901e50da1ae21bf309a6af5006",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/df54ba51570e886724590edeb03dbd455bb0464d",
"reference": "df54ba51570e886724590edeb03dbd455bb0464d",
"shasum": ""
},
"require": {
@@ -1946,9 +1947,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.8.1"
"source": "https://github.com/utopia-php/messaging/tree/0.9.0"
},
"time": "2024-01-10T23:55:03+00:00"
"time": "2024-01-31T11:51:27+00:00"
},
{
"name": "utopia-php/migration",
@@ -2821,16 +2822,16 @@
},
{
"name": "doctrine/deprecations",
"version": "1.1.2",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931"
"reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931",
"reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
"reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
"shasum": ""
},
"require": {
@@ -2862,9 +2863,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/1.1.2"
"source": "https://github.com/doctrine/deprecations/tree/1.1.3"
},
"time": "2023-09-27T20:04:15+00:00"
"time": "2024-01-30T19:34:25+00:00"
},
{
"name": "doctrine/instantiator",
@@ -5520,7 +5521,7 @@
"ext-fileinfo": "*"
},
"platform-overrides": {
"php": "8.0"
"php": "8.2"
},
"plugin-api-version": "2.3.0"
}
+6
View File
@@ -0,0 +1,6 @@
zend_extension=xdebug
[xdebug]
xdebug.mode=develop,debug
xdebug.client_host=host.docker.internal
xdebug.start_with_request=yes
-4
View File
@@ -1,4 +0,0 @@
<?php
echo 'execute init_file success' . PHP_EOL;
Yasd\Api\setRemoteHost('host.docker.internal'); //Set your development machine's IP
+1 -1
View File
@@ -885,7 +885,7 @@ services:
- OPR_PROXY_HEALTHCHECK=enabled
mariadb:
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<<: *x-logging
networks:
+1
View File
@@ -0,0 +1 @@
Delete a message by its unique ID.
+2 -1
View File
@@ -60,7 +60,8 @@ class Scryptmodified extends Hash
$saltBytes = \base64_decode($options['salt']);
$saltSeparatorBytes = \base64_decode($options['saltSeparator']);
$derivedKey = \scrypt(\utf8_encode($password), $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64);
$password = mb_convert_encoding($password, 'UTF-8');
$derivedKey = \scrypt($password, $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64);
$derivedKey = \hex2bin($derivedKey);
return $derivedKey;
+4 -4
View File
@@ -43,10 +43,10 @@ class PersonalData extends Password
if (!$this->strict) {
$password = strtolower($password);
$this->userId = strtolower($this->userId);
$this->email = strtolower($this->email);
$this->name = strtolower($this->name);
$this->phone = strtolower($this->phone);
$this->userId = strtolower($this->userId ?? '');
$this->email = strtolower($this->email ?? '');
$this->name = strtolower($this->name ?? '');
$this->phone = strtolower($this->phone ?? '');
}
if ($this->userId && strpos($password, $this->userId) !== false) {
+1 -1
View File
@@ -249,7 +249,7 @@ class Exception extends \Exception
public const PROVIDER_NOT_FOUND = 'provider_not_found';
public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists';
public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type';
public const PROVIDER_INTERNAL_UPDATE_DISABLED = 'provider_internal_update_disabled';
public const PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials';
/** Topic */
public const TOPIC_NOT_FOUND = 'topic_not_found';
+1 -1
View File
@@ -397,7 +397,7 @@ abstract class Migration
*/
protected function changeAttributeInternalType(string $collection, string $attribute, string $type): void
{
$stmt = $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY `$attribute` $type;");
$stmt = $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY `$attribute` $type;");
try {
$stmt->execute();
+11 -11
View File
@@ -295,7 +295,7 @@ class V15 extends Migration
protected function removeWritePermissions(string $table): void
{
try {
$this->pdo->prepare("DELETE FROM `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _type = 'write'")->execute();
$this->pdo->prepare("DELETE FROM `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _type = 'write'")->execute();
} catch (\Throwable $th) {
Console::warning("Remove 'write' permissions from {$table}: {$th->getMessage()}");
}
@@ -311,7 +311,7 @@ class V15 extends Migration
*/
protected function getSQLColumnTypes(string $table): array
{
$query = $this->pdo->prepare("SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '_{$this->project->getInternalId()}_{$table}' AND table_schema = '{$this->projectDB->getDefaultDatabase()}'");
$query = $this->pdo->prepare("SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '_{$this->project->getInternalId()}_{$table}' AND table_schema = '{$this->projectDB->getDatabase()}'");
$query->execute();
return array_reduce($query->fetchAll(), function (array $carry, array $item) {
@@ -333,8 +333,8 @@ class V15 extends Migration
if ($columns[$attribute] === 'int') {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} VARCHAR(64)")->execute();
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` SET {$attribute} = IF({$attribute} = 0, NULL, FROM_UNIXTIME({$attribute}))")->execute();
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} VARCHAR(64)")->execute();
$this->pdo->prepare("UPDATE `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` SET {$attribute} = IF({$attribute} = 0, NULL, FROM_UNIXTIME({$attribute}))")->execute();
$columns[$attribute] = 'varchar';
} catch (\Throwable $th) {
Console::warning($th->getMessage());
@@ -343,7 +343,7 @@ class V15 extends Migration
if ($columns[$attribute] === 'varchar') {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} DATETIME(3)")->execute();
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} DATETIME(3)")->execute();
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
@@ -372,7 +372,7 @@ class V15 extends Migration
}
}
$this->projectDB->deleteCachedCollection($table);
$this->projectDB->purgeCachedCollection($table);
}
/**
@@ -389,7 +389,7 @@ class V15 extends Migration
if (!array_key_exists('_permissions', $columns)) {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` ADD `_permissions` MEDIUMTEXT DEFAULT NULL")->execute();
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` ADD `_permissions` MEDIUMTEXT DEFAULT NULL")->execute();
} catch (\Throwable $th) {
Console::warning("Add '_permissions' column to '{$table}': {$th->getMessage()}");
}
@@ -410,7 +410,7 @@ class V15 extends Migration
{
$table ??= $document->getCollection();
$query = $this->pdo->prepare("SELECT * FROM `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _document = '{$document->getId()}'");
$query = $this->pdo->prepare("SELECT * FROM `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _document = '{$document->getId()}'");
$query->execute();
$results = $query->fetchAll();
$permissions = [];
@@ -479,7 +479,7 @@ class V15 extends Migration
$this->createCollection('cache');
Console::log('Created new Collection "variables" collection');
$this->createCollection('variables');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'abuse':
@@ -1472,9 +1472,9 @@ class V15 extends Migration
$from = $this->pdo->quote($from);
$to = $this->pdo->quote($to);
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_stats` SET metric = {$to} WHERE metric = {$from}")->execute();
$this->pdo->prepare("UPDATE `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_stats` SET metric = {$to} WHERE metric = {$from}")->execute();
} catch (\Throwable $th) {
Console::warning("Migrating steps from {$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_stats:" . $th->getMessage());
Console::warning("Migrating steps from {$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}_stats:" . $th->getMessage());
}
}
+16 -16
View File
@@ -48,7 +48,7 @@ class V17 extends Migration
try {
$this->projectDB->updateAttribute($id, 'mimeType', Database::VAR_STRING, 255, true, false);
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'mimeType' from {$id}: {$th->getMessage()}");
}
@@ -76,7 +76,7 @@ class V17 extends Migration
* Create 'size' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'size');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'size' from {$id}: {$th->getMessage()}");
}
@@ -88,7 +88,7 @@ class V17 extends Migration
* Update 'mimeType' attribute size (127->255)
*/
$this->projectDB->updateAttribute($id, 'mimeType', Database::VAR_STRING, 255, true, false);
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'mimeType' from {$id}: {$th->getMessage()}");
}
@@ -98,7 +98,7 @@ class V17 extends Migration
* Create 'bucketInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'bucketInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
}
@@ -110,7 +110,7 @@ class V17 extends Migration
* Delete 'endTime' attribute (use startTime+duration if needed)
*/
$this->projectDB->deleteAttribute($id, 'endTime');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'endTime' from {$id}: {$th->getMessage()}");
}
@@ -120,7 +120,7 @@ class V17 extends Migration
* Rename 'outputPath' to 'path'
*/
$this->projectDB->renameAttribute($id, 'outputPath', 'path');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'path' from {$id}: {$th->getMessage()}");
}
@@ -130,7 +130,7 @@ class V17 extends Migration
* Create 'deploymentInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'deploymentInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
}
@@ -142,7 +142,7 @@ class V17 extends Migration
* Delete 'type' attribute
*/
$this->projectDB->deleteAttribute($id, 'type');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'type' from {$id}: {$th->getMessage()}");
}
@@ -154,7 +154,7 @@ class V17 extends Migration
* Create 'resourceInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'resourceInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'resourceInternalId' from {$id}: {$th->getMessage()}");
}
@@ -166,7 +166,7 @@ class V17 extends Migration
* Create 'deploymentInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'deploymentInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
}
@@ -176,7 +176,7 @@ class V17 extends Migration
* Create 'scheduleInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'scheduleInternalId' from {$id}: {$th->getMessage()}");
}
@@ -186,7 +186,7 @@ class V17 extends Migration
* Delete 'scheduleUpdatedAt' attribute
*/
$this->projectDB->deleteAttribute($id, 'scheduleUpdatedAt');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'scheduleUpdatedAt' from {$id}: {$th->getMessage()}");
}
@@ -198,7 +198,7 @@ class V17 extends Migration
* Create 'resourceInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'resourceInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'resourceInternalId' from {$id}: {$th->getMessage()}");
}
@@ -208,7 +208,7 @@ class V17 extends Migration
* Create 'buildInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'buildInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'buildInternalId' from {$id}: {$th->getMessage()}");
}
@@ -220,7 +220,7 @@ class V17 extends Migration
* Create 'functionInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'functionInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'functionInternalId' from {$id}: {$th->getMessage()}");
}
@@ -230,7 +230,7 @@ class V17 extends Migration
* Create 'deploymentInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'deploymentInternalId');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
}
+5 -5
View File
@@ -106,7 +106,7 @@ class V18 extends Migration
* Create 'passwordHistory' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'passwordHistory');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'passwordHistory' from {$id}: {$th->getMessage()}");
}
@@ -117,7 +117,7 @@ class V18 extends Migration
* Create 'prefs' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'prefs');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'prefs' from {$id}: {$th->getMessage()}");
}
@@ -128,7 +128,7 @@ class V18 extends Migration
* Create 'options' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'options');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'options' from {$id}: {$th->getMessage()}");
}
@@ -244,7 +244,7 @@ class V18 extends Migration
/**
* Create 'documentSecurity' column
*/
$this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` ADD COLUMN IF NOT EXISTS documentSecurity TINYINT(1);")->execute();
$this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}__metadata` ADD COLUMN IF NOT EXISTS documentSecurity TINYINT(1);")->execute();
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
@@ -253,7 +253,7 @@ class V18 extends Migration
/**
* Set 'documentSecurity' column to 1 if NULL
*/
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` SET documentSecurity = 1 WHERE documentSecurity IS NULL")->execute();
$this->pdo->prepare("UPDATE `{$this->projectDB->getDatabase()}`.`_{$this->project->getInternalId()}__metadata` SET documentSecurity = 1 WHERE documentSecurity IS NULL")->execute();
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
+23 -23
View File
@@ -56,7 +56,7 @@ class V19 extends Migration
protected function migrateDomains(): void
{
if ($this->consoleDB->exists($this->consoleDB->getDefaultDatabase(), 'domains')) {
if ($this->consoleDB->exists($this->consoleDB->getDatabase(), 'domains')) {
foreach ($this->documentsIterator('domains') as $domain) {
$status = 'created';
if ($domain->getAttribute('verification', false)) {
@@ -106,7 +106,7 @@ class V19 extends Migration
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'bucketInternalId', 'files');
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
} catch (\Throwable $th) {
Console::warning("'bucketInternalId' from {$id}: {$th->getMessage()}");
}
@@ -166,7 +166,7 @@ class V19 extends Migration
Console::warning("'error' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'buckets':
@@ -194,7 +194,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'builds':
@@ -217,7 +217,7 @@ class V19 extends Migration
Console::warning("'path' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'certificates':
@@ -233,7 +233,7 @@ class V19 extends Migration
Console::warning("'logs' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'databases':
@@ -243,7 +243,7 @@ class V19 extends Migration
Console::warning("'enabled' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'deployments':
@@ -306,7 +306,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'executions':
@@ -367,7 +367,7 @@ class V19 extends Migration
Console::warning("'_key_responseStatusCode' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'files':
@@ -394,7 +394,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'functions':
@@ -456,7 +456,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'memberships':
@@ -466,7 +466,7 @@ class V19 extends Migration
Console::warning("'teamInternalId' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
// Intentional fall through to update memberships.userInternalId
case 'sessions':
@@ -477,7 +477,7 @@ class V19 extends Migration
Console::warning("'userInternalId' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'domains':
@@ -490,7 +490,7 @@ class V19 extends Migration
Console::warning("'projectInternalId' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'projects':
@@ -508,7 +508,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'stats':
@@ -521,26 +521,26 @@ class V19 extends Migration
// Holding off on these until a future release
// try {
// $this->projectDB->deleteAttribute($id, 'type');
// $this->projectDB->deleteCachedCollection($id);
// $this->projectDB->purgeCachedCollection($id);
// } catch (\Throwable $th) {
// Console::warning("'type' from {$id}: {$th->getMessage()}");
// }
// try {
// $this->projectDB->deleteIndex($id, '_key_metric_period_time');
// $this->projectDB->deleteCachedCollection($id);
// $this->projectDB->purgeCachedCollection($id);
// } catch (\Throwable $th) {
// Console::warning("'_key_metric_period_time' from {$id}: {$th->getMessage()}");
// }
// try {
// $this->createIndexFromCollection($this->projectDB, $id, '_key_metric_period_time');
// $this->projectDB->deleteCachedCollection($id);
// $this->projectDB->purgeCachedCollection($id);
// } catch (\Throwable $th) {
// Console::warning("'_key_metric_period_time' from {$id}: {$th->getMessage()}");
// }
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'users':
@@ -568,7 +568,7 @@ class V19 extends Migration
Console::warning("'_key_accessedAt' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
case 'variables':
@@ -616,7 +616,7 @@ class V19 extends Migration
}
}
$this->projectDB->deleteCachedCollection($id);
$this->projectDB->purgeCachedCollection($id);
break;
default:
@@ -775,7 +775,7 @@ class V19 extends Migration
Console::warning("'domains' from projects: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection('projects');
$this->projectDB->purgeCachedCollection('projects');
try {
$this->projectDB->deleteAttribute('builds', 'stderr');
@@ -789,7 +789,7 @@ class V19 extends Migration
Console::warning("'stdout' from builds: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection('builds');
$this->projectDB->purgeCachedCollection('builds');
}
/**
@@ -90,12 +90,12 @@ class DeleteOrphanedProjects extends Action
->getResource();
$dbForProject = new Database($adapter, $cache);
$dbForProject->setDefaultDatabase('appwrite');
$dbForProject->setDatabase('appwrite');
$dbForProject->setNamespace('_' . $project->getInternalId());
$collectionsCreated = 0;
$cnt++;
if ($dbForProject->exists($dbForProject->getDefaultDatabase(), Database::METADATA)) {
if ($dbForProject->exists($dbForProject->getDatabase(), Database::METADATA)) {
$collectionsCreated = $dbForProject->count(Database::METADATA);
}
@@ -113,7 +113,7 @@ class DeleteOrphanedProjects extends Action
foreach ($collections as $collection) {
if ($commit) {
$dbForProject->deleteCollection($collection->getId());
$dbForConsole->deleteCachedCollection($collection->getId());
$dbForConsole->purgeCachedCollection($collection->getId());
}
Console::info('--Deleting collection (' . $collection->getId() . ') project no (' . $project->getInternalId() . ')');
}
@@ -121,12 +121,12 @@ class DeleteOrphanedProjects extends Action
if ($commit) {
$dbForConsole->deleteDocument('projects', $project->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
if ($dbForProject->exists($dbForProject->getDefaultDatabase(), Database::METADATA)) {
try {
$dbForProject->deleteCollection(Database::METADATA);
$dbForProject->deleteCachedCollection(Database::METADATA);
$dbForProject->purgeCachedCollection(Database::METADATA);
} catch (\Throwable $th) {
Console::warning('Metadata collection does not exist');
}
+10 -4
View File
@@ -59,6 +59,7 @@ class Maintenance extends Action
$this->renewCertificates($dbForConsole, $queueForCertificates);
$this->notifyDeleteCache($cacheRetention, $queueForDeletes);
$this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes);
$this->notifyDeleteTargets($queueForDeletes);
}, $interval);
}
@@ -161,8 +162,7 @@ class Maintenance extends Action
private function notifyDeleteCache($interval, Delete $queueForDeletes): void
{
($queueForDeletes)
$queueForDeletes
->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP)
->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval))
->trigger();
@@ -170,10 +170,16 @@ class Maintenance extends Action
private function notifyDeleteSchedules($interval, Delete $queueForDeletes): void
{
($queueForDeletes)
$queueForDeletes
->setType(DELETE_TYPE_SCHEDULES)
->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval))
->trigger();
}
private function notifyDeleteTargets(Delete $queueForDeletes): void
{
$queueForDeletes
->setType(DELETE_TYPE_EXPIRED_TARGETS)
->trigger();
}
}
@@ -73,6 +73,7 @@ abstract class ScheduleBase extends Action
'$id' => $schedule->getId(),
'resourceId' => $schedule->getAttribute('resourceId'),
'schedule' => $schedule->getAttribute('schedule'),
'active' => $schedule->getAttribute('active'),
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
'project' => $project, // TODO: @Meldiron Send only ID to worker to reduce memory usage here
'resource' => $resource, // TODO: @Meldiron Send only ID to worker to reduce memory usage here
@@ -33,6 +33,10 @@ class ScheduleMessages extends ScheduleBase
protected function enqueueResources(Group $pools, Database $dbForConsole): void
{
foreach ($this->schedules as $schedule) {
if (!$schedule['active']) {
continue;
}
$now = DateTime::now();
$scheduledAt = DateTime::formatTz($schedule['schedule']);
@@ -44,24 +48,17 @@ class ScheduleMessages extends ScheduleBase
$queue = $pools->get('queue')->pop();
$connection = $queue->getResource();
$queueForMessaging = new Messaging($connection);
$queueForDeletes = new Delete($connection);
$queueForMessaging
->setMessageId($schedule['resourceId'])
->setProject($schedule['project'])
->trigger();
$dbForConsole->updateDocument(
$dbForConsole->deleteDocument(
'schedules',
$schedule['$id'],
new Document(['active' => false])
);
$queueForDeletes
->setType(DELETE_TYPE_SCHEDULES)
->setDocument($schedule)
->trigger();
$queue->reclaim();
unset($this->schedules[$schedule['resourceId']]);
+10 -10
View File
@@ -195,10 +195,10 @@ class Databases extends Action
}
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
/**
@@ -346,12 +346,12 @@ class Databases extends Action
}
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
}
}
@@ -413,7 +413,7 @@ class Databases extends Action
$this->trigger($database, $collection, $index, $project, $projectId, $events);
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId);
}
/**
@@ -471,7 +471,7 @@ class Databases extends Action
$this->trigger($database, $collection, $index, $project, $projectId, $events);
}
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collection->getId());
$dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId());
}
/**
@@ -526,8 +526,8 @@ class Databases extends Action
}
$relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']);
$dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']);
$dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
}
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
+42 -21
View File
@@ -122,11 +122,9 @@ class Deletes extends Action
break;
}
break;
case DELETE_TYPE_EXECUTIONS:
$this->deleteExecutionLogs($project, $getProjectDB, $executionRetention);
break;
case DELETE_TYPE_AUDIT:
if (!$project->isEmpty()) {
$this->deleteAuditLogs($project, $getProjectDB, $auditRetention);
@@ -139,11 +137,9 @@ class Deletes extends Action
case DELETE_TYPE_ABUSE:
$this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention);
break;
case DELETE_TYPE_REALTIME:
$this->deleteRealtimeUsage($dbForConsole, $datetime);
break;
case DELETE_TYPE_SESSIONS:
$this->deleteExpiredSessions($project, $getProjectDB);
break;
@@ -157,17 +153,19 @@ class Deletes extends Action
$this->deleteCacheByDate($project, $getProjectDB, $datetime);
break;
case DELETE_TYPE_SCHEDULES:
$this->deleteSchedules($dbForConsole, $getProjectDB, $datetime, $document);
$this->deleteSchedules($dbForConsole, $getProjectDB, $datetime);
break;
case DELETE_TYPE_TOPIC:
$this->deleteTopic($project, $getProjectDB, $document);
break;
case DELETE_TYPE_TARGET:
$this->deleteTarget($project, $getProjectDB, $document);
$this->deleteTargetSubscribers($project, $getProjectDB, $document);
break;
case DELETE_TYPE_EXPIRED_TARGETS:
$this->deleteExpiredTargets($project, $getProjectDB);
break;
default:
throw new \Exception('No delete operation for type: ' . \strval($type));
break;
}
}
@@ -183,13 +181,12 @@ class Deletes extends Action
* @throws Structure
* @throws DatabaseException
*/
private function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime, ?Document $document = null): void
private function deleteSchedules(Database $dbForConsole, callable $getProjectDB, string $datetime): void
{
$this->listByGroup(
'schedules',
[
Query::equal('region', [App::getEnv('_APP_REGION', 'default')]),
Query::equal('resourceType', [$document->getAttribute('resourceType')]),
Query::lessThanEqual('resourceUpdatedAt', $datetime),
Query::equal('active', [false]),
],
@@ -230,17 +227,20 @@ class Deletes extends Action
* @param Document $topic
* @throws Exception
*/
protected function deleteTopic(Document $project, callable $getProjectDB, Document $topic)
private function deleteTopic(Document $project, callable $getProjectDB, Document $topic)
{
if ($topic->isEmpty()) {
Console::error('Failed to delete subscribers. Topic not found');
return;
}
$dbForProject = $getProjectDB($project);
$this->deleteByGroup('subscribers', [
Query::equal('topicInternalId', [$topic->getInternalId()])
], $dbForProject);
$this->deleteByGroup(
'subscribers',
[
Query::equal('topicInternalId', [$topic->getInternalId()])
],
$getProjectDB($project)
);
}
/**
@@ -249,7 +249,7 @@ class Deletes extends Action
* @param Document $target
* @throws Exception
*/
protected function deleteTarget(Document $project, callable $getProjectDB, Document $target)
private function deleteTargetSubscribers(Document $project, callable $getProjectDB, Document $target)
{
/** @var Database */
$dbForProject = $getProjectDB($project);
@@ -272,6 +272,27 @@ class Deletes extends Action
);
}
/**
* @param Document $project
* @param callable $getProjectDB
* @param Document $target
* @return void
* @throws Exception
*/
private function deleteExpiredTargets(Document $project, callable $getProjectDB)
{
$this->deleteByGroup(
'targets',
[
Query::equal('expired', [true])
],
$getProjectDB($project),
function (Document $target) use ($getProjectDB, $project) {
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
}
);
}
/**
* @param Document $project
* @param callable $getProjectDB
@@ -390,8 +411,8 @@ class Deletes extends Action
}
$relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']);
$dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']);
$dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
$dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
$dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
$dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
}
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId());
@@ -447,7 +468,7 @@ class Deletes extends Action
$dbForProject,
function (Document $membership) use ($dbForProject) {
$userId = $membership->getAttribute('userId');
$dbForProject->deleteCachedDocument('users', $userId);
$dbForProject->purgeCachedDocument('users', $userId);
}
);
}
@@ -582,7 +603,7 @@ class Deletes extends Action
Query::equal('userInternalId', [$userInternalId])
], $dbForProject);
$dbForProject->deleteCachedDocument('users', $userId);
$dbForProject->purgeCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
@@ -613,14 +634,14 @@ class Deletes extends Action
], $dbForProject);
// Delete targets
$this->listByGroup(
$this->deleteByGroup(
'targets',
[
Query::equal('userInternalId', [$userInternalId])
],
$dbForProject,
function (Document $target) use ($getProjectDB, $project) {
$this->deleteTarget($project, $getProjectDB, $target);
$this->deleteTargetSubscribers($project, $getProjectDB, $target);
}
);
}
+28 -15
View File
@@ -18,6 +18,7 @@ use Utopia\Database\Query;
use Utopia\Messaging\Adapter\Email as EmailAdapter;
use Utopia\Messaging\Adapter\Email\Mailgun;
use Utopia\Messaging\Adapter\Email\Sendgrid;
use Utopia\Messaging\Adapter\Email\SMTP;
use Utopia\Messaging\Adapter\Push as PushAdapter;
use Utopia\Messaging\Adapter\Push\APNS;
use Utopia\Messaging\Adapter\Push\FCM;
@@ -216,8 +217,8 @@ class Messaging extends Action
$batches = \array_chunk($identifiers, $maxBatchSize);
$batchIndex = 0;
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex, $dbForProject) {
return function () use ($batch, $message, $provider, $adapter, $batchIndex, $dbForProject) {
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, &$batchIndex, $dbForProject) {
return function () use ($batch, $message, $provider, $adapter, &$batchIndex, $dbForProject) {
$deliveredTotal = 0;
$deliveryErrors = [];
$messageData = clone $message;
@@ -248,7 +249,11 @@ class Messaging extends Action
]);
if ($target instanceof Document && !$target->isEmpty()) {
$dbForProject->deleteDocument('targets', $target->getId());
$dbForProject->updateDocument(
'targets',
$target->getId(),
$target->setAttribute('expired', true)
);
}
}
}
@@ -403,10 +408,24 @@ class Messaging extends Action
private function email(Document $provider): ?EmailAdapter
{
$credentials = $provider->getAttribute('credentials');
$credentials = $provider->getAttribute('credentials', []);
$options = $provider->getAttribute('options', []);
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'mailgun' => new Mailgun($credentials['apiKey'], $credentials['domain'], $credentials['isEuRegion']),
'smtp' => new SMTP(
$credentials['host'],
$credentials['port'],
$credentials['username'],
$credentials['password'],
$options['encryption'],
$options['autoTLS'],
$options['mailer'],
),
'mailgun' => new Mailgun(
$credentials['apiKey'],
$credentials['domain'],
$credentials['isEuRegion']
),
'sendgrid' => new Sendgrid($credentials['apiKey']),
default => null
};
@@ -414,16 +433,10 @@ class Messaging extends Action
private function buildEmailMessage(Database $dbForProject, Document $message, Document $provider): Email
{
$fromName = $provider['options']['fromName'];
$fromEmail = $provider['options']['fromEmail'];
$replyToEmail = null;
$replyToName = null;
if (isset($provider['options']['replyToName']) && isset($provider['options']['replyToEmail'])) {
$replyToName = $provider['options']['replyToName'];
$replyToEmail = $provider['options']['replyToEmail'];
}
$fromName = $provider['options']['fromName'] ?? null;
$fromEmail = $provider['options']['fromEmail'] ?? null;
$replyToEmail = $provider['options']['replyToEmail'] ?? null;
$replyToName = $provider['options']['replyToName'] ?? null;
$data = $message['data'] ?? [];
$ccTargets = $data['cc'] ?? [];
$bccTargets = $data['bcc'] ?? [];
+1 -1
View File
@@ -225,7 +225,7 @@ class Migrations extends Action
]);
$this->dbForConsole->createDocument('keys', $key);
$this->dbForConsole->deleteCachedDocument('projects', $project->getId());
$this->dbForConsole->purgeCachedDocument('projects', $project->getId());
return $key;
}
+2 -2
View File
@@ -163,13 +163,13 @@ class Webhooks extends Action
}
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$this->errors[] = $logs;
} else {
$webhook->setAttribute('attempts', 0); // Reset attempts on success
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
}
@@ -5,6 +5,7 @@ namespace Tests\E2E\Services\Account;
use Tests\E2E\Client;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Query;
trait AccountBase
{
@@ -9,6 +9,7 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use function sleep;
@@ -344,7 +345,9 @@ class AccountCustomClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'limit(1)' ],
'queries' => [
Query::limit(1)->toString()
]
]);
$this->assertEquals($responseLimit['headers']['status-code'], 200);
@@ -361,7 +364,9 @@ class AccountCustomClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'offset(1)' ],
'queries' => [
Query::offset(1)->toString()
]
]);
$this->assertEquals($responseOffset['headers']['status-code'], 200);
@@ -378,7 +383,10 @@ class AccountCustomClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'limit(1)', 'offset(1)' ],
'queries' => [
Query::offset(1)->toString(),
Query::limit(1)->toString()
]
]);
$this->assertEquals($responseLimitOffset['headers']['status-code'], 200);
+486 -61
View File
@@ -6,13 +6,152 @@ use Appwrite\Extend\Exception;
use Tests\E2E\Client;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Validator\JSON;
trait DatabasesBase
{
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testOrQueries(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Or queries'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('Or queries', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$presidents = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'USA Presidents',
'documentSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $presidents['headers']['status-code']);
$this->assertEquals($presidents['body']['name'], 'USA Presidents');
// Create Attributes
$firstName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'first_name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $firstName['headers']['status-code']);
$lastName = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'last_name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $lastName['headers']['status-code']);
// Wait for worker
sleep(2);
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::unique(),
'data' => [
'first_name' => 'Donald',
'last_name' => 'Trump',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $document1['headers']['status-code']);
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::unique(),
'data' => [
'first_name' => 'George',
'last_name' => 'Bush',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $document2['headers']['status-code']);
$document3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::unique(),
'data' => [
'first_name' => 'Joe',
'last_name' => 'Biden',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $document3['headers']['status-code']);
$documents = $this->client->call(
Client::METHOD_GET,
'/databases/' . $databaseId . '/collections/' . $presidents['body']['$id'] . '/documents',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::select(['first_name', 'last_name'])->toString(),
Query::or([
Query::equal('first_name', ['Donald']),
Query::equal('last_name', ['Bush'])
])->toString(),
Query::limit(999)->toString(),
Query::offset(0)->toString()
],
]
);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertCount(2, $documents['body']['documents']);
}
public function testCreateDatabase(): array
{
/**
@@ -243,6 +382,18 @@ trait DatabasesBase
'twoWayKey' => 'movie'
]);
$integers = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'integers',
'required' => false,
'array' => true,
'min' => 10,
'max' => 99,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$this->assertEquals($title['body']['key'], 'title');
$this->assertEquals($title['body']['type'], 'string');
@@ -293,6 +444,13 @@ trait DatabasesBase
$this->assertEquals($relationship['body']['twoWay'], true);
$this->assertEquals($relationship['body']['twoWayKey'], 'movie');
$this->assertEquals(202, $integers['headers']['status-code']);
$this->assertEquals($integers['body']['key'], 'integers');
$this->assertEquals($integers['body']['type'], 'integer');
$this->assertArrayNotHasKey('size', $integers['body']);
$this->assertEquals($integers['body']['required'], false);
$this->assertEquals($integers['body']['array'], true);
// wait for database worker to create attributes
sleep(2);
@@ -303,7 +461,7 @@ trait DatabasesBase
]));
$this->assertIsArray($movies['body']['attributes']);
$this->assertCount(8, $movies['body']['attributes']);
$this->assertCount(9, $movies['body']['attributes']);
$this->assertEquals($movies['body']['attributes'][0]['key'], $title['body']['key']);
$this->assertEquals($movies['body']['attributes'][1]['key'], $description['body']['key']);
$this->assertEquals($movies['body']['attributes'][2]['key'], $tagline['body']['key']);
@@ -312,6 +470,7 @@ trait DatabasesBase
$this->assertEquals($movies['body']['attributes'][5]['key'], $actors['body']['key']);
$this->assertEquals($movies['body']['attributes'][6]['key'], $datetime['body']['key']);
$this->assertEquals($movies['body']['attributes'][7]['key'], $relationship['body']['key']);
$this->assertEquals($movies['body']['attributes'][8]['key'], $integers['body']['key']);
return $data;
}
@@ -327,7 +486,11 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['equal("type", "string")', 'limit(2)', 'cursorAfter(title)'],
'queries' => [
Query::equal('type', ['string'])->toString(),
Query::limit(2)->toString(),
Query::cursorAfter(new Document(['$id' => 'title']))->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['attributes']));
@@ -336,7 +499,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['select(["key"])'],
'queries' => [Query::select(['key'])->toString()],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
@@ -531,6 +694,29 @@ trait DatabasesBase
'twoWayKey' => 'twoWayKey'
]);
$strings = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'names',
'size' => 512,
'required' => false,
'array' => true,
]);
$integers = $this->client->call(Client::METHOD_POST, $attributesPath . '/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'numbers',
'required' => false,
'array' => true,
'min' => 1,
'max' => 999,
]);
$this->assertEquals(202, $string['headers']['status-code']);
$this->assertEquals('string', $string['body']['key']);
$this->assertEquals('string', $string['body']['type']);
@@ -615,6 +801,22 @@ trait DatabasesBase
$this->assertEquals(true, $relationship['body']['twoWay']);
$this->assertEquals('twoWayKey', $relationship['body']['twoWayKey']);
$this->assertEquals(202, $strings['headers']['status-code']);
$this->assertEquals('names', $strings['body']['key']);
$this->assertEquals('string', $strings['body']['type']);
$this->assertEquals(false, $strings['body']['required']);
$this->assertEquals(true, $strings['body']['array']);
$this->assertEquals(null, $strings['body']['default']);
$this->assertEquals(202, $integers['headers']['status-code']);
$this->assertEquals('numbers', $integers['body']['key']);
$this->assertEquals('integer', $integers['body']['type']);
$this->assertEquals(false, $integers['body']['required']);
$this->assertEquals(true, $integers['body']['array']);
$this->assertEquals(1, $integers['body']['min']);
$this->assertEquals(999, $integers['body']['max']);
$this->assertEquals(null, $integers['body']['default']);
// Wait for database worker to create attributes
sleep(5);
@@ -678,6 +880,18 @@ trait DatabasesBase
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$stringsResponse = $this->client->call(Client::METHOD_GET, $attributesPath . '/' . $strings['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$integersResponse = $this->client->call(Client::METHOD_GET, $attributesPath . '/' . $integers['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $stringResponse['headers']['status-code']);
$this->assertEquals($string['body']['key'], $stringResponse['body']['key']);
$this->assertEquals($string['body']['type'], $stringResponse['body']['type']);
@@ -778,11 +992,11 @@ trait DatabasesBase
]));
$this->assertEquals(200, $attributes['headers']['status-code']);
$this->assertEquals(10, $attributes['body']['total']);
$this->assertEquals(12, $attributes['body']['total']);
$attributes = $attributes['body']['attributes'];
$this->assertIsArray($attributes);
$this->assertCount(10, $attributes);
$this->assertCount(12, $attributes);
$this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']);
$this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']);
@@ -867,6 +1081,22 @@ trait DatabasesBase
$this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']);
$this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']);
$this->assertEquals($stringsResponse['body']['key'], $attributes[10]['key']);
$this->assertEquals($stringsResponse['body']['type'], $attributes[10]['type']);
$this->assertEquals($stringsResponse['body']['status'], $attributes[10]['status']);
$this->assertEquals($stringsResponse['body']['required'], $attributes[10]['required']);
$this->assertEquals($stringsResponse['body']['array'], $attributes[10]['array']);
$this->assertEquals($stringsResponse['body']['default'], $attributes[10]['default']);
$this->assertEquals($integersResponse['body']['key'], $attributes[11]['key']);
$this->assertEquals($integersResponse['body']['type'], $attributes[11]['type']);
$this->assertEquals($integersResponse['body']['status'], $attributes[11]['status']);
$this->assertEquals($integersResponse['body']['required'], $attributes[11]['required']);
$this->assertEquals($integersResponse['body']['array'], $attributes[11]['array']);
$this->assertEquals($integersResponse['body']['default'], $attributes[11]['default']);
$this->assertEquals($integersResponse['body']['min'], $attributes[11]['min']);
$this->assertEquals($integersResponse['body']['max'], $attributes[11]['max']);
$collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -878,7 +1108,7 @@ trait DatabasesBase
$attributes = $collection['body']['attributes'];
$this->assertIsArray($attributes);
$this->assertCount(10, $attributes);
$this->assertCount(12, $attributes);
$this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']);
$this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']);
@@ -963,6 +1193,22 @@ trait DatabasesBase
$this->assertEquals($relationshipResponse['body']['twoWay'], $attributes[9]['twoWay']);
$this->assertEquals($relationshipResponse['body']['twoWayKey'], $attributes[9]['twoWayKey']);
$this->assertEquals($stringsResponse['body']['key'], $attributes[10]['key']);
$this->assertEquals($stringsResponse['body']['type'], $attributes[10]['type']);
$this->assertEquals($stringsResponse['body']['status'], $attributes[10]['status']);
$this->assertEquals($stringsResponse['body']['required'], $attributes[10]['required']);
$this->assertEquals($stringsResponse['body']['array'], $attributes[10]['array']);
$this->assertEquals($stringsResponse['body']['default'], $attributes[10]['default']);
$this->assertEquals($integersResponse['body']['key'], $attributes[11]['key']);
$this->assertEquals($integersResponse['body']['type'], $attributes[11]['type']);
$this->assertEquals($integersResponse['body']['status'], $attributes[11]['status']);
$this->assertEquals($integersResponse['body']['required'], $attributes[11]['required']);
$this->assertEquals($integersResponse['body']['array'], $attributes[11]['array']);
$this->assertEquals($integersResponse['body']['default'], $attributes[11]['default']);
$this->assertEquals($integersResponse['body']['min'], $attributes[11]['min']);
$this->assertEquals($integersResponse['body']['max'], $attributes[11]['max']);
/**
* Test for FAILURE
*/
@@ -1127,6 +1373,56 @@ trait DatabasesBase
$this->assertEquals(400, $tooLong['headers']['status-code']);
$this->assertStringContainsString('Index length is longer than the maximum', $tooLong['body']['message']);
$fulltextArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'ft',
'type' => 'fulltext',
'attributes' => ['actors'],
]);
$this->assertEquals(400, $fulltextArray['headers']['status-code']);
$this->assertEquals('"Fulltext" index is forbidden on array attributes', $fulltextArray['body']['message']);
$actorsArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-actors',
'type' => 'key',
'attributes' => ['actors'],
]);
$this->assertEquals(202, $actorsArray['headers']['status-code']);
$twoLevelsArray = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-ip-actors',
'type' => 'key',
'attributes' => ['releaseYear', 'actors'], // 2 levels
]);
$this->assertEquals(202, $twoLevelsArray['headers']['status-code']);
$unknown = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-unknown',
'type' => 'key',
'attributes' => ['Unknown'],
]);
$this->assertEquals(400, $unknown['headers']['status-code']);
$this->assertEquals('Unknown attribute: Unknown', $unknown['body']['message']);
return $data;
}
@@ -1141,7 +1437,10 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['equal("type", "key")', 'limit(2)'],
'queries' => [
Query::equal('type', ['key'])->toString(),
Query::limit(2)->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['indexes']));
@@ -1150,7 +1449,9 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => ['select(["key"])'],
'queries' => [
Query::select(['key'])->toString(),
],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
@@ -1196,7 +1497,8 @@ trait DatabasesBase
'Tom Holland',
'Zendaya Maree Stoermer',
'Samuel Jackson',
]
],
'integers' => [50,60]
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
@@ -1219,6 +1521,7 @@ trait DatabasesBase
'Tom Holland',
'Zendaya Maree Stoermer',
],
'integers' => [50]
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
@@ -1269,6 +1572,8 @@ trait DatabasesBase
$this->assertEquals($document2['body']['actors'][1], 'Zendaya Maree Stoermer');
$this->assertEquals($document2['body']['actors'][2], 'Samuel Jackson');
$this->assertEquals($document2['body']['birthDay'], null);
$this->assertEquals($document2['body']['integers'][0], 50);
$this->assertEquals($document2['body']['integers'][1], 60);
$this->assertEquals(201, $document3['headers']['status-code']);
$this->assertEquals($data['moviesId'], $document3['body']['$collectionId']);
@@ -1305,7 +1610,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("releaseYear")'],
'queries' => [
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1327,7 +1634,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderDesc("releaseYear")'],
'queries' => [
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1377,7 +1686,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["title", "releaseYear", "$id"])',
Query::select(['title', 'releaseYear', '$id'])->toString(),
],
]);
@@ -1411,7 +1720,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['documents'][0]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString()
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1423,7 +1734,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['documents'][2]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString()
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1436,7 +1749,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("releaseYear")'],
'queries' => [
Query::orderAsc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@@ -1449,7 +1764,10 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['documents'][1]['$id'] . '")', 'orderAsc("releaseYear")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(),
Query::orderAsc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1463,7 +1781,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderDesc("releaseYear")'],
'queries' => [
Query::orderDesc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@@ -1476,7 +1796,10 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['documents'][1]['$id'] . '")', 'orderDesc("releaseYear")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(),
Query::orderDesc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1490,7 +1813,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("unknown")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
@@ -1522,7 +1847,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['documents'][2]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['documents'][2]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1534,7 +1861,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['documents'][0]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['documents'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1547,7 +1876,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("releaseYear")'],
'queries' => [
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@@ -1560,7 +1891,10 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['documents'][1]['$id'] . '")', 'orderAsc("releaseYear")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(),
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1574,7 +1908,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderDesc("releaseYear")'],
'queries' => [
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@@ -1587,7 +1923,10 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['documents'][1]['$id'] . '")', 'orderDesc("releaseYear")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['documents'][1]['$id']]))->toString(),
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1607,7 +1946,10 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'orderAsc("releaseYear")'],
'queries' => [
Query::orderAsc('releaseYear')->toString(),
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1618,7 +1960,11 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(2)', 'offset(1)', 'orderAsc("releaseYear")'],
'queries' => [
Query::orderAsc('releaseYear')->toString(),
Query::limit(2)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1639,7 +1985,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['search("title", "Captain America")'],
'queries' => [
Query::search('title', 'Captain America')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1650,7 +1998,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "' . $documents['body']['documents'][0]['$id'] . '")'],
'queries' => [
Query::equal('$id', [$documents['body']['documents'][0]['$id']])->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1661,7 +2011,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['search("title", "Homecoming")'],
'queries' => [
Query::search('title', 'Homecoming')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1672,7 +2024,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['search("title", "spider")'],
'queries' => [
Query::search('title', 'spider')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1684,7 +2038,21 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("releaseYear", 1944)'],
'queries' => [
Query::contains('title', ['spi'])->toString(), // like query
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertEquals(2, $documents['body']['total']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('releaseYear', [1944])->toString(),
],
]);
$this->assertCount(1, $documents['body']['documents']);
@@ -1694,7 +2062,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['notEqual("releaseYear", 1944)'],
'queries' => [
Query::notEqual('releaseYear', 1944)->toString(),
],
]);
$this->assertCount(2, $documents['body']['documents']);
@@ -1705,7 +2075,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['greaterThan("$createdAt", "1976-06-12")'],
'queries' => [
Query::greaterThan('$createdAt', '1976-06-12')->toString(),
],
]);
$this->assertCount(3, $documents['body']['documents']);
@@ -1714,7 +2086,9 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['lessThan("$createdAt", "1976-06-12")'],
'queries' => [
Query::lessThan('$createdAt', '1976-06-12')->toString(),
],
]);
$this->assertCount(0, $documents['body']['documents']);
@@ -1723,15 +2097,45 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("actors", "Tom Holland")'],
'queries' => [
Query::contains('actors', ['Tom Holland', 'Samuel Jackson'])->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertEquals(3, $documents['body']['total']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['greaterThan("birthDay", "1960-01-01 10:10:10+02:30")'],
'queries' => [
Query::contains('actors', ['Tom'])->toString(), // Full-match not like
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertEquals(0, $documents['body']['total']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('birthDay', '16/01/2024 12:00:00AM')->toString(),
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
$this->assertEquals('Invalid query: Query value is invalid for attribute "birthDay"', $documents['body']['message']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('birthDay', '1960-01-01 10:10:10+02:30')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@@ -1739,6 +2143,18 @@ trait DatabasesBase
$this->assertEquals('1975-06-12T18:12:55.000+00:00', $documents['body']['documents'][1]['birthDay']);
$this->assertCount(2, $documents['body']['documents']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::isNull('integers')->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertEquals(1, $documents['body']['total']);
/**
* Test for Failure
*/
@@ -1752,34 +2168,41 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("releaseYear", [' . implode(',', $conditions) . '])'],
'queries' => [
Query::equal('releaseYear', $conditions)->toString(),
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
$this->assertEquals('Invalid query: Query on attribute has greater than 100 values: releaseYear', $documents['body']['message']);
$conditions = [];
$value = '';
for ($i = 0; $i < 101; $i++) {
$conditions[] = "[" . $i . "] Too long title to cross 2k chars query limit";
$value .= "[" . $i . "] Too long title to cross 2k chars query limit ";
}
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['search("title", ' . implode(',', $conditions) . ')'],
'queries' => [
Query::search('title', $value)->toString(),
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
// Todo: Not sure what to do we with Query length Test VS old? JSON validator will fails if query string will be truncated?
//$this->assertEquals(400, $documents['headers']['status-code']);
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['search("actors", "Tom")'],
'queries' => [
Query::search('actors', 'Tom')->toString(),
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
$this->assertEquals('Searching by attribute "actors" requires a fulltext index.', $documents['body']['message']);
$this->assertEquals('Invalid query: Cannot query search on attribute "actors" because it is an array.', $documents['body']['message']);
return [];
}
@@ -3520,9 +3943,9 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'equal("library", "library1")',
'select(["fullName","library.*"])'
]
Query::select(['fullName', 'library.*'])->toString(),
Query::equal('library', ['library1'])->toString(),
],
]);
$this->assertEquals(1, $documents['body']['total']);
@@ -3534,7 +3957,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'equal("library.libraryName", ["Library 1"])',
Query::equal('library.libraryName', ['Library 1'])->toString(),
],
]);
@@ -4040,10 +4463,10 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'isNotNull("$id")',
'startsWith("fullName", "Stevie")',
'endsWith("fullName", "Wonder")',
'between("$createdAt", "1975-12-06", "2050-12-06")',
Query::isNotNull('$id')->toString(),
Query::startsWith('fullName', 'Stevie')->toString(),
Query::endsWith('fullName', 'Wonder')->toString(),
Query::between('$createdAt', '1975-12-06', '2050-12-0')->toString(),
],
]);
@@ -4058,9 +4481,9 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'isNotNull("$id")',
'isNull("fullName")',
'select(["fullName"])'
Query::isNotNull('$id')->toString(),
Query::isNull('fullName')->toString(),
Query::select(['fullName'])->toString(),
],
]);
@@ -4080,8 +4503,8 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'equal("fullName", "Stevie Wonder")',
'select(["fullName"])'
Query::equal('fullName', ['Stevie Wonder'])->toString(),
Query::select(['fullName'])->toString(),
],
]);
@@ -4093,7 +4516,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["libraries.*", "$id"])',
Query::select(['libraries.*', '$id'])->toString(),
],
]);
$document = $response['body']['documents'][0];
@@ -4105,7 +4528,7 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'select(["fullName", "$id"])'
Query::select(['fullName', '$id'])->toString(),
],
]);
@@ -4275,7 +4698,9 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-timeout' => 1,
], $this->getHeaders()), [
'queries' => ['notEqual("longtext", "appwrite")'],
'queries' => [
Query::notEqual('longtext', 'appwrite')->toString(),
],
]);
$this->assertEquals(408, $response['headers']['status-code']);
@@ -9,6 +9,7 @@ use Tests\E2E\Scopes\SideConsole;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
class DatabasesConsoleClientTest extends Scope
{
@@ -279,6 +280,7 @@ class DatabasesConsoleClientTest extends Scope
/**
* @depends testCreateCollection
* @throws \Utopia\Database\Exception\Query
*/
public function testGetCollectionLogs(array $data)
{
@@ -299,7 +301,7 @@ class DatabasesConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)']
'queries' => [Query::limit(1)->toString()]
]);
$this->assertEquals(200, $logs['headers']['status-code']);
@@ -311,7 +313,7 @@ class DatabasesConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)']
'queries' => [Query::offset(1)->toString()]
]);
$this->assertEquals(200, $logs['headers']['status-code']);
@@ -322,7 +324,7 @@ class DatabasesConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)', 'limit(1)']
'queries' => [Query::offset(1)->toString(), Query::limit(1)->toString()]
]);
$this->assertEquals(200, $logs['headers']['status-code']);
@@ -7,9 +7,11 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Client;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
class DatabasesCustomServerTest extends Scope
{
@@ -57,7 +59,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
@@ -66,7 +70,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
@@ -75,7 +81,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("name", ["Test 1", "Test 2"])'],
'queries' => [
Query::equal('name', ['Test 1', 'Test 2'])->toString(),
],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(2, $databases['body']['databases']);
@@ -84,7 +92,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("name", "Test 2")'],
'queries' => [
Query::equal('name', ['Test 2'])->toString(),
],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
@@ -93,7 +103,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "first")'],
'queries' => [
Query::equal('$id', ['first'])->toString(),
],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
@@ -105,7 +117,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderDesc("")'],
'queries' => [
Query::orderDesc()->toString(),
],
]);
$this->assertEquals(2, $databases['body']['total']);
@@ -124,7 +138,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['databases'][0]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['databases'][0]['$id']]))->toString(),
],
]);
$this->assertCount(1, $databases['body']['databases']);
@@ -134,7 +150,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['databases'][1]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['databases'][1]['$id']]))->toString(),
],
]);
$this->assertCount(0, $databases['body']['databases']);
@@ -152,7 +170,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['databases'][1]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['databases'][1]['$id']]))->toString(),
],
]);
$this->assertCount(1, $databases['body']['databases']);
@@ -162,7 +182,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['databases'][0]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['databases'][0]['$id']]))->toString(),
],
]);
$this->assertCount(0, $databases['body']['databases']);
@@ -208,7 +230,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("unknown")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
@@ -375,7 +399,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)']
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $collections['headers']['status-code']);
@@ -385,7 +411,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)']
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $collections['headers']['status-code']);
@@ -395,7 +423,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("enabled", true)']
'queries' => [
Query::equal('enabled', [true])->toString(),
],
]);
$this->assertEquals(200, $collections['headers']['status-code']);
@@ -405,7 +435,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("enabled", false)']
'queries' => [
Query::equal('enabled', [false])->toString(),
],
]);
$this->assertEquals(200, $collections['headers']['status-code']);
@@ -418,7 +450,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderDesc("")'],
'queries' => [
Query::orderDesc()->toString(),
],
]);
$this->assertEquals(2, $collections['body']['total']);
@@ -437,7 +471,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['collections'][0]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(),
],
]);
$this->assertCount(1, $collections['body']['collections']);
@@ -447,7 +483,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $base['body']['collections'][1]['$id'] . '")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(),
],
]);
$this->assertCount(0, $collections['body']['collections']);
@@ -465,7 +503,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['collections'][1]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['collections'][1]['$id']]))->toString(),
],
]);
$this->assertCount(1, $collections['body']['collections']);
@@ -475,7 +515,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("' . $base['body']['collections'][0]['$id'] . '")'],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['collections'][0]['$id']]))->toString(),
],
]);
$this->assertCount(0, $collections['body']['collections']);
@@ -521,7 +563,9 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("unknown")'],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
@@ -1376,7 +1420,7 @@ class DatabasesCustomServerTest extends Scope
// Test indexLimit = 64
// MariaDB, MySQL, and MongoDB create 5 indexes per new collection
// Add up to the limit, then check if the next index throws IndexLimitException
for ($i = 0; $i < 59; $i++) {
for ($i = 0; $i < 58; $i++) {
// $this->assertEquals(true, static::getDatabase()->createIndex('indexLimit', "index{$i}", Database::INDEX_KEY, ["test{$i}"], [16]));
$index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([
'content-type' => 'application/json',
@@ -1405,7 +1449,7 @@ class DatabasesCustomServerTest extends Scope
$this->assertIsArray($collection['body']['attributes']);
$this->assertIsArray($collection['body']['indexes']);
$this->assertCount(64, $collection['body']['attributes']);
$this->assertCount(59, $collection['body']['indexes']);
$this->assertCount(58, $collection['body']['indexes']);
$tooMany = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([
'content-type' => 'application/json',
@@ -7,8 +7,10 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
class FunctionsCustomClientTest extends Scope
{
@@ -474,7 +476,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
@@ -485,7 +489,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
@@ -496,7 +502,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'equal("status", ["completed"])' ]
'queries' => [
Query::equal('status', ['completed'])->toString(),
],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
@@ -507,7 +515,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'equal("status", ["failed"])' ]
'queries' => [
Query::equal('status', ['failed'])->toString(),
],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
@@ -518,7 +528,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'cursorAfter("' . $base['body']['executions'][0]['$id'] . '")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['executions'][0]['$id']]))->toString(),
],
]);
$this->assertCount(2, $executions['body']['executions']);
@@ -529,7 +541,9 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'cursorBefore("' . $base['body']['executions'][1]['$id'] . '")' ],
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['executions'][1]['$id']]))->toString(),
],
]);
// Cleanup : Delete function
@@ -9,7 +9,9 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
class FunctionsCustomServerTest extends Scope
@@ -121,7 +123,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -131,7 +135,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -141,7 +147,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("enabled", true)' ]
'queries' => [
Query::equal('enabled', [true])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -151,7 +159,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("enabled", false)' ]
'queries' => [
Query::equal('enabled', [false])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -244,7 +254,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("' . $functions['body']['functions'][0]['$id'] . '")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => $functions['body']['functions'][0]['$id']]))->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -255,7 +267,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorBefore("' . $functions['body']['functions'][1]['$id'] . '")' ],
'queries' => [
Query::cursorBefore(new Document(['$id' => $functions['body']['functions'][1]['$id']]))->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -269,7 +283,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("unknown")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -532,7 +548,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($function['headers']['status-code'], 200);
@@ -542,7 +560,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($function['headers']['status-code'], 200);
@@ -552,7 +572,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("entrypoint", "index.php")' ]
'queries' => [
Query::equal('entrypoint', ['index.php'])->toString(),
],
]);
$this->assertEquals($function['headers']['status-code'], 200);
@@ -562,7 +584,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("entrypoint", "index.js")' ]
'queries' => [
Query::equal('entrypoint', ['index.js'])->toString(),
],
]);
$this->assertEquals($function['headers']['status-code'], 200);
@@ -690,7 +714,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -700,7 +726,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -710,7 +738,9 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("trigger", "http")' ]
'queries' => [
Query::equal('trigger', ['http'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -1459,7 +1489,10 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("resourceId", "' . $functionId . '")', 'equal("resourceType", "function")' ]
'queries' => [
Query::equal('resourceId', [$functionId])->toString(),
Query::equal('resourceType', ['function'])->toString(),
],
]);
$this->assertEquals(200, $rules['headers']['status-code']);
+6 -6
View File
@@ -29,7 +29,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(18767, \strlen($creditCardIcon['body']));
$this->assertEquals(18546, \strlen($creditCardIcon['body']));
return $creditCardIcon['body'];
}
@@ -50,7 +50,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(11100, \strlen($browserIcon['body']));
$this->assertEquals(13312, \strlen($browserIcon['body']));
return $browserIcon['body'];
}
@@ -71,7 +71,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(7460, \strlen($countryFlag['body']));
$this->assertEquals(8814, \strlen($countryFlag['body']));
return $countryFlag['body'];
}
@@ -92,7 +92,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(36036, \strlen($image['body']));
$this->assertEquals(52585, \strlen($image['body']));
return $image['body'];
}
@@ -134,7 +134,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(14771, \strlen($qrCode['body']));
$this->assertEquals(29428, \strlen($qrCode['body']));
return $qrCode['body'];
}
@@ -155,7 +155,7 @@ class AvatarsTest extends Scope
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(5041, \strlen($initials['body']));
$this->assertEquals(5025, \strlen($initials['body']));
return $initials['body'];
}
+38 -28
View File
@@ -207,6 +207,7 @@ trait Base
// Providers
public static string $CREATE_MAILGUN_PROVIDER = 'create_mailgun_provider';
public static string $CREATE_SENDGRID_PROVIDER = 'create_sendgrid_provider';
public static string $CREATE_SMTP_PROVIDER = 'create_smtp_provider';
public static string $CREATE_TWILIO_PROVIDER = 'create_twilio_provider';
public static string $CREATE_TELESIGN_PROVIDER = 'create_telesign_provider';
public static string $CREATE_TEXTMAGIC_PROVIDER = 'create_textmagic_provider';
@@ -218,6 +219,7 @@ trait Base
public static string $GET_PROVIDER = 'get_provider';
public static string $UPDATE_MAILGUN_PROVIDER = 'update_mailgun_provider';
public static string $UPDATE_SENDGRID_PROVIDER = 'update_sendgrid_provider';
public static string $UPDATE_SMTP_PROVIDER = 'update_smtp_provider';
public static string $UPDATE_TWILIO_PROVIDER = 'update_twilio_provider';
public static string $UPDATE_TELESIGN_PROVIDER = 'update_telesign_provider';
public static string $UPDATE_TEXTMAGIC_PROVIDER = 'update_textmagic_provider';
@@ -1809,6 +1811,16 @@ trait Base
enabled
}
}';
case self::$CREATE_SMTP_PROVIDER:
return 'mutation createSMTPProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean! $fromName: String!, $fromEmail: String!, $replyToName: String, $replyToEmail: String) {
messagingCreateSMTPProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_TWILIO_PROVIDER:
return 'mutation createTwilioProvider($providerId: String!, $name: String!, $from: String!, $accountSid: String!, $authToken: String!) {
messagingCreateTwilioProvider(providerId: $providerId, name: $name, from: $from, accountSid: $accountSid, authToken: $authToken) {
@@ -1923,6 +1935,16 @@ trait Base
enabled
}
}';
case self::$UPDATE_SMTP_PROVIDER:
return 'mutation updateSMTPProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean!, $fromName: String, $fromEmail: String, $enabled: Boolean) {
messagingUpdateSMTPProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, enabled: $enabled) {
_id
name
provider
type
enabled
}
}';
case self::$UPDATE_TWILIO_PROVIDER:
return 'mutation updateTwilioProvider($providerId: String!, $name: String!, $accountSid: String!, $authToken: String!) {
messagingUpdateTwilioProvider(providerId: $providerId, name: $name, accountSid: $accountSid, authToken: $authToken) {
@@ -2000,11 +2022,10 @@ trait Base
}
}';
case self::$CREATE_TOPIC:
return 'mutation createTopic($topicId: String!, $name: String!, $description: String!) {
messagingCreateTopic(topicId: $topicId, name: $name, description: $description) {
return 'mutation createTopic($topicId: String!, $name: String!) {
messagingCreateTopic(topicId: $topicId, name: $name) {
_id
name
description
}
}';
case self::$LIST_TOPICS:
@@ -2014,7 +2035,6 @@ trait Base
topics {
_id
name
description
}
}
}';
@@ -2023,15 +2043,13 @@ trait Base
messagingGetTopic(topicId: $topicId) {
_id
name
description
}
}';
case self::$UPDATE_TOPIC:
return 'mutation updateTopic($topicId: String!, $name: String!, $description: String!) {
messagingUpdateTopic(topicId: $topicId, name: $name, description: $description) {
return 'mutation updateTopic($topicId: String!, $name: String!) {
messagingUpdateTopic(topicId: $topicId, name: $name) {
_id
name
description
}
}';
case self::$DELETE_TOPIC:
@@ -2098,8 +2116,8 @@ trait Base
}
}';
case self::$CREATE_EMAIL:
return 'mutation createEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String!, $content: String!, $status: String, $description: String, $html: Boolean, $cc: [String], $bcc: [String], $scheduledAt: String) {
messagingCreateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, cc: $cc, bcc: $bcc, scheduledAt: $scheduledAt) {
return 'mutation createEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String!, $content: String!, $status: String, $html: Boolean, $cc: [String], $bcc: [String], $scheduledAt: String) {
messagingCreateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, html: $html, cc: $cc, bcc: $bcc, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2109,12 +2127,11 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$CREATE_SMS:
return 'mutation createSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String!, $status: String, $description: String, $scheduledAt: String) {
messagingCreateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
return 'mutation createSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String!, $status: String, $scheduledAt: String) {
messagingCreateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2124,12 +2141,11 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$CREATE_PUSH_NOTIFICATION:
return 'mutation createPushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String!, $body: String!, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
messagingCreatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
return 'mutation createPushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String!, $body: String!, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $scheduledAt: String) {
messagingCreatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2139,7 +2155,6 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$LIST_MESSAGES:
@@ -2157,7 +2172,6 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}
}';
@@ -2174,12 +2188,11 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_EMAIL:
return 'mutation updateEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String, $content: String, $status: String, $description: String, $html: Boolean, $cc: [String], $bcc: [String], $scheduledAt: String) {
messagingUpdateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, cc: $cc, bcc: $bcc, scheduledAt: $scheduledAt) {
return 'mutation updateEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String, $content: String, $status: String, , $html: Boolean, $cc: [String], $bcc: [String], $scheduledAt: String) {
messagingUpdateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, html: $html, cc: $cc, bcc: $bcc, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2189,12 +2202,11 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_SMS:
return 'mutation updateSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String, $status: String, $description: String, $scheduledAt: String) {
messagingUpdateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
return 'mutation updateSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String, $status: String, $scheduledAt: String) {
messagingUpdateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2204,12 +2216,11 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_PUSH_NOTIFICATION:
return 'mutation updatePushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String, $body: String, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
messagingUpdatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
return 'mutation updatePushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String, $body: String, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $scheduledAt: String) {
messagingUpdatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, scheduledAt: $scheduledAt) {
_id
topics
users
@@ -2219,7 +2230,6 @@ trait Base
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$COMPLEX_QUERY:
@@ -285,7 +285,6 @@ class MessagingTest extends Scope
'variables' => [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Active users',
],
];
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
@@ -296,7 +295,6 @@ class MessagingTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('topic1', $response['body']['data']['messagingCreateTopic']['name']);
$this->assertEquals('Active users', $response['body']['data']['messagingCreateTopic']['description']);
return $response['body']['data']['messagingCreateTopic'];
}
@@ -313,7 +311,6 @@ class MessagingTest extends Scope
'variables' => [
'topicId' => $topicId,
'name' => 'topic2',
'description' => 'Inactive users',
],
];
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
@@ -324,7 +321,6 @@ class MessagingTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('topic2', $response['body']['data']['messagingUpdateTopic']['name']);
$this->assertEquals('Inactive users', $response['body']['data']['messagingUpdateTopic']['description']);
return $topicId;
}
@@ -368,7 +364,6 @@ class MessagingTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('topic2', $response['body']['data']['messagingGetTopic']['name']);
$this->assertEquals('Inactive users', $response['body']['data']['messagingGetTopic']['description']);
}
/**
@@ -594,7 +589,6 @@ class MessagingTest extends Scope
'variables' => [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Active users',
],
];
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
@@ -801,7 +795,6 @@ class MessagingTest extends Scope
'variables' => [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Active users',
],
];
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
@@ -1006,7 +999,6 @@ class MessagingTest extends Scope
'variables' => [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Active users',
],
];
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
+1 -1
View File
@@ -34,7 +34,7 @@ class ScopeTest extends Scope
'x-appwrite-key' => $apiKey,
], $gqlPayload);
$message = "app.${projectId}@service.localhost (role: applications) missing scope (databases.write)";
$message = "app.{$projectId}@service.localhost (role: applications) missing scope (databases.write)";
$this->assertArrayHasKey('errors', $database['body']);
$this->assertEquals($message, $database['body']['errors'][0]['message']);
}
+2 -2
View File
@@ -104,8 +104,8 @@ class UsersTest extends Scope
'query' => $query,
'variables' => [
'queries' => [
'limit(100)',
'offset(0)',
Query::limit(100)->toString(),
Query::offset(0)->toString(),
],
]
];
+159 -64
View File
@@ -6,6 +6,7 @@ use Appwrite\Enum\MessageStatus;
use Tests\E2E\Client;
use Utopia\App;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\DSN\DSN;
trait MessagingBase
@@ -28,6 +29,17 @@ trait MessagingBase
'fromEmail' => 'sender-email@my-domain.com',
'isEuRegion' => false,
],
'smtp' => [
'providerId' => ID::unique(),
'name' => 'SMTP1',
'host' => 'smtp.appwrite.io',
'port' => 587,
'security' => 'tls',
'username' => 'my-username',
'password' => 'my-password',
'fromName' => 'sender name',
'fromEmail' => 'tester@appwrite.io',
],
'twilio' => [
'providerId' => ID::unique(),
'name' => 'Twilio1',
@@ -114,6 +126,14 @@ trait MessagingBase
'apiKey' => 'my-apikey',
'domain' => 'my-domain',
],
'smtp' => [
'name' => 'SMTP2',
'host' => 'smtp.appwrite.io',
'port' => 587,
'security' => 'tls',
'username' => 'my-username',
'password' => 'my-password',
],
'twilio' => [
'name' => 'Twilio2',
'accountSid' => 'my-accountSid',
@@ -185,6 +205,32 @@ trait MessagingBase
return $providers;
}
public function testUpdateProviderMissingCredentialsThrows(): void
{
// Create new FCM provider with no serviceAccountJSON
$response = $this->client->call(Client::METHOD_POST, '/messaging/providers/fcm', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'providerId' => ID::unique(),
'name' => 'FCM3',
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Enable provider with no serviceAccountJSON
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/providers/fcm/' . $response['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'enabled' => true,
]);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testUpdateProviders
*/
@@ -197,7 +243,7 @@ trait MessagingBase
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(\count($providers), \count($response['body']['providers']));
$this->assertEquals(11, \count($response['body']['providers']));
return $providers;
}
@@ -243,7 +289,6 @@ trait MessagingBase
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals('my-app', $response['body']['name']);
$this->assertEquals('', $response['body']['description']);
return $response['body'];
}
@@ -259,11 +304,9 @@ trait MessagingBase
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'name' => 'android-app',
'description' => 'updated-description'
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('android-app', $response['body']['name']);
$this->assertEquals('updated-description', $response['body']['description']);
return $response['body']['$id'];
}
@@ -272,24 +315,13 @@ trait MessagingBase
*/
public function testListTopic(string $topicId)
{
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'search' => 'updated-description',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, \count($response['body']['topics']));
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => [
'equal("total", [0])'
Query::equal('total', [0])->toString(),
],
]);
@@ -302,7 +334,7 @@ trait MessagingBase
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => [
'greaterThan("total", 0)'
Query::greaterThan('total', 0)->toString(),
],
]);
@@ -324,7 +356,6 @@ trait MessagingBase
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('android-app', $response['body']['name']);
$this->assertEquals('updated-description', $response['body']['description']);
$this->assertEquals(0, $response['body']['total']);
}
@@ -381,7 +412,6 @@ trait MessagingBase
$this->assertEquals(200, $topic['headers']['status-code']);
$this->assertEquals('android-app', $topic['body']['name']);
$this->assertEquals('updated-description', $topic['body']['description']);
$this->assertEquals(1, $topic['body']['total']);
return [
@@ -493,7 +523,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -506,7 +538,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -518,7 +552,10 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['limit(1)', 'offset(1)'],
'queries' => [
Query::limit(1)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -534,7 +571,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['limit(-1)']
'queries' => [
Query::limit(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -544,7 +583,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['offset(-1)']
'queries' => [
Query::offset(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -554,7 +595,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['equal("$id", "asdf")']
'queries' => [
Query::equal('$id', ['asdf'])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -564,7 +607,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['orderAsc("$id")']
'queries' => [
Query::orderAsc('$id')->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -574,7 +619,9 @@ trait MessagingBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => ['cursorAsc("$id")']
'queries' => [
'{ "method": "cursorAsc", "attribute": "$id" }'
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -600,7 +647,6 @@ trait MessagingBase
$this->assertEquals(200, $topic['headers']['status-code']);
$this->assertEquals('android-app', $topic['body']['name']);
$this->assertEquals('updated-description', $topic['body']['description']);
$this->assertEquals(0, $topic['body']['total']);
}
@@ -617,6 +663,60 @@ trait MessagingBase
$this->assertEquals(204, $response['headers']['status-code']);
}
/**
* @depends testCreateDraftEmail
*/
public function testListTargets(array $message)
{
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/does_not_exist/targets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(404, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$targetList = $response['body'];
$this->assertEquals(1, $targetList['total']);
$this->assertEquals(1, count($targetList['targets']));
$this->assertEquals($message['targets'][0], $targetList['targets'][0]['$id']);
// Test for empty targets
$response = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'messageId' => ID::unique(),
'subject' => 'New blog post',
'content' => 'Check out the new blog post at http://localhost',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$message = $response['body'];
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$targetList = $response['body'];
$this->assertEquals(0, $targetList['total']);
$this->assertEquals(0, count($targetList['targets']));
}
public function testCreateDraftEmail()
{
// Create User
@@ -634,7 +734,7 @@ trait MessagingBase
$this->assertEquals(201, $response['headers']['status-code'], "Error creating user: " . var_export($response['body'], true));
$user = $response['body'];
var_dump($user);
$this->assertEquals(1, \count($user['targets']));
$targetId = $user['targets'][0]['$id'];
@@ -698,7 +798,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -720,7 +819,6 @@ trait MessagingBase
// Get target
$target = $user['body']['targets'][0];
// Create Subscriber
$subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([
'content-type' => 'application/json',
@@ -759,14 +857,19 @@ trait MessagingBase
$this->assertEquals(1, $message['body']['deliveredTotal']);
$this->assertEquals(0, \count($message['body']['deliveryErrors']));
return $message;
return [
'message' => $email['body'],
'topic' => $topic['body'],
];
}
/**
* @depends testSendEmail
*/
public function testUpdateEmail(array $email): void
public function testUpdateEmail(array $params): void
{
$email = $params['message'];
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -855,7 +958,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -1016,7 +1118,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -1144,56 +1245,50 @@ trait MessagingBase
}
/**
* @depends testCreateDraftEmail
* @depends testSendEmail
* @return void
* @throws \Exception
*/
public function testListTargets(array $message)
public function testDeleteMessage(array $params): void
{
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/does_not_exist/targets', [
$message = $params['message'];
$topic = $params['topic'];
$response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/' . $message['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(404, $response['headers']['status-code']);
$this->assertEquals(204, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$targetList = $response['body'];
$this->assertEquals(1, $targetList['total']);
$this->assertEquals(1, count($targetList['targets']));
$this->assertEquals($message['targets'][0], $targetList['targets'][0]['$id']);
// Test for empty targets
// Test for FAILURE
$response = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'messageId' => ID::unique(),
'subject' => 'New blog post',
'content' => 'Check out the new blog post at http://localhost',
'status' => 'processing',
'topics' => [$topic['$id']],
'subject' => 'Test subject',
'content' => 'Test content',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$message = $response['body'];
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['$id'] . '/targets', [
$response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/' . $response['body']['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(400, $response['headers']['status-code']);
$targetList = $response['body'];
$this->assertEquals(0, $targetList['total']);
$this->assertEquals(0, count($targetList['targets']));
$response = $this->client->call(Client::METHOD_DELETE, '/messaging/messages/does_not_exist', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(404, $response['headers']['status-code']);
}
}
@@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideConsole;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
class MessagingConsoleClientTest extends Scope
{
@@ -67,7 +68,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -79,7 +82,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -90,7 +95,10 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
'queries' => [
Query::limit(1)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -105,7 +113,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
'queries' => [
Query::limit(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -114,7 +124,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
'queries' => [
Query::offset(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -123,7 +135,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
'queries' => [
Query::equal('$id', ['asdf'])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -132,7 +146,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
'queries' => [
Query::orderAsc('$id')->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -141,7 +157,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
'queries' => [
'{ "method": "cursorAsc", "attribute":"$id" }'
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -170,7 +188,6 @@ class MessagingConsoleClientTest extends Scope
], $this->getHeaders()), [
'topicId' => ID::unique(),
'name' => 'my-app',
'description' => 'web app'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -178,7 +195,7 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'description' => 'updated-description'
'name' => 'android-app'
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -197,7 +214,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -209,7 +228,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -220,7 +241,10 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
'queries' => [
Query::limit(1)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -235,7 +259,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
'queries' => [
Query::limit(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -244,7 +270,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
'queries' => [
Query::offset(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -253,7 +281,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
'queries' => [
Query::equal('$id', ['asdf'])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -262,7 +292,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
'queries' => [
Query::orderAsc('$id')->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -271,7 +303,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
'queries' => [
'{"method":"cursorAsc","attribute":"$id","values":[]}'
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -331,7 +365,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -343,7 +379,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -354,7 +392,10 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
'queries' => [
Query::limit(1)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -369,7 +410,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
'queries' => [
Query::limit(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -378,7 +421,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
'queries' => [
Query::offset(-1)->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -387,7 +432,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
'queries' => [
Query::equal('$id', ['asdf'])->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -396,7 +443,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
'queries' => [
Query::orderAsc('$id')->toString(),
],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -405,7 +454,9 @@ class MessagingConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
'queries' => [
'{"method":"cursorAsc","attribute":"$id","values":[]}'
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -10,7 +10,9 @@ use Tests\E2E\Scopes\SideClient;
use Tests\E2E\Client;
use Tests\E2E\General\UsageTest;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
class ProjectsConsoleClientTest extends Scope
{
@@ -288,7 +290,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("teamId", "' . $team['body']['$id'] . '")' ],
'queries' => [
Query::equal('teamId', [$team['body']['$id']])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -300,7 +304,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -312,7 +318,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(3)' ],
'queries' => [
Query::offset(3)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -324,7 +332,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("name", "Project Test 2")' ],
'queries' => [
Query::equal('name', ['Project Test 2'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -336,7 +346,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'orderDesc("")' ],
'queries' => [
Query::orderDesc()->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -360,7 +372,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("' . $response['body']['projects'][0]['$id'] . '")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => $response['body']['projects'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -372,7 +386,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorBefore("' . $response['body']['projects'][0]['$id'] . '")' ],
'queries' => [
Query::cursorBefore(new Document(['$id' => $response['body']['projects'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -387,7 +403,9 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("unknown")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
+14 -5
View File
@@ -9,6 +9,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait StorageBase
@@ -27,7 +28,7 @@ trait StorageBase
'name' => 'Test Bucket',
'fileSecurity' => true,
'maximumFileSize' => 2000000, //2MB
'allowedFileExtensions' => ["jpg", "png", 'jfif'],
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
@@ -380,7 +381,9 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(1, count($files['body']['files']));
@@ -389,7 +392,9 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(0, count($files['body']['files']));
@@ -398,7 +403,9 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("mimeType", "image/png")' ]
'queries' => [
Query::equal('mimeType', ['image/png'])->toString(),
],
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(1, count($files['body']['files']));
@@ -407,7 +414,9 @@ trait StorageBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("mimeType", "image/jpeg")' ]
'queries' => [
Query::equal('mimeType', ['image/jpeg'])->toString(),
],
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(0, count($files['body']['files']));
@@ -6,7 +6,9 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
class StorageCustomServerTest extends Scope
@@ -98,7 +100,9 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ],
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -108,7 +112,9 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -118,7 +124,9 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("$id", "bucket1")' ],
'queries' => [
Query::equal('$id', ['bucket1'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -128,7 +136,9 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("fileSecurity", true)' ],
'queries' => [
Query::equal('fileSecurity', [true])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -138,7 +148,9 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("' . $response['body']['buckets'][0]['$id'] . '")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => $response['body']['buckets'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
+28 -8
View File
@@ -4,7 +4,9 @@ namespace Tests\E2E\Services\Teams;
use Tests\E2E\Client;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait TeamsBase
@@ -147,7 +149,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(2)' ],
'queries' => [
Query::limit(2)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -157,7 +161,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ],
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -167,7 +173,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'greaterThanEqual("total", 0)' ],
'queries' => [
Query::greaterThanEqual('total', 0)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -177,7 +185,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("name", ["Arsenal", "Newcastle"])' ],
'queries' => [
Query::equal('name', ['Arsenal', 'Newcastle'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -226,7 +236,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(2)' ],
'queries' => [
Query::limit(2)->toString(),
],
]);
$this->assertEquals(200, $teams['headers']['status-code']);
@@ -238,7 +250,10 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)', 'cursorAfter("' . $teams['body']['teams'][0]['$id'] . '")' ],
'queries' => [
Query::limit(1)->toString(),
Query::cursorAfter(new Document(['$id' => $teams['body']['teams'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -251,7 +266,10 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)', 'cursorBefore("' . $teams['body']['teams'][1]['$id'] . '")' ],
'queries' => [
Query::limit(1)->toString(),
Query::cursorBefore(new Document(['$id' => $teams['body']['teams'][1]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -267,7 +285,9 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("unknown")' ],
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
+18 -6
View File
@@ -4,7 +4,9 @@ namespace Tests\E2E\Services\Teams;
use Tests\E2E\Client;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait TeamsBaseClient
@@ -40,7 +42,9 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
'queries' => [
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -50,7 +54,9 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
'queries' => [
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -60,7 +66,9 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("confirm", true)' ]
'queries' => [
Query::equal('confirm', [true])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -70,7 +78,9 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("confirm", false)' ]
'queries' => [
Query::equal('confirm', [false])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -362,7 +372,9 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("' . $memberships['body']['memberships'][0]['$id'] . '")' ]
'queries' => [
Query::cursorAfter(new Document(['$id' => $memberships['body']['memberships'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@@ -527,7 +539,7 @@ trait TeamsBaseClient
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'secret' => $secret,
'userId' => ID::custom(''),
'userId' => ID::custom('asdf'),
]);
$this->assertEquals(401, $response['headers']['status-code']);
+67 -21
View File
@@ -5,7 +5,9 @@ namespace Tests\E2E\Services\Users;
use Appwrite\Tests\Retry;
use Appwrite\Utopia\Response;
use Tests\E2E\Client;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
trait UsersBase
{
@@ -409,7 +411,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("name", "' . $user1['name'] . '")']
'queries' => [
Query::equal('name', [$user1['name']])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -418,11 +422,14 @@ trait UsersBase
$this->assertCount(1, $response['body']['users']);
$this->assertEquals($response['body']['users'][0]['name'], $user1['name']);
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("email", "' . $user1['email'] . '")']
'queries' => [
Query::equal('name', [$user1['name']])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -435,7 +442,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("status", true)']
'queries' => [
Query::equal('status', [true])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -451,7 +460,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("status", false)']
'queries' => [
Query::equal('status', [false])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -463,7 +474,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("passwordUpdate", "' . $user1['passwordUpdate'] . '")']
'queries' => [
Query::equal('passwordUpdate', [$user1['passwordUpdate']])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -476,7 +489,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("registration", "' . $user1['registration'] . '")']
'queries' => [
Query::equal('registration', [$user1['registration']])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -489,7 +504,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("emailVerification", false)']
'queries' => [
Query::equal('emailVerification', [false])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -505,7 +522,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("emailVerification", true)']
'queries' => [
Query::equal('emailVerification', [true])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -517,7 +536,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("phoneVerification", false)']
'queries' => [
Query::equal('phoneVerification', [false])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -529,7 +550,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("phoneVerification", true)']
'queries' => [
Query::equal('phoneVerification', [true])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -541,7 +564,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("' . $data['userId'] . '")']
'queries' => [
Query::cursorAfter(new Document(['$id' => $data['userId']]))->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -554,7 +579,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorBefore("user1")']
'queries' => [
Query::cursorBefore(new Document(['$id' => 'user1']))->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -674,7 +701,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAfter("unknown")']
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString()
]
]);
$this->assertEquals(400, $response['headers']['status-code']);
@@ -1203,7 +1232,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
'queries' => [
Query::limit(1)->toString()
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -1215,7 +1246,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
'queries' => [
Query::offset(1)->toString()
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -1226,7 +1259,10 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
'queries' => [
Query::limit(1)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
@@ -1241,7 +1277,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
'queries' => [
Query::limit(-1)->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -1250,7 +1288,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
'queries' => [
Query::offset(-1)->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -1259,7 +1299,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
'queries' => [
Query::equal('$id', ['asdf'])->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -1268,7 +1310,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
'queries' => [
Query::orderAsc('$id')->toString()
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
@@ -1277,7 +1321,9 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
'queries' => [
'{ "method": "cursorAsc", "attribute": "$id" }'
]
]);
$this->assertEquals($response['headers']['status-code'], 400);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@@ -7,10 +7,7 @@ use PHPUnit\Framework\TestCase;
class CustomIdTest extends TestCase
{
/**
* @var Key
*/
protected $object = null;
protected ?CustomId $object = null;
public function setUp(): void
{
@@ -7,10 +7,7 @@ use PHPUnit\Framework\TestCase;
class ProjectIdTest extends TestCase
{
/**
* @var Key
*/
protected $object = null;
protected ?ProjectId $object = null;
public function setUp(): void
{
@@ -4,6 +4,8 @@ namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Base;
use PHPUnit\Framework\TestCase;
use Utopia\Database\Document;
use Utopia\Database\Query;
class CollectionTest extends TestCase
{
@@ -25,17 +27,17 @@ class CollectionTest extends TestCase
public function testValid(): void
{
$validator = new Base('users', ['name', 'search']);
$this->assertEquals(true, $validator->isValid(['cursorAfter("asdf")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("name", "value")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['limit(10)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['offset(10)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['orderAsc("name")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::cursorAfter(new Document(['$id' => 'asdf']))]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('name', ['value'])]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::limit(10)]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::offset(10)]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::orderAsc('name')]), $validator->getDescription());
}
public function testMissingIndex(): void
{
$validator = new Base('users', ['name']);
$this->assertEquals(false, $validator->isValid(['equal("dne", "value")']), $validator->getDescription());
$this->assertEquals(false, $validator->isValid(['orderAsc("dne")']), $validator->getDescription());
$this->assertEquals(false, $validator->isValid([Query::equal('dne', ['value'])]), $validator->getDescription());
$this->assertEquals(false, $validator->isValid([Query::orderAsc('dne')]), $validator->getDescription());
}
}
@@ -4,6 +4,7 @@ namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Users;
use PHPUnit\Framework\TestCase;
use Utopia\Database\Query;
class UsersTest extends TestCase
{
@@ -23,17 +24,17 @@ class UsersTest extends TestCase
* Test for Success
*/
$this->assertEquals(true, $validator->isValid([]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("name", "value")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("email", "value")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("phone", "value")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['greaterThan("passwordUpdate", "2020-10-15 06:38")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['greaterThan("registration", "2020-10-15 06:38")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("emailVerification", true)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("phoneVerification", true)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('name', ['value'])]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('email', ['value'])]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('phone', ['value'])]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::greaterThan('passwordUpdate', '2020-10-15 06:38')]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::greaterThan('registration', '2020-10-15 06:38')]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('emailVerification', [true])]), $validator->getDescription());
$this->assertEquals(true, $validator->isValid([Query::equal('phoneVerification', [true])]), $validator->getDescription());
/**
* Test for Failure
*/
$this->assertEquals(false, $validator->isValid(['equal("password", "value")']), $validator->getDescription());
$this->assertEquals(false, $validator->isValid([Query::equal('password', ['value'])]), $validator->getDescription());
}
}