Merge branch '1.5.x' into feat-check-certificate-validity

This commit is contained in:
Khushboo Verma
2024-02-02 16:49:18 +05:30
80 changed files with 1810 additions and 863 deletions
+1
View File
@@ -99,3 +99,4 @@ _APP_ASSISTANT_OPENAI_API_KEY=
_APP_MESSAGE_SMS_TEST_DSN=
_APP_MESSAGE_EMAIL_TEST_DSN=
_APP_MESSAGE_PUSH_TEST_DSN=
_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
+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.');
}
-22
View File
@@ -1573,17 +1573,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,
@@ -1744,17 +1733,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,
+1 -1
View File
@@ -4,8 +4,8 @@
* List of server wide error codes and their respective messages.
*/
use Appwrite\Enum\MessageStatus;
use Appwrite\Extend\Exception;
use Appwrite\Messaging\Status as MessageStatus;
return [
/** General Errors */
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+46 -53
View File
@@ -8,7 +8,7 @@ use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Auth\SecurityPhrase;
use Appwrite\Auth\Phrase;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\Host;
@@ -169,7 +169,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);
}
@@ -273,7 +274,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())),
@@ -461,8 +462,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->label('docs', false)
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('usage.params', ['provider:{request.provider}'])
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 provider.')
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
@@ -607,7 +606,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());
}
}
@@ -867,7 +866,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']);
@@ -885,7 +884,6 @@ App::get('/v1/account/identities')
->desc('List Identities')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listIdentities')
@@ -906,7 +904,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) {
@@ -989,7 +987,7 @@ App::post('/v1/account/tokens/magic-url')
->param('userId', '', new CustomId(), 'User 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('email', '', new Email(), 'User email.')
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('securityPhrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of authentication flow.', true)
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
->inject('request')
->inject('response')
->inject('user')
@@ -998,14 +996,14 @@ App::post('/v1/account/tokens/magic-url')
->inject('locale')
->inject('queueForEvents')
->inject('queueForMails')
->action(function (string $userId, string $email, string $url, bool $securityPhrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
if ($securityPhrase === true) {
$securityPhrase = SecurityPhrase::generate();
if ($phrase === true) {
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
@@ -1087,7 +1085,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';
@@ -1115,7 +1113,7 @@ App::post('/v1/account/tokens/magic-url')
->setParam('{{thanks}}', $locale->getText("emails.magicSession.thanks"))
->setParam('{{signature}}', $locale->getText("emails.magicSession.signature"));
if (!empty($securityPhrase)) {
if (!empty($phrase)) {
$message->setParam('{{securityPhrase}}', $locale->getText("emails.magicSession.securityPhrase"));
} else {
$message->setParam('{{securityPhrase}}', '');
@@ -1179,7 +1177,7 @@ App::post('/v1/account/tokens/magic-url')
'agentDevice' => '<strong>' . ( $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN') . '</strong>',
'agentClient' => '<strong>' . ($agentClient['clientName'] ?? 'UNKNOWN') . '</strong>',
'agentOs' => '<strong>' . ($agentOs['osName'] ?? 'UNKNOWN') . '</strong>',
'phrase' => '<strong>' . (!empty($securityPhrase) ? $securityPhrase : '') . '</strong>'
'phrase' => '<strong>' . (!empty($phrase) ? $phrase : '') . '</strong>'
];
$queueForMails
@@ -1199,8 +1197,8 @@ App::post('/v1/account/tokens/magic-url')
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
if (!empty($securityPhrase)) {
$token->setAttribute('securityPhrase', $securityPhrase);
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
$response
@@ -1228,7 +1226,7 @@ App::post('/v1/account/tokens/email')
->label('abuse-key', 'url:{url},email:{param-email}')
->param('userId', '', new CustomId(), 'User 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('email', '', new Email(), 'User email.')
->param('securityPhrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of authentication flow.', true)
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
->inject('request')
->inject('response')
->inject('user')
@@ -1237,13 +1235,13 @@ App::post('/v1/account/tokens/email')
->inject('locale')
->inject('queueForEvents')
->inject('queueForMails')
->action(function (string $userId, string $email, bool $securityPhrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
if ($securityPhrase === true) {
$securityPhrase = SecurityPhrase::generate();
if ($phrase === true) {
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
@@ -1325,7 +1323,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] ?? [];
@@ -1343,7 +1341,7 @@ App::post('/v1/account/tokens/email')
->setParam('{{thanks}}', $locale->getText("emails.otpSession.thanks"))
->setParam('{{signature}}', $locale->getText("emails.otpSession.signature"));
if (!empty($securityPhrase)) {
if (!empty($phrase)) {
$message->setParam('{{securityPhrase}}', $locale->getText("emails.otpSession.securityPhrase"));
} else {
$message->setParam('{{securityPhrase}}', '');
@@ -1407,7 +1405,7 @@ App::post('/v1/account/tokens/email')
'agentDevice' => '<strong>' . ( $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN') . '</strong>',
'agentClient' => '<strong>' . ($agentClient['clientName'] ?? 'UNKNOWN') . '</strong>',
'agentOs' => '<strong>' . ($agentOs['osName'] ?? 'UNKNOWN') . '</strong>',
'phrase' => '<strong>' . (!empty($securityPhrase) ? $securityPhrase : '') . '</strong>'
'phrase' => '<strong>' . (!empty($phrase) ? $phrase : '') . '</strong>'
];
$queueForMails
@@ -1427,8 +1425,8 @@ App::post('/v1/account/tokens/email')
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
if (!empty($securityPhrase)) {
$token->setAttribute('securityPhrase', $securityPhrase);
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
$response
@@ -1488,9 +1486,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);
@@ -1543,7 +1541,6 @@ App::put('/v1/account/sessions/magic-url')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', ['updateMagicURLSession', 'updatePhoneSession'])
@@ -1573,7 +1570,6 @@ App::post('/v1/account/sessions/token')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createSession')
@@ -1687,7 +1683,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();
@@ -1713,7 +1709,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');
@@ -1870,7 +1866,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())
@@ -2006,7 +2002,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())
@@ -2021,7 +2017,6 @@ App::get('/v1/account')
->desc('Get account')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'get')
@@ -2034,6 +2029,9 @@ App::get('/v1/account')
->inject('response')
->inject('user')
->action(function (Response $response, Document $user) {
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
@@ -2042,7 +2040,6 @@ App::get('/v1/account/prefs')
->desc('Get account preferences')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'getPrefs')
@@ -2065,7 +2062,6 @@ App::get('/v1/account/sessions')
->desc('List sessions')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listSessions')
@@ -2101,7 +2097,6 @@ App::get('/v1/account/logs')
->desc('List logs')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listLogs')
@@ -2162,7 +2157,6 @@ App::get('/v1/account/sessions/:sessionId')
->desc('Get session')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'getSession')
@@ -2385,7 +2379,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);
}
@@ -2466,7 +2460,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);
}
@@ -2612,7 +2606,7 @@ App::delete('/v1/account/sessions/:sessionId')
;
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2691,7 +2685,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())
@@ -2752,7 +2746,7 @@ App::delete('/v1/account/sessions')
}
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
@@ -2837,7 +2831,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]);
@@ -3013,7 +3007,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())
@@ -3084,7 +3078,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]);
@@ -3233,7 +3227,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)
@@ -3306,7 +3300,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');
@@ -3402,7 +3396,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())
@@ -3459,7 +3453,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())
@@ -3476,7 +3470,6 @@ App::delete('/v1/account')
->label('scope', 'accounts.write')
->label('audits.event', 'user.delete')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'account')
->label('sdk.method', 'delete')
+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) {
+303 -73
View File
@@ -2,11 +2,11 @@
use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Enum\MessageStatus;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Messaging;
use Appwrite\Extend\Exception;
use Appwrite\Messaging\Status as MessageStatus;
use Appwrite\Network\Validator\Email;
use Appwrite\Permission;
use Appwrite\Role;
@@ -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, 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. 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('fromEmail', $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, 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 $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('fromEmail', $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'])
@@ -761,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) {
@@ -1085,6 +1168,125 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
->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 = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
$queueForEvents
->setParam('providerId', $provider->getId());
$response
->dynamic($provider, Response::MODEL_PROVIDER);
});
App::patch('/v1/messaging/providers/msg91/:providerId')
->desc('Update Msg91 provider')
->groups(['api', 'messaging'])
@@ -1701,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 {
@@ -1751,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) {
@@ -1898,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()) {
@@ -1913,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
@@ -2078,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) {
@@ -2301,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)
@@ -2311,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;
@@ -2350,7 +2544,6 @@ App::post('/v1/messaging/messages/email')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'scheduledAt' => $scheduledAt,
'data' => [
'subject' => $subject,
@@ -2420,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')
@@ -2429,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;
@@ -2466,7 +2658,6 @@ App::post('/v1/messaging/messages/sms')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'data' => [
'content' => $content,
],
@@ -2532,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)
@@ -2548,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;
@@ -2595,7 +2785,6 @@ App::post('/v1/messaging/messages/push')
'topics' => $topics,
'users' => $users,
'targets' => $targets,
'description' => $description,
'scheduledAt' => $scheduledAt,
'data' => $pushData,
'status' => $status,
@@ -2663,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) {
@@ -2806,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) {
@@ -2869,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)
@@ -2882,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()) {
@@ -2933,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);
}
@@ -3007,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)
@@ -3017,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()) {
@@ -3056,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([
@@ -3126,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)
@@ -3144,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()) {
@@ -3215,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([
@@ -3266,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) {
@@ -856,7 +856,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())
@@ -972,13 +972,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)));
@@ -1057,7 +1057,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));
+13 -84
View File
@@ -144,7 +144,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);
}
@@ -507,7 +507,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())
@@ -543,7 +543,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) {
@@ -838,7 +838,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) {
@@ -883,7 +883,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) {
@@ -957,7 +957,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')
@@ -1183,7 +1183,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);
}
@@ -1245,7 +1245,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);
}
@@ -1407,7 +1407,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())
@@ -1417,76 +1417,6 @@ App::patch('/v1/users/:userId/targets/:targetId')
->dynamic($target, Response::MODEL_TARGET);
});
App::post('/v1/users/:userId/sessions')
->desc('Create session')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'users.write')
->label('audits.event', 'session.create')
->label('audits.resource', 'user/{request.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users')
->label('sdk.method', 'createSession')
->label('sdk.description', '/docs/references/users/create-session.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION)
->param('userId', '', new CustomId(), 'User 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.')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
$user = $dbForProject->getDocument('users', $userId);
if ($user === false || $user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$secret = Auth::codeGenerator();
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
$session = new Document(array_merge(
[
'$id' => ID::unique(),
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'provider' => Auth::SESSION_PROVIDER_SERVER,
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
],
$detector->getOS(),
$detector->getClient(),
$detector->getDevice()
));
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
$session = $dbForProject->createDocument('sessions', $session);
$session
->setAttribute('secret', $secret)
->setAttribute('expire', $expire)
->setAttribute('countryName', $countryName);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload($response->output($session, Response::MODEL_SESSION));
return $response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($session, Response::MODEL_SESSION);
});
App::post('/v1/users/:userId/tokens')
->desc('Create token')
->groups(['api', 'users'])
@@ -1494,7 +1424,6 @@ App::post('/v1/users/:userId/tokens')
->label('scope', 'users.write')
->label('audits.event', 'tokens.create')
->label('audits.resource', 'user/{request.userId}')
->label('usage.metric', 'tokens.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users')
->label('sdk.method', 'createToken')
@@ -1531,7 +1460,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);
@@ -1578,7 +1507,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())
@@ -1621,7 +1550,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())
@@ -1710,7 +1639,7 @@ 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)
+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
@@ -511,7 +511,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);
@@ -525,7 +525,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());
}
}
}
@@ -739,7 +739,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
@@ -147,7 +147,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'),
+15 -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;
@@ -804,10 +806,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
));
@@ -829,12 +831,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()) {
@@ -843,7 +843,7 @@ $register->set('pools', function () {
default => null
};
$adapter->setDefaultDatabase($dsn->getPath());
$adapter->setDatabase($dsn->getPath());
break;
case 'pubsub':
$adapter = $resource();
@@ -863,7 +863,6 @@ $register->set('pools', function () {
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
break;
}
return $adapter;
@@ -880,22 +879,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 () {
+3 -3
View File
@@ -15,7 +15,7 @@ $image = $this->getParam('image', '');
services:
traefik:
image: traefik:2.9
image: traefik:2.11
container_name: appwrite-traefik
<<: *x-logging
command:
@@ -735,7 +735,7 @@ services:
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
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
restart: unless-stopped
@@ -751,7 +751,7 @@ services:
command: 'mysqld --innodb-flush-method=fsync'
redis:
image: redis:7.0.4-alpine
image: redis:7.2.4-alpine
container_name: appwrite-redis
<<: *x-logging
restart: unless-stopped
+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.*",
@@ -74,7 +74,7 @@
"chillerlan/php-qrcode": "4.3.4",
"adhocore/jwt": "1.1.2",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.7.1"
"league/csv": "^9.14"
},
"repositories": [
{
@@ -96,7 +96,7 @@
},
"config": {
"platform": {
"php": "8.0"
"php": "8.2"
}
}
}
Generated
+74 -73
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": "758fc8f128df402e823dd2cb340bf78b",
"content-hash": "4b9b6ff602a179493e0196636d961e9c",
"packages": [
{
"name": "adhocore/jwt",
@@ -463,34 +463,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",
@@ -520,7 +525,7 @@
}
],
"description": "CSV data manipulation made easy in PHP",
"homepage": "http://csv.thephpleague.com",
"homepage": "https://csv.thephpleague.com",
"keywords": [
"convert",
"csv",
@@ -543,7 +548,7 @@
"type": "github"
}
],
"time": "2021-04-17T16:32:08+00:00"
"time": "2023-12-29T07:34:53+00:00"
},
{
"name": "matomo/device-detector",
@@ -898,23 +903,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.*",
@@ -941,9 +946,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",
@@ -993,21 +998,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.*",
@@ -1034,9 +1039,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",
@@ -1190,16 +1195,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": {
@@ -1240,9 +1245,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",
@@ -1306,16 +1311,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": {
@@ -1347,22 +1352,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": {
@@ -1392,22 +1397,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": {
@@ -1415,6 +1420,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"
},
@@ -1428,12 +1435,6 @@
"license": [
"MIT"
],
"authors": [
{
"name": "Eldad Fux",
"email": "eldad@appwrite.io"
}
],
"description": "A simple Image manipulation library",
"keywords": [
"framework",
@@ -1444,9 +1445,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",
@@ -1554,16 +1555,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": {
@@ -1598,9 +1599,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",
@@ -2473,16 +2474,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": {
@@ -2514,9 +2515,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",
@@ -5172,7 +5173,7 @@
"ext-fileinfo": "*"
},
"platform-overrides": {
"php": "8.0"
"php": "8.2"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.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
+4 -3
View File
@@ -14,7 +14,7 @@ version: '3'
services:
traefik:
image: traefik:2.9
image: traefik:2.11
<<: *x-logging
container_name: appwrite-traefik
command:
@@ -297,6 +297,7 @@ services:
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
appwrite-worker-deletes:
entrypoint: worker-deletes
@@ -885,7 +886,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:
@@ -915,7 +916,7 @@ services:
# - SMARTHOST_PORT=587
redis:
image: redis:7.0.4-alpine
image: redis:7.2.4-alpine
<<: *x-logging
container_name: appwrite-redis
command: >
+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;
@@ -2,7 +2,7 @@
namespace Appwrite\Auth;
class SecurityPhrase
class Phrase
{
public static function generate(): string
{
+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,8 +1,8 @@
<?php
namespace Appwrite\Enum;
namespace Appwrite\Messaging;
class MessageStatus
class Status
{
/**
* Message that is not ready to be sent
+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 -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());
+4 -4
View File
@@ -411,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());
@@ -468,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);
}
);
}
@@ -603,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', [
+24 -15
View File
@@ -2,8 +2,8 @@
namespace Appwrite\Platform\Workers;
use Appwrite\Enum\MessageStatus;
use Appwrite\Extend\Exception;
use Appwrite\Messaging\Status as MessageStatus;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Helpers\ID;
@@ -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;
@@ -407,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
};
@@ -418,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;
}
+3 -4
View File
@@ -16,7 +16,6 @@ use Utopia\Queue\Message;
class Webhooks extends Action
{
private array $errors = [];
private const MAX_FAILED_ATTEMPTS = 10;
private const MAX_FILE_SIZE = 5242880; // 5 MB
public static function getName(): string
@@ -157,19 +156,19 @@ class Webhooks extends Action
$webhook->setAttribute('logs', $logs);
if ($attempts >= self::MAX_FAILED_ATTEMPTS) {
if ($attempts >= \intval(App::getEnv('_APP_WEBHOOK_MAX_FAILED_ATTEMPTS', '10'))) {
$webhook->setAttribute('enabled', false);
$this->sendEmailAlert($attempts, $statusCode, $webhook, $project, $dbForConsole, $queueForMails);
}
$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());
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ class Token extends Model
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('securityPhrase', [
->addRule('phrase', [
'type' => self::TYPE_STRING,
'description' => 'Security phrase of a token. Empty if security phrase was not requested when creating a token. It includes randomly generated phrase which is also sent in the external resource such as email.',
'default' => '',
+6 -5
View File
@@ -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
{
@@ -147,7 +148,7 @@ trait AccountBase
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['expire']);
$this->assertEmpty($response['body']['secret']);
$this->assertEmpty($response['body']['securityPhrase']);
$this->assertEmpty($response['body']['phrase']);
$userId = $response['body']['userId'];
@@ -207,21 +208,21 @@ trait AccountBase
]), [
'userId' => ID::unique(),
'email' => 'otpuser@appwrite.io',
'securityPhrase' => true
'phrase' => true
]);
$this->assertEquals($response['headers']['status-code'], 201);
$this->assertNotEmpty($response['body']['securityPhrase']);
$this->assertNotEmpty($response['body']['phrase']);
$this->assertEmpty($response['body']['secret']);
$this->assertEquals($userId, $response['body']['userId']);
$securityPhrase = $response['body']['securityPhrase'];
$phrase = $response['body']['phrase'];
$lastEmail = $this->getLastEmail();
$this->assertEquals('otpuser@appwrite.io', $lastEmail['to'][0]['address']);
$this->assertEquals('OTP for ' . $this->getProject()['name'] . ' Login', $lastEmail['subject']);
$this->assertStringContainsStringIgnoringCase('security phrase', $lastEmail['text']);
$this->assertStringContainsStringIgnoringCase($securityPhrase, $lastEmail['text']);
$this->assertStringContainsStringIgnoringCase($phrase, $lastEmail['text']);
$response = $this->client->call(Client::METHOD_POST, '/account/tokens/email', array_merge([
'origin' => 'http://localhost',
@@ -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);
@@ -2323,7 +2331,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
$this->assertEmpty($response['body']['securityPhrase']);
$this->assertEmpty($response['body']['phrase']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
$userId = $response['body']['userId'];
@@ -2391,15 +2399,15 @@ class AccountCustomClientTest extends Scope
]), [
'userId' => ID::unique(),
'email' => $email,
'securityPhrase' => true
'phrase' => true
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertNotEmpty($response['body']['securityPhrase']);
$this->assertNotEmpty($response['body']['phrase']);
$lastEmail = $this->getLastEmail();
$this->assertStringContainsStringIgnoringCase($response['body']['securityPhrase'], $lastEmail['text']);
$this->assertStringContainsStringIgnoringCase($response['body']['phrase'], $lastEmail['text']);
$data['token'] = $token;
$data['id'] = $userId;
@@ -120,6 +120,47 @@ class AccountCustomServerTest extends Scope
]);
}
/**
* @depends testCreateAccountSession
*/
public function testGetAccount($data): array
{
$email = $data['email'] ?? '';
$name = $data['name'] ?? '';
$session = $data['session'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-session' => $session,
]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$this->assertEquals($response['body']['name'], $name);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertNotEmpty($response['body']['accessedAt']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
public function testCreateAnonymousAccount()
{
/**
+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(),
],
]
];
+134 -65
View File
@@ -2,10 +2,11 @@
namespace Tests\E2E\Services\Messaging;
use Appwrite\Enum\MessageStatus;
use Appwrite\Messaging\Status as 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',
@@ -223,7 +243,7 @@ trait MessagingBase
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(10, \count($response['body']['providers']));
$this->assertEquals(11, \count($response['body']['providers']));
return $providers;
}
@@ -269,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'];
}
@@ -285,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'];
}
@@ -298,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(),
],
]);
@@ -328,7 +334,7 @@ trait MessagingBase
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => [
'greaterThan("total", 0)'
Query::greaterThan('total', 0)->toString(),
],
]);
@@ -350,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']);
}
@@ -407,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 [
@@ -519,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);
@@ -532,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);
@@ -544,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);
@@ -560,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);
@@ -570,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);
@@ -580,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);
@@ -590,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);
@@ -600,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);
@@ -626,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']);
}
@@ -643,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
@@ -660,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'];
@@ -724,7 +798,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -746,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',
@@ -785,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'],
@@ -881,7 +958,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -1042,7 +1118,6 @@ trait MessagingBase
], [
'topicId' => ID::unique(),
'name' => 'topic1',
'description' => 'Test Topic'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
@@ -1170,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']);
@@ -526,7 +538,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());
}
}