Merge branch '1.5.x' into 1.5.x-api-descriptions
This commit is contained in:
commit
0cde18ef74
29 changed files with 796 additions and 262 deletions
|
@ -178,7 +178,7 @@ return [
|
||||||
],
|
],
|
||||||
Exception::USER_SESSION_ALREADY_EXISTS => [
|
Exception::USER_SESSION_ALREADY_EXISTS => [
|
||||||
'name' => Exception::USER_SESSION_ALREADY_EXISTS,
|
'name' => Exception::USER_SESSION_ALREADY_EXISTS,
|
||||||
'description' => 'Creation of anonymous users is prohibited when a session is active.',
|
'description' => 'Creation of a session is prohibited when a session is active.',
|
||||||
'code' => 401,
|
'code' => 401,
|
||||||
],
|
],
|
||||||
Exception::USER_NOT_FOUND => [
|
Exception::USER_NOT_FOUND => [
|
||||||
|
|
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
|
@ -1 +1 @@
|
||||||
Subproject commit 0a007a3b1b6eafc39dc19b7129f41643102f9676
|
Subproject commit f196bcfb485adfb36324aabf32a3449471319bbd
|
|
@ -228,7 +228,6 @@ App::post('/v1/account/sessions/email')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('hooks')
|
->inject('hooks')
|
||||||
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) {
|
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) {
|
||||||
|
|
||||||
$email = \strtolower($email);
|
$email = \strtolower($email);
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
|
|
||||||
|
@ -553,7 +552,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||||
|
@ -675,6 +673,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
if (!empty($userWithMatchingEmail)) {
|
if (!empty($userWithMatchingEmail)) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sessionUpgrade = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sessions = $user->getAttribute('sessions', []);
|
$sessions = $user->getAttribute('sessions', []);
|
||||||
|
@ -704,7 +704,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not.
|
* Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not.
|
||||||
*/
|
*/
|
||||||
$isVerified = $oauth2->isEmailVerified($accessToken);
|
$isVerified = $oauth2->isEmailVerified($accessToken);
|
||||||
|
|
||||||
|
@ -947,6 +947,20 @@ 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'));
|
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($sessionUpgrade) && $sessionUpgrade) {
|
||||||
|
foreach ($user->getAttribute('targets', []) as $target) {
|
||||||
|
if ($target->getAttribute('providerType') !== MESSAGE_TYPE_PUSH) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target
|
||||||
|
->setAttribute('sessionId', $session->getId())
|
||||||
|
->setAttrubte('sessionInternalId', $session->getInternalId());
|
||||||
|
|
||||||
|
$dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
$state['success']['query'] = URLParser::unparseQuery($query);
|
$state['success']['query'] = URLParser::unparseQuery($query);
|
||||||
|
@ -1636,7 +1650,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||||
App::put('/v1/account/sessions/magic-url')
|
App::put('/v1/account/sessions/magic-url')
|
||||||
->desc('Update magic URL session')
|
->desc('Update magic URL session')
|
||||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account', 'session'])
|
||||||
->label('scope', 'sessions.write')
|
->label('scope', 'sessions.write')
|
||||||
->label('audits.event', 'session.create')
|
->label('audits.event', 'session.create')
|
||||||
->label('audits.resource', 'user/{response.userId}')
|
->label('audits.resource', 'user/{response.userId}')
|
||||||
|
@ -1666,7 +1680,7 @@ App::put('/v1/account/sessions/magic-url')
|
||||||
App::put('/v1/account/sessions/phone')
|
App::put('/v1/account/sessions/phone')
|
||||||
->desc('Update phone session')
|
->desc('Update phone session')
|
||||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account', 'session'])
|
||||||
->label('scope', 'sessions.write')
|
->label('scope', 'sessions.write')
|
||||||
->label('audits.event', 'session.create')
|
->label('audits.event', 'session.create')
|
||||||
->label('audits.resource', 'user/{response.userId}')
|
->label('audits.resource', 'user/{response.userId}')
|
||||||
|
@ -1696,7 +1710,7 @@ App::put('/v1/account/sessions/phone')
|
||||||
App::post('/v1/account/sessions/token')
|
App::post('/v1/account/sessions/token')
|
||||||
->desc('Create session')
|
->desc('Create session')
|
||||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account', 'session'])
|
||||||
->label('scope', 'sessions.write')
|
->label('scope', 'sessions.write')
|
||||||
->label('audits.event', 'session.create')
|
->label('audits.event', 'session.create')
|
||||||
->label('audits.resource', 'user/{response.userId}')
|
->label('audits.resource', 'user/{response.userId}')
|
||||||
|
@ -1919,7 +1933,6 @@ App::post('/v1/account/sessions/anonymous')
|
||||||
->inject('geodb')
|
->inject('geodb')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
|
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
|
||||||
|
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
$roles = Authorization::getRoles();
|
$roles = Authorization::getRoles();
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||||
|
@ -1929,10 +1942,6 @@ App::post('/v1/account/sessions/anonymous')
|
||||||
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
|
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$user->isEmpty()) {
|
|
||||||
throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS, 'Cannot create an anonymous user when logged in');
|
|
||||||
}
|
|
||||||
|
|
||||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||||
|
|
||||||
if ($limit !== 0) {
|
if ($limit !== 0) {
|
||||||
|
@ -2994,7 +3003,7 @@ App::post('/v1/account/recovery')
|
||||||
|
|
||||||
$queueForMails
|
$queueForMails
|
||||||
->setRecipient($profile->getAttribute('email', ''))
|
->setRecipient($profile->getAttribute('email', ''))
|
||||||
->setName($profile->getAttribute('name'))
|
->setName($profile->getAttribute('name', ''))
|
||||||
->setBody($body)
|
->setBody($body)
|
||||||
->setVariables($emailVariables)
|
->setVariables($emailVariables)
|
||||||
->setSubject($subject)
|
->setSubject($subject)
|
||||||
|
|
|
@ -2404,7 +2404,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||||
->param('databaseId', '', new UID(), 'Database ID.')
|
->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('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('key', null, new Key(), 'Index Key.')
|
||||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.')
|
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), '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('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)
|
->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')
|
->inject('response')
|
||||||
|
@ -3090,6 +3090,33 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||||
$processDocument($collection, $document);
|
$processDocument($collection, $document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$select = \array_reduce($queries, function ($result, $query) {
|
||||||
|
return $result || ($query->getMethod() === Query::TYPE_SELECT);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// Check if the SELECT query includes $databaseId and $collectionId
|
||||||
|
$hasDatabaseId = false;
|
||||||
|
$hasCollectionId = false;
|
||||||
|
if ($select) {
|
||||||
|
$hasDatabaseId = \array_reduce($queries, function ($result, $query) {
|
||||||
|
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues()));
|
||||||
|
}, false);
|
||||||
|
$hasCollectionId = \array_reduce($queries, function ($result, $query) {
|
||||||
|
return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues()));
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($select) {
|
||||||
|
foreach ($documents as $document) {
|
||||||
|
if (!$hasDatabaseId) {
|
||||||
|
$document->removeAttribute('$databaseId');
|
||||||
|
}
|
||||||
|
if (!$hasCollectionId) {
|
||||||
|
$document->removeAttribute('$collectionId');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'documents' => $documents,
|
'documents' => $documents,
|
||||||
|
@ -3635,7 +3662,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/databases/usage')
|
App::get('/v1/databases/usage')
|
||||||
->desc('Get usage stats for the database')
|
->desc('Get databases usage stats')
|
||||||
->groups(['api', 'database', 'usage'])
|
->groups(['api', 'database', 'usage'])
|
||||||
->label('scope', 'collections.read')
|
->label('scope', 'collections.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
@ -3713,7 +3740,7 @@ App::get('/v1/databases/usage')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/databases/:databaseId/usage')
|
App::get('/v1/databases/:databaseId/usage')
|
||||||
->desc('Get usage stats for the database')
|
->desc('Get database usage stats')
|
||||||
->groups(['api', 'database', 'usage'])
|
->groups(['api', 'database', 'usage'])
|
||||||
->label('scope', 'collections.read')
|
->label('scope', 'collections.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
@ -3797,7 +3824,7 @@ App::get('/v1/databases/:databaseId/usage')
|
||||||
|
|
||||||
App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||||
->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default'])
|
->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default'])
|
||||||
->desc('Get usage stat')
|
->desc('Get collection usage stats')
|
||||||
->groups(['api', 'database', 'usage'])
|
->groups(['api', 'database', 'usage'])
|
||||||
->label('scope', 'collections.read')
|
->label('scope', 'collections.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
@ -2597,7 +2597,7 @@ App::post('/v1/messaging/messages/email')
|
||||||
->param('cc', [], new ArrayList(new UID()), 'Array of target IDs to be added as CC.', 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('bcc', [], new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
|
||||||
->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
|
->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', 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('draft', false, new Boolean(), 'Is message a draft', true)
|
||||||
->param('html', false, new Boolean(), 'Is content of type HTML', 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)
|
->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')
|
->inject('queueForEvents')
|
||||||
|
@ -2606,11 +2606,19 @@ App::post('/v1/messaging/messages/email')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, array $attachments, 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, array $attachments, bool $draft, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||||
$messageId = $messageId == 'unique()'
|
$messageId = $messageId == 'unique()'
|
||||||
? ID::unique()
|
? ID::unique()
|
||||||
: $messageId;
|
: $messageId;
|
||||||
|
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
|
||||||
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
||||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
}
|
}
|
||||||
|
@ -2737,7 +2745,7 @@ App::post('/v1/messaging/messages/sms')
|
||||||
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
|
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
|
||||||
->param('users', [], new ArrayList(new UID()), 'List of User 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('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', 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('draft', false, new Boolean(), 'Is message a draft', 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)
|
->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')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
@ -2745,11 +2753,19 @@ App::post('/v1/messaging/messages/sms')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('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) {
|
->action(function (string $messageId, string $content, array $topics, array $users, array $targets, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||||
$messageId = $messageId == 'unique()'
|
$messageId = $messageId == 'unique()'
|
||||||
? ID::unique()
|
? ID::unique()
|
||||||
: $messageId;
|
: $messageId;
|
||||||
|
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
|
||||||
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
||||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
}
|
}
|
||||||
|
@ -2854,7 +2870,7 @@ App::post('/v1/messaging/messages/push')
|
||||||
->param('color', '', new Text(256), 'Color for push notification. Available only for Android Platform.', true)
|
->param('color', '', new Text(256), 'Color for push notification. Available only for Android Platform.', true)
|
||||||
->param('tag', '', new Text(256), 'Tag for push notification. Available only for Android Platform.', true)
|
->param('tag', '', new Text(256), 'Tag for push notification. Available only for Android Platform.', true)
|
||||||
->param('badge', '', new Text(256), 'Badge for push notification. Available only for IOS Platform.', true)
|
->param('badge', '', new Text(256), 'Badge for push notification. Available only for IOS Platform.', 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('draft', false, new Boolean(), 'Is message a draft', 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)
|
->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')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
@ -2862,11 +2878,19 @@ App::post('/v1/messaging/messages/push')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $image, 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 $image, string $icon, string $sound, string $color, string $tag, string $badge, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||||
$messageId = $messageId == 'unique()'
|
$messageId = $messageId == 'unique()'
|
||||||
? ID::unique()
|
? ID::unique()
|
||||||
: $messageId;
|
: $messageId;
|
||||||
|
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
|
||||||
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
if ($status !== MessageStatus::DRAFT && \count($topics) === 0 && \count($users) === 0 && \count($targets) === 0) {
|
||||||
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
}
|
}
|
||||||
|
@ -3233,7 +3257,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||||
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
|
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
|
||||||
->param('subject', null, new Text(998), 'Email Subject.', true)
|
->param('subject', null, new Text(998), 'Email Subject.', true)
|
||||||
->param('content', null, new Text(64230), 'Email Content.', true)
|
->param('content', null, new Text(64230), 'Email Content.', true)
|
||||||
->param('status', null, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
|
->param('draft', null, new Boolean(), 'Is message a draft', true)
|
||||||
->param('html', null, new Boolean(), 'Is content of type HTML', true)
|
->param('html', null, new Boolean(), 'Is content of type HTML', true)
|
||||||
->param('cc', null, new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
|
->param('cc', null, new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
|
||||||
->param('bcc', null, new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
|
->param('bcc', null, new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
|
||||||
|
@ -3244,13 +3268,36 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('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) {
|
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $subject, ?string $content, ?bool $draft, ?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);
|
$message = $dbForProject->getDocument('messages', $messageId);
|
||||||
|
|
||||||
if ($message->isEmpty()) {
|
if ($message->isEmpty()) {
|
||||||
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!\is_null($draft) || !\is_null($scheduledAt)) {
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$status !== MessageStatus::DRAFT
|
||||||
|
&& \count($topics ?? $message->getAttribute('topics', [])) === 0
|
||||||
|
&& \count($users ?? $message->getAttribute('users', [])) === 0
|
||||||
|
&& \count($targets ?? $message->getAttribute('targets', [])) === 0
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentScheduledAt = $message->getAttribute('scheduledAt');
|
||||||
|
|
||||||
switch ($message->getAttribute('status')) {
|
switch ($message->getAttribute('status')) {
|
||||||
case MessageStatus::PROCESSING:
|
case MessageStatus::PROCESSING:
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
||||||
|
@ -3260,10 +3307,56 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\is_null($message->getAttribute('scheduledAt')) && $message->getAttribute('scheduledAt') < new \DateTime()) {
|
if (
|
||||||
|
$status === MessageStatus::SCHEDULED
|
||||||
|
&& \is_null($scheduledAt)
|
||||||
|
&& \is_null($currentScheduledAt)
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt) && new \DateTime($currentScheduledAt) < new \DateTime()) {
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
||||||
|
'region' => App::getEnv('_APP_REGION', 'default'),
|
||||||
|
'resourceType' => 'message',
|
||||||
|
'resourceId' => $message->getId(),
|
||||||
|
'resourceInternalId' => $message->getInternalId(),
|
||||||
|
'resourceUpdatedAt' => DateTime::now(),
|
||||||
|
'projectId' => $project->getId(),
|
||||||
|
'schedule' => $scheduledAt,
|
||||||
|
'active' => $status === MessageStatus::SCHEDULED,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$message->setAttribute('scheduleId', $schedule->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
||||||
|
$scheduledStatus = ($status ?? $message->getAttribute('status')) === MessageStatus::SCHEDULED;
|
||||||
|
|
||||||
|
if ($schedule->isEmpty()) {
|
||||||
|
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$schedule
|
||||||
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
|
->setAttribute('active', $scheduledStatus);
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$schedule->setAttribute('schedule', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$message->setAttribute('scheduledAt', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
if (!\is_null($topics)) {
|
if (!\is_null($topics)) {
|
||||||
$message->setAttribute('topics', $topics);
|
$message->setAttribute('topics', $topics);
|
||||||
}
|
}
|
||||||
|
@ -3304,38 +3397,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||||
$message->setAttribute('status', $status);
|
$message->setAttribute('status', $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\is_null($scheduledAt)) {
|
|
||||||
if (\is_null($message->getAttribute(('scheduleId')))) {
|
|
||||||
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
|
||||||
'region' => App::getEnv('_APP_REGION', 'default'),
|
|
||||||
'resourceType' => 'message',
|
|
||||||
'resourceId' => $message->getId(),
|
|
||||||
'resourceInternalId' => $message->getInternalId(),
|
|
||||||
'resourceUpdatedAt' => DateTime::now(),
|
|
||||||
'projectId' => $project->getId(),
|
|
||||||
'schedule' => $scheduledAt,
|
|
||||||
'active' => $status === MessageStatus::SCHEDULED,
|
|
||||||
]));
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
} else {
|
|
||||||
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
|
||||||
|
|
||||||
if ($schedule->isEmpty()) {
|
|
||||||
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$schedule
|
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
|
||||||
->setAttribute('schedule', $scheduledAt)
|
|
||||||
->setAttribute('active', $status === MessageStatus::SCHEDULED);
|
|
||||||
|
|
||||||
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||||
|
|
||||||
if ($status === MessageStatus::PROCESSING) {
|
if ($status === MessageStatus::PROCESSING) {
|
||||||
|
@ -3370,7 +3431,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
->param('users', null, new ArrayList(new UID()), 'List of User 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('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
|
||||||
->param('content', null, new Text(64230), 'Email Content.', true)
|
->param('content', null, new Text(64230), 'Email Content.', true)
|
||||||
->param('status', null, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
|
->param('draft', null, new Boolean(), 'Is message a draft', 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)
|
->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')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
@ -3378,13 +3439,36 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('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) {
|
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $content, ?bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||||
$message = $dbForProject->getDocument('messages', $messageId);
|
$message = $dbForProject->getDocument('messages', $messageId);
|
||||||
|
|
||||||
if ($message->isEmpty()) {
|
if ($message->isEmpty()) {
|
||||||
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!\is_null($draft) || !\is_null($scheduledAt)) {
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$status !== MessageStatus::DRAFT
|
||||||
|
&& \count($topics ?? $message->getAttribute('topics', [])) === 0
|
||||||
|
&& \count($users ?? $message->getAttribute('users', [])) === 0
|
||||||
|
&& \count($targets ?? $message->getAttribute('targets', [])) === 0
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentScheduledAt = $message->getAttribute('scheduledAt');
|
||||||
|
|
||||||
switch ($message->getAttribute('status')) {
|
switch ($message->getAttribute('status')) {
|
||||||
case MessageStatus::PROCESSING:
|
case MessageStatus::PROCESSING:
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
||||||
|
@ -3394,10 +3478,56 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_null($message->getAttribute('scheduledAt')) && $message->getAttribute('scheduledAt') < new \DateTime()) {
|
if (
|
||||||
|
$status === MessageStatus::SCHEDULED
|
||||||
|
&& \is_null($scheduledAt)
|
||||||
|
&& \is_null($currentScheduledAt)
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt) && new \DateTime($currentScheduledAt) < new \DateTime()) {
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
||||||
|
'region' => App::getEnv('_APP_REGION', 'default'),
|
||||||
|
'resourceType' => 'message',
|
||||||
|
'resourceId' => $message->getId(),
|
||||||
|
'resourceInternalId' => $message->getInternalId(),
|
||||||
|
'resourceUpdatedAt' => DateTime::now(),
|
||||||
|
'projectId' => $project->getId(),
|
||||||
|
'schedule' => $scheduledAt,
|
||||||
|
'active' => $status === MessageStatus::SCHEDULED,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$message->setAttribute('scheduleId', $schedule->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
||||||
|
$scheduledStatus = ($status ?? $message->getAttribute('status')) === MessageStatus::SCHEDULED;
|
||||||
|
|
||||||
|
if ($schedule->isEmpty()) {
|
||||||
|
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$schedule
|
||||||
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
|
->setAttribute('active', $scheduledStatus);
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$schedule->setAttribute('schedule', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$message->setAttribute('scheduledAt', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
if (!\is_null($topics)) {
|
if (!\is_null($topics)) {
|
||||||
$message->setAttribute('topics', $topics);
|
$message->setAttribute('topics', $topics);
|
||||||
}
|
}
|
||||||
|
@ -3422,38 +3552,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||||
$message->setAttribute('status', $status);
|
$message->setAttribute('status', $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\is_null($scheduledAt)) {
|
|
||||||
if (\is_null($message->getAttribute(('scheduleId')))) {
|
|
||||||
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
|
||||||
'region' => App::getEnv('_APP_REGION', 'default'),
|
|
||||||
'resourceType' => 'message',
|
|
||||||
'resourceId' => $message->getId(),
|
|
||||||
'resourceInternalId' => $message->getInternalId(),
|
|
||||||
'resourceUpdatedAt' => DateTime::now(),
|
|
||||||
'projectId' => $project->getId(),
|
|
||||||
'schedule' => $scheduledAt,
|
|
||||||
'active' => $status === MessageStatus::SCHEDULED,
|
|
||||||
]));
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
} else {
|
|
||||||
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
|
||||||
|
|
||||||
if ($schedule->isEmpty()) {
|
|
||||||
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$schedule
|
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
|
||||||
->setAttribute('schedule', $scheduledAt)
|
|
||||||
->setAttribute('active', $status === MessageStatus::SCHEDULED);
|
|
||||||
|
|
||||||
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||||
|
|
||||||
if ($status === MessageStatus::PROCESSING) {
|
if ($status === MessageStatus::PROCESSING) {
|
||||||
|
@ -3497,7 +3595,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
->param('color', null, new Text(256), 'Color for push notification. Available only for Android platforms.', true)
|
->param('color', null, new Text(256), 'Color for push notification. Available only for Android platforms.', true)
|
||||||
->param('tag', null, new Text(256), 'Tag for push notification. Available only for Android platforms.', true)
|
->param('tag', null, new Text(256), 'Tag for push notification. Available only for Android platforms.', true)
|
||||||
->param('badge', null, new Integer(), 'Badge for push notification. Available only for iOS platforms.', true)
|
->param('badge', null, new Integer(), 'Badge for push notification. Available only for iOS platforms.', true)
|
||||||
->param('status', null, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true)
|
->param('draft', null, new Boolean(), 'Is message a draft', 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)
|
->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')
|
->inject('queueForEvents')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
@ -3505,13 +3603,36 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $title, ?string $body, ?array $data, ?string $action, ?string $image, ?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 $image, ?string $icon, ?string $sound, ?string $color, ?string $tag, ?int $badge, ?bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||||
$message = $dbForProject->getDocument('messages', $messageId);
|
$message = $dbForProject->getDocument('messages', $messageId);
|
||||||
|
|
||||||
if ($message->isEmpty()) {
|
if ($message->isEmpty()) {
|
||||||
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
throw new Exception(Exception::MESSAGE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!\is_null($draft) || !\is_null($scheduledAt)) {
|
||||||
|
if ($draft) {
|
||||||
|
$status = MessageStatus::DRAFT;
|
||||||
|
} else {
|
||||||
|
$status = \is_null($scheduledAt)
|
||||||
|
? MessageStatus::PROCESSING
|
||||||
|
: MessageStatus::SCHEDULED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$status !== MessageStatus::DRAFT
|
||||||
|
&& \count($topics ?? $message->getAttribute('topics', [])) === 0
|
||||||
|
&& \count($users ?? $message->getAttribute('users', [])) === 0
|
||||||
|
&& \count($targets ?? $message->getAttribute('targets', [])) === 0
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentScheduledAt = $message->getAttribute('scheduledAt');
|
||||||
|
|
||||||
switch ($message->getAttribute('status')) {
|
switch ($message->getAttribute('status')) {
|
||||||
case MessageStatus::PROCESSING:
|
case MessageStatus::PROCESSING:
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
throw new Exception(Exception::MESSAGE_ALREADY_PROCESSING);
|
||||||
|
@ -3521,10 +3642,56 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
throw new Exception(Exception::MESSAGE_ALREADY_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_null($message->getAttribute('scheduledAt')) && $message->getAttribute('scheduledAt') < new \DateTime()) {
|
if (
|
||||||
|
$status === MessageStatus::SCHEDULED
|
||||||
|
&& \is_null($scheduledAt)
|
||||||
|
&& \is_null($currentScheduledAt)
|
||||||
|
) {
|
||||||
|
throw new Exception(Exception::MESSAGE_MISSING_SCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt) && new \DateTime($currentScheduledAt) < new \DateTime()) {
|
||||||
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
throw new Exception(Exception::MESSAGE_ALREADY_SCHEDULED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
||||||
|
'region' => App::getEnv('_APP_REGION', 'default'),
|
||||||
|
'resourceType' => 'message',
|
||||||
|
'resourceId' => $message->getId(),
|
||||||
|
'resourceInternalId' => $message->getInternalId(),
|
||||||
|
'resourceUpdatedAt' => DateTime::now(),
|
||||||
|
'projectId' => $project->getId(),
|
||||||
|
'schedule' => $scheduledAt,
|
||||||
|
'active' => $status === MessageStatus::SCHEDULED,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$message->setAttribute('scheduleId', $schedule->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($currentScheduledAt)) {
|
||||||
|
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
||||||
|
$scheduledStatus = ($status ?? $message->getAttribute('status')) === MessageStatus::SCHEDULED;
|
||||||
|
|
||||||
|
if ($schedule->isEmpty()) {
|
||||||
|
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$schedule
|
||||||
|
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||||
|
->setAttribute('active', $scheduledStatus);
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$schedule->setAttribute('schedule', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_null($scheduledAt)) {
|
||||||
|
$message->setAttribute('scheduledAt', $scheduledAt);
|
||||||
|
}
|
||||||
|
|
||||||
if (!\is_null($topics)) {
|
if (!\is_null($topics)) {
|
||||||
$message->setAttribute('topics', $topics);
|
$message->setAttribute('topics', $topics);
|
||||||
}
|
}
|
||||||
|
@ -3613,38 +3780,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||||
$message->setAttribute('status', $status);
|
$message->setAttribute('status', $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\is_null($scheduledAt)) {
|
|
||||||
if (\is_null($message->getAttribute(('scheduleId')))) {
|
|
||||||
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
|
||||||
'region' => App::getEnv('_APP_REGION', 'default'),
|
|
||||||
'resourceType' => 'message',
|
|
||||||
'resourceId' => $message->getId(),
|
|
||||||
'resourceInternalId' => $message->getInternalId(),
|
|
||||||
'resourceUpdatedAt' => DateTime::now(),
|
|
||||||
'projectId' => $project->getId(),
|
|
||||||
'schedule' => $scheduledAt,
|
|
||||||
'active' => $status === MessageStatus::SCHEDULED,
|
|
||||||
]));
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
} else {
|
|
||||||
$schedule = $dbForConsole->getDocument('schedules', $message->getAttribute('scheduleId'));
|
|
||||||
|
|
||||||
if ($schedule->isEmpty()) {
|
|
||||||
throw new Exception(Exception::SCHEDULE_NOT_FOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
$schedule
|
|
||||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
|
||||||
->setAttribute('schedule', $scheduledAt)
|
|
||||||
->setAttribute('active', $status === MessageStatus::SCHEDULED);
|
|
||||||
|
|
||||||
$dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
$message->setAttribute('scheduleId', $schedule->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||||
|
|
||||||
if ($status === MessageStatus::PROCESSING) {
|
if ($status === MessageStatus::PROCESSING) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ use Utopia\Validator\Text;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Validator\WhiteList;
|
||||||
|
|
||||||
App::get('/v1/project/usage')
|
App::get('/v1/project/usage')
|
||||||
->desc('Get usage stats for a project')
|
->desc('Get project usage stats')
|
||||||
->groups(['api', 'usage'])
|
->groups(['api', 'usage'])
|
||||||
->label('scope', 'projects.read')
|
->label('scope', 'projects.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
@ -1514,7 +1514,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/usage')
|
App::get('/v1/storage/usage')
|
||||||
->desc('Get usage stats for storage')
|
->desc('Get storage usage stats')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'files.read')
|
->label('scope', 'files.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
@ -1593,7 +1593,7 @@ App::get('/v1/storage/usage')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/storage/:bucketId/usage')
|
App::get('/v1/storage/:bucketId/usage')
|
||||||
->desc('Get usage stats for storage bucket')
|
->desc('Get bucket usage stats')
|
||||||
->groups(['api', 'storage'])
|
->groups(['api', 'storage'])
|
||||||
->label('scope', 'files.read')
|
->label('scope', 'files.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
@ -71,7 +71,14 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
: ID::custom($userId);
|
: ID::custom($userId);
|
||||||
|
|
||||||
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
if ($project->getAttribute('auths', [])['personalDataCheck'] ?? false) {
|
||||||
$personalDataValidator = new PersonalData($userId, $email, $name, $phone);
|
$personalDataValidator = new PersonalData(
|
||||||
|
$userId,
|
||||||
|
$email,
|
||||||
|
$name,
|
||||||
|
$phone,
|
||||||
|
strict: false,
|
||||||
|
allowEmpty: true
|
||||||
|
);
|
||||||
if (!$personalDataValidator->isValid($plaintextPassword)) {
|
if (!$personalDataValidator->isValid($plaintextPassword)) {
|
||||||
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
|
||||||
}
|
}
|
||||||
|
@ -1968,7 +1975,7 @@ App::delete('/v1/users/identities/:identityId')
|
||||||
});
|
});
|
||||||
|
|
||||||
App::get('/v1/users/usage')
|
App::get('/v1/users/usage')
|
||||||
->desc('Get usage stats for the users API')
|
->desc('Get users usage stats')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
->label('scope', 'users.read')
|
->label('scope', 'users.read')
|
||||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||||
|
|
|
@ -461,6 +461,20 @@ App::init()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::init()
|
||||||
|
->groups(['session'])
|
||||||
|
->inject('user')
|
||||||
|
->inject('request')
|
||||||
|
->action(function (Document $user, Request $request) {
|
||||||
|
if (\str_contains($request->getURI(), 'oauth2')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit user session
|
* Limit user session
|
||||||
*
|
*
|
||||||
|
@ -497,6 +511,7 @@ App::shutdown()
|
||||||
$session = array_shift($sessions);
|
$session = array_shift($sessions);
|
||||||
$dbForProject->deleteDocument('sessions', $session->getId());
|
$dbForProject->deleteDocument('sessions', $session->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbForProject->purgeCachedDocument('users', $userId);
|
$dbForProject->purgeCachedDocument('users', $userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@appwrite.io/repo",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "@appwrite.io/repo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,9 +12,10 @@ class PersonalData extends Password
|
||||||
protected ?string $email = null,
|
protected ?string $email = null,
|
||||||
protected ?string $name = null,
|
protected ?string $name = null,
|
||||||
protected ?string $phone = null,
|
protected ?string $phone = null,
|
||||||
protected bool $strict = false
|
protected bool $strict = false,
|
||||||
|
protected bool $allowEmpty = false,
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct($allowEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -252,6 +252,14 @@ class V20 extends Migration
|
||||||
Console::warning("'totpBackup' from {$id}: {$th->getMessage()}");
|
Console::warning("'totpBackup' from {$id}: {$th->getMessage()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create challenges attribute
|
||||||
|
try {
|
||||||
|
$this->createAttributeFromCollection($this->projectDB, $id, 'challenges');
|
||||||
|
$this->projectDB->purgeCachedCollection($id);
|
||||||
|
} catch (Throwable $th) {
|
||||||
|
Console::warning("'challenges' from {$id}: {$th->getMessage()}");
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'projects':
|
case 'projects':
|
||||||
// Rename providers authProviders to oAuthProviders
|
// Rename providers authProviders to oAuthProviders
|
||||||
|
@ -543,9 +551,11 @@ class V20 extends Migration
|
||||||
$document->setAttribute('expire', $expire);
|
$document->setAttribute('expire', $expire);
|
||||||
|
|
||||||
$factors = match ($document->getAttribute('provider')) {
|
$factors = match ($document->getAttribute('provider')) {
|
||||||
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
|
Auth::SESSION_PROVIDER_EMAIL => ['password'],
|
||||||
Auth::SESSION_PROVIDER_PHONE => ['phone'],
|
Auth::SESSION_PROVIDER_PHONE => ['phone'],
|
||||||
default => ['password'],
|
Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'],
|
||||||
|
Auth::SESSION_PROVIDER_TOKEN => ['token'],
|
||||||
|
default => ['email'],
|
||||||
};
|
};
|
||||||
|
|
||||||
$document->setAttribute('factors', $factors);
|
$document->setAttribute('factors', $factors);
|
||||||
|
|
|
@ -17,8 +17,8 @@ use function Swoole\Coroutine\run;
|
||||||
|
|
||||||
class ScheduleMessages extends ScheduleBase
|
class ScheduleMessages extends ScheduleBase
|
||||||
{
|
{
|
||||||
public const UPDATE_TIMER = 10; // seconds
|
public const UPDATE_TIMER = 3; // seconds
|
||||||
public const ENQUEUE_TIMER = 60; // seconds
|
public const ENQUEUE_TIMER = 4; // seconds
|
||||||
|
|
||||||
public static function getName(): string
|
public static function getName(): string
|
||||||
{
|
{
|
||||||
|
@ -37,14 +37,14 @@ class ScheduleMessages extends ScheduleBase
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = DateTime::now();
|
$now = new \DateTime();
|
||||||
$scheduledAt = DateTime::formatTz($schedule['schedule']);
|
$scheduledAt = new \DateTime($schedule['schedule']);
|
||||||
|
|
||||||
if ($scheduledAt > $now) {
|
if ($scheduledAt > $now) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
\go(function () use ($schedule, $pools, $dbForConsole) {
|
\go(function () use ($now, $schedule, $pools, $dbForConsole) {
|
||||||
$queue = $pools->get('queue')->pop();
|
$queue = $pools->get('queue')->pop();
|
||||||
$connection = $queue->getResource();
|
$connection = $queue->getResource();
|
||||||
$queueForMessaging = new Messaging($connection);
|
$queueForMessaging = new Messaging($connection);
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Messaging extends Action
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
if (empty($payload)) {
|
if (empty($payload)) {
|
||||||
throw new Exception('Missing payload');
|
throw new \Exception('Missing payload');
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $payload['type'] ?? '';
|
$type = $payload['type'] ?? '';
|
||||||
|
@ -105,7 +105,7 @@ class Messaging extends Action
|
||||||
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $deviceForLocalFiles,);
|
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $deviceForLocalFiles,);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Unknown message type: ' . $type);
|
throw new \Exception('Unknown message type: ' . $type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +118,12 @@ class Messaging extends Action
|
||||||
$topicIds = $message->getAttribute('topics', []);
|
$topicIds = $message->getAttribute('topics', []);
|
||||||
$targetIds = $message->getAttribute('targets', []);
|
$targetIds = $message->getAttribute('targets', []);
|
||||||
$userIds = $message->getAttribute('users', []);
|
$userIds = $message->getAttribute('users', []);
|
||||||
|
$providerType = $message->getAttribute('providerType');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<Document> $recipients
|
* @var array<Document> $allTargets
|
||||||
*/
|
*/
|
||||||
$recipients = [];
|
$allTargets = [];
|
||||||
|
|
||||||
if (\count($topicIds) > 0) {
|
if (\count($topicIds) > 0) {
|
||||||
$topics = $dbForProject->find('topics', [
|
$topics = $dbForProject->find('topics', [
|
||||||
|
@ -130,9 +131,11 @@ class Messaging extends Action
|
||||||
Query::limit(\count($topicIds)),
|
Query::limit(\count($topicIds)),
|
||||||
]);
|
]);
|
||||||
foreach ($topics as $topic) {
|
foreach ($topics as $topic) {
|
||||||
$targets = \array_filter($topic->getAttribute('targets'), fn(Document $target) =>
|
$targets = \array_filter($topic->getAttribute('targets'), function (Document $target) use ($providerType) {
|
||||||
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
|
return $target->getAttribute('providerType') === $providerType;
|
||||||
$recipients = \array_merge($recipients, $targets);
|
});
|
||||||
|
|
||||||
|
\array_push($allTargets, ...$targets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,23 +145,25 @@ class Messaging extends Action
|
||||||
Query::limit(\count($userIds)),
|
Query::limit(\count($userIds)),
|
||||||
]);
|
]);
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
$targets = \array_filter($user->getAttribute('targets'), fn(Document $target) =>
|
$targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) {
|
||||||
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
|
return $target->getAttribute('providerType') === $providerType;
|
||||||
$recipients = \array_merge($recipients, $targets);
|
});
|
||||||
|
|
||||||
|
\array_push($allTargets, ...$targets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($targetIds) > 0) {
|
if (\count($targetIds) > 0) {
|
||||||
$targets = $dbForProject->find('targets', [
|
$targets = $dbForProject->find('targets', [
|
||||||
Query::equal('$id', $targetIds),
|
Query::equal('$id', $targetIds),
|
||||||
|
Query::equal('providerType', [$providerType]),
|
||||||
Query::limit(\count($targetIds)),
|
Query::limit(\count($targetIds)),
|
||||||
]);
|
]);
|
||||||
$targets = \array_filter($targets, fn(Document $target) =>
|
|
||||||
$target->getAttribute('providerType') === $message->getAttribute('providerType'));
|
\array_push($allTargets, ...$targets);
|
||||||
$recipients = \array_merge($recipients, $targets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($recipients)) {
|
if (empty($allTargets)) {
|
||||||
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
||||||
'status' => MessageStatus::FAILED,
|
'status' => MessageStatus::FAILED,
|
||||||
'deliveryErrors' => ['No valid recipients found.']
|
'deliveryErrors' => ['No valid recipients found.']
|
||||||
|
@ -168,85 +173,82 @@ class Messaging extends Action
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fallback = $dbForProject->findOne('providers', [
|
$default = $dbForProject->findOne('providers', [
|
||||||
Query::equal('enabled', [true]),
|
Query::equal('enabled', [true]),
|
||||||
Query::equal('type', [$recipients[0]->getAttribute('providerType')]),
|
Query::equal('type', [$providerType]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($fallback === false || $fallback->isEmpty()) {
|
if ($default === false || $default->isEmpty()) {
|
||||||
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
$dbForProject->updateDocument('messages', $message->getId(), $message->setAttributes([
|
||||||
'status' => MessageStatus::FAILED,
|
'status' => MessageStatus::FAILED,
|
||||||
'deliveryErrors' => ['No fallback provider found.']
|
'deliveryErrors' => ['No enabled provider found.']
|
||||||
]));
|
]));
|
||||||
|
|
||||||
Console::warning('No fallback provider found.');
|
Console::warning('No enabled provider found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, array<string>> $identifiers
|
* @var array<string, array<string, null>> $identifiers
|
||||||
*/
|
*/
|
||||||
$identifiers = [];
|
$identifiers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Document[] $providers
|
* @var array<Document> $providers
|
||||||
*/
|
*/
|
||||||
$providers = [
|
$providers = [
|
||||||
$fallback->getId() => $fallback
|
$default->getId() => $default
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($recipients as $recipient) {
|
foreach ($allTargets as $target) {
|
||||||
$providerId = $recipient->getAttribute('providerId');
|
$providerId = $target->getAttribute('providerId');
|
||||||
|
|
||||||
if (
|
if (!$providerId) {
|
||||||
!$providerId
|
$providerId = $default->getId();
|
||||||
&& $fallback instanceof Document
|
|
||||||
&& !$fallback->isEmpty()
|
|
||||||
&& $fallback->getAttribute('enabled')
|
|
||||||
) {
|
|
||||||
$providerId = $fallback->getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($providerId) {
|
if ($providerId) {
|
||||||
if (!\array_key_exists($providerId, $identifiers)) {
|
if (!\array_key_exists($providerId, $identifiers)) {
|
||||||
$identifiers[$providerId] = [];
|
$identifiers[$providerId] = [];
|
||||||
}
|
}
|
||||||
$identifiers[$providerId][] = $recipient->getAttribute('identifier');
|
// Use null as value to avoid duplicate keys
|
||||||
|
$identifiers[$providerId][$target->getAttribute('identifier')] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<array> $results
|
* @var array<array> $results
|
||||||
*/
|
*/
|
||||||
$results = batch(\array_map(function ($providerId) use ($identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
$results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||||
return function () use ($providerId, $identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||||
if (\array_key_exists($providerId, $providers)) {
|
if (\array_key_exists($providerId, $providers)) {
|
||||||
$provider = $providers[$providerId];
|
$provider = $providers[$providerId];
|
||||||
} else {
|
} else {
|
||||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||||
|
|
||||||
if ($provider->isEmpty() || !$provider->getAttribute('enabled')) {
|
if ($provider->isEmpty() || !$provider->getAttribute('enabled')) {
|
||||||
$provider = $fallback;
|
$provider = $default;
|
||||||
} else {
|
} else {
|
||||||
$providers[$providerId] = $provider;
|
$providers[$providerId] = $provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$identifiers = $identifiers[$providerId];
|
$identifiersForProvider = $identifiers[$providerId];
|
||||||
|
|
||||||
$adapter = match ($provider->getAttribute('type')) {
|
$adapter = match ($provider->getAttribute('type')) {
|
||||||
MESSAGE_TYPE_SMS => $this->getSmsAdapter($provider),
|
MESSAGE_TYPE_SMS => $this->getSmsAdapter($provider),
|
||||||
MESSAGE_TYPE_PUSH => $this->getPushAdapter($provider),
|
MESSAGE_TYPE_PUSH => $this->getPushAdapter($provider),
|
||||||
MESSAGE_TYPE_EMAIL => $this->getEmailAdapter($provider),
|
MESSAGE_TYPE_EMAIL => $this->getEmailAdapter($provider),
|
||||||
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
default => throw new \Exception('Provider with the requested ID is of the incorrect type')
|
||||||
};
|
};
|
||||||
|
|
||||||
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
$batches = \array_chunk(
|
||||||
$batches = \array_chunk($identifiers, $maxBatchSize);
|
\array_keys($identifiersForProvider),
|
||||||
$batchIndex = 0;
|
$adapter->getMaxMessagesPerRequest()
|
||||||
|
);
|
||||||
|
|
||||||
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||||
return function () use ($batch, $message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $deviceForLocalFiles) {
|
||||||
$deliveredTotal = 0;
|
$deliveredTotal = 0;
|
||||||
$deliveryErrors = [];
|
$deliveryErrors = [];
|
||||||
$messageData = clone $message;
|
$messageData = clone $message;
|
||||||
|
@ -256,7 +258,7 @@ class Messaging extends Action
|
||||||
MESSAGE_TYPE_SMS => $this->buildSmsMessage($messageData, $provider),
|
MESSAGE_TYPE_SMS => $this->buildSmsMessage($messageData, $provider),
|
||||||
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
|
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
|
||||||
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $deviceForLocalFiles),
|
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $deviceForLocalFiles),
|
||||||
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
default => throw new \Exception('Provider with the requested ID is of the incorrect type')
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -283,10 +285,8 @@ class Messaging extends Action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . ' of ' . \count($batch) . ' with error: ' . $e->getMessage();
|
$deliveryErrors[] = 'Failed sending to targets with error: ' . $e->getMessage();
|
||||||
} finally {
|
} finally {
|
||||||
$batchIndex++;
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'deliveredTotal' => $deliveredTotal,
|
'deliveredTotal' => $deliveredTotal,
|
||||||
'deliveryErrors' => $deliveryErrors,
|
'deliveryErrors' => $deliveryErrors,
|
||||||
|
@ -297,7 +297,7 @@ class Messaging extends Action
|
||||||
};
|
};
|
||||||
}, \array_keys($identifiers)));
|
}, \array_keys($identifiers)));
|
||||||
|
|
||||||
$results = array_merge(...$results);
|
$results = \array_merge(...$results);
|
||||||
|
|
||||||
$deliveredTotal = 0;
|
$deliveredTotal = 0;
|
||||||
$deliveryErrors = [];
|
$deliveryErrors = [];
|
||||||
|
@ -330,7 +330,7 @@ class Messaging extends Action
|
||||||
|
|
||||||
$dbForProject->updateDocument('messages', $message->getId(), $message);
|
$dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||||
|
|
||||||
// Delete any attachments that were downloaded to the local cache
|
// Delete any attachments that were downloaded to local storage
|
||||||
if ($provider->getAttribute('type') === MESSAGE_TYPE_EMAIL) {
|
if ($provider->getAttribute('type') === MESSAGE_TYPE_EMAIL) {
|
||||||
if ($deviceForFiles->getType() === Storage::DEVICE_LOCAL) {
|
if ($deviceForFiles->getType() === Storage::DEVICE_LOCAL) {
|
||||||
return;
|
return;
|
||||||
|
@ -345,12 +345,12 @@ class Messaging extends Action
|
||||||
|
|
||||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||||
if ($bucket->isEmpty()) {
|
if ($bucket->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new \Exception('Storage bucket with the requested ID could not be found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
throw new \Exception('Storage file with the requested ID could not be found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = $file->getAttribute('path', '');
|
$path = $file->getAttribute('path', '');
|
||||||
|
@ -369,7 +369,7 @@ class Messaging extends Action
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($project->isEmpty()) {
|
if ($project->isEmpty()) {
|
||||||
throw new Exception('Project not set in payload');
|
throw new \Exception('Project not set in payload');
|
||||||
}
|
}
|
||||||
|
|
||||||
Console::log('Project: ' . $project->getId());
|
Console::log('Project: ' . $project->getId());
|
||||||
|
@ -427,12 +427,13 @@ class Messaging extends Action
|
||||||
|
|
||||||
$adapter = $this->getSmsAdapter($provider);
|
$adapter = $this->getSmsAdapter($provider);
|
||||||
|
|
||||||
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
$batches = \array_chunk(
|
||||||
$batches = \array_chunk($recipients, $maxBatchSize);
|
$recipients,
|
||||||
$batchIndex = 0;
|
$adapter->getMaxMessagesPerRequest()
|
||||||
|
);
|
||||||
|
|
||||||
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex, $project, $queueForUsage) {
|
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $project, $queueForUsage) {
|
||||||
return function () use ($batch, $message, $provider, $adapter, $batchIndex, $project, $queueForUsage) {
|
return function () use ($batch, $message, $provider, $adapter, $project, $queueForUsage) {
|
||||||
$message->setAttribute('to', $batch);
|
$message->setAttribute('to', $batch);
|
||||||
|
|
||||||
$data = $this->buildSmsMessage($message, $provider);
|
$data = $this->buildSmsMessage($message, $provider);
|
||||||
|
@ -445,7 +446,7 @@ class Messaging extends Action
|
||||||
->addMetric(METRIC_MESSAGES, 1)
|
->addMetric(METRIC_MESSAGES, 1)
|
||||||
->trigger();
|
->trigger();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
throw new Exception('Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage(), 500);
|
throw new \Exception('Failed sending to targets with error: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, $batches));
|
}, $batches));
|
||||||
|
@ -556,19 +557,19 @@ class Messaging extends Action
|
||||||
|
|
||||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||||
if ($bucket->isEmpty()) {
|
if ($bucket->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
throw new \Exception('Storage bucket with the requested ID could not be found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||||
if ($file->isEmpty()) {
|
if ($file->isEmpty()) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
throw new \Exception('Storage file with the requested ID could not be found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$mimes = Config::getParam('storage-mimes');
|
$mimes = Config::getParam('storage-mimes');
|
||||||
$path = $file->getAttribute('path', '');
|
$path = $file->getAttribute('path', '');
|
||||||
|
|
||||||
if (!$deviceForFiles->exists($path)) {
|
if (!$deviceForFiles->exists($path)) {
|
||||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
|
throw new \Exception('File not found in ' . $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
$contentType = 'text/plain';
|
$contentType = 'text/plain';
|
||||||
|
|
|
@ -22,6 +22,15 @@ class V17 extends Filter
|
||||||
case Response::MODEL_TOKEN:
|
case Response::MODEL_TOKEN:
|
||||||
$parsedResponse = $this->parseToken($parsedResponse);
|
$parsedResponse = $this->parseToken($parsedResponse);
|
||||||
break;
|
break;
|
||||||
|
case Response::MODEL_MEMBERSHIP:
|
||||||
|
$parsedResponse = $this->parseMembership($parsedResponse);
|
||||||
|
break;
|
||||||
|
case Response::MODEL_SESSION:
|
||||||
|
$parsedResponse = $this->parseSession($parsedResponse);
|
||||||
|
break;
|
||||||
|
case Response::MODEL_WEBHOOK:
|
||||||
|
$parsedResponse = $this->parseWebhook($parsedResponse);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $parsedResponse;
|
return $parsedResponse;
|
||||||
|
@ -30,6 +39,8 @@ class V17 extends Filter
|
||||||
protected function parseUser(array $content)
|
protected function parseUser(array $content)
|
||||||
{
|
{
|
||||||
unset($content['targets']);
|
unset($content['targets']);
|
||||||
|
unset($content['mfa']);
|
||||||
|
unset($content['totp']);
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,4 +56,25 @@ class V17 extends Filter
|
||||||
unset($content['phrase']);
|
unset($content['phrase']);
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function parseMembership(array $content)
|
||||||
|
{
|
||||||
|
unset($content['mfa']);
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseSession(array $content)
|
||||||
|
{
|
||||||
|
unset($content['factors']);
|
||||||
|
unset($content['secret']);
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseWebhook(array $content)
|
||||||
|
{
|
||||||
|
unset($content['enabled']);
|
||||||
|
unset($content['logs']);
|
||||||
|
unset($content['attempts']);
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,8 @@ class Message extends Model
|
||||||
->addRule('status', [
|
->addRule('status', [
|
||||||
'type' => self::TYPE_STRING,
|
'type' => self::TYPE_STRING,
|
||||||
'description' => 'Status of delivery.',
|
'description' => 'Status of delivery.',
|
||||||
'default' => 'processing',
|
'default' => 'draft',
|
||||||
'example' => 'Message status can be one of the following: processing, sent, cancelled, failed.',
|
'example' => 'Message status can be one of the following: draft, processing, scheduled, sent, or failed.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,9 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEmpty($response['body']['secret']);
|
$this->assertEmpty($response['body']['secret']);
|
||||||
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
||||||
|
|
||||||
// already logged in
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
@ -85,11 +87,8 @@ class AccountCustomClientTest extends Scope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for FAILURE
|
|
||||||
*/
|
|
||||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
@ -233,10 +232,7 @@ class AccountCustomClientTest extends Scope
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertIsArray($response['body']);
|
$this->assertEquals(2, $response['body']['total']);
|
||||||
$this->assertNotEmpty($response['body']);
|
|
||||||
$this->assertCount(2, $response['body']);
|
|
||||||
$this->assertEquals(3, $response['body']['total']);
|
|
||||||
$this->assertEquals($sessionId, $response['body']['sessions'][0]['$id']);
|
$this->assertEquals($sessionId, $response['body']['sessions'][0]['$id']);
|
||||||
|
|
||||||
$this->assertEquals('Windows', $response['body']['sessions'][0]['osName']);
|
$this->assertEquals('Windows', $response['body']['sessions'][0]['osName']);
|
||||||
|
@ -293,9 +289,9 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertIsArray($response['body']['logs']);
|
$this->assertIsArray($response['body']['logs']);
|
||||||
$this->assertNotEmpty($response['body']['logs']);
|
$this->assertNotEmpty($response['body']['logs']);
|
||||||
$this->assertCount(4, $response['body']['logs']);
|
$this->assertCount(3, $response['body']['logs']);
|
||||||
$this->assertIsNumeric($response['body']['total']);
|
$this->assertIsNumeric($response['body']['total']);
|
||||||
$this->assertEquals("session.create", $response['body']['logs'][2]['event']);
|
$this->assertEquals("user.create", $response['body']['logs'][2]['event']);
|
||||||
$this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']);
|
$this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']);
|
||||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
|
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
|
||||||
|
|
||||||
|
@ -317,10 +313,6 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals('--', $response['body']['logs'][1]['countryCode']);
|
$this->assertEquals('--', $response['body']['logs'][1]['countryCode']);
|
||||||
$this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']);
|
$this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']);
|
||||||
|
|
||||||
$this->assertEquals("user.create", $response['body']['logs'][3]['event']);
|
|
||||||
$this->assertEquals(filter_var($response['body']['logs'][3]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][3]['ip']);
|
|
||||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
|
|
||||||
|
|
||||||
$this->assertEquals('Windows', $response['body']['logs'][2]['osName']);
|
$this->assertEquals('Windows', $response['body']['logs'][2]['osName']);
|
||||||
$this->assertEquals('WIN', $response['body']['logs'][2]['osCode']);
|
$this->assertEquals('WIN', $response['body']['logs'][2]['osCode']);
|
||||||
$this->assertEquals('10', $response['body']['logs'][2]['osVersion']);
|
$this->assertEquals('10', $response['body']['logs'][2]['osVersion']);
|
||||||
|
@ -372,7 +364,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals($responseOffset['headers']['status-code'], 200);
|
$this->assertEquals($responseOffset['headers']['status-code'], 200);
|
||||||
$this->assertIsArray($responseOffset['body']['logs']);
|
$this->assertIsArray($responseOffset['body']['logs']);
|
||||||
$this->assertNotEmpty($responseOffset['body']['logs']);
|
$this->assertNotEmpty($responseOffset['body']['logs']);
|
||||||
$this->assertCount(3, $responseOffset['body']['logs']);
|
$this->assertCount(2, $responseOffset['body']['logs']);
|
||||||
$this->assertIsNumeric($responseOffset['body']['total']);
|
$this->assertIsNumeric($responseOffset['body']['total']);
|
||||||
|
|
||||||
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
|
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
|
||||||
|
@ -2239,7 +2231,6 @@ class AccountCustomClientTest extends Scope
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||||
|
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
@ -2249,6 +2240,9 @@ class AccountCustomClientTest extends Scope
|
||||||
|
|
||||||
$smsRequest = $this->getLastRequest();
|
$smsRequest = $this->getLastRequest();
|
||||||
|
|
||||||
|
$message = $smsRequest['data']['message'];
|
||||||
|
$token = substr($message, 0, 6);
|
||||||
|
|
||||||
return \array_merge($data, [
|
return \array_merge($data, [
|
||||||
'token' => \substr($smsRequest['data']['message'], 0, 6)
|
'token' => \substr($smsRequest['data']['message'], 0, 6)
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -62,7 +62,9 @@ class AccountCustomServerTest extends Scope
|
||||||
$this->assertNotEmpty($response['body']['secret']);
|
$this->assertNotEmpty($response['body']['secret']);
|
||||||
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
||||||
|
|
||||||
// already logged in
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
@ -72,11 +74,8 @@ class AccountCustomServerTest extends Scope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(401, $response['headers']['status-code']);
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for FAILURE
|
|
||||||
*/
|
|
||||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
|
|
@ -4532,6 +4532,8 @@ trait DatabasesBase
|
||||||
$this->assertEquals(2, count($response['body']['documents']));
|
$this->assertEquals(2, count($response['body']['documents']));
|
||||||
$this->assertEquals(null, $response['body']['documents'][0]['fullName']);
|
$this->assertEquals(null, $response['body']['documents'][0]['fullName']);
|
||||||
$this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]);
|
$this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]);
|
||||||
|
$this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]);
|
||||||
|
$this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4551,6 +4553,8 @@ trait DatabasesBase
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]);
|
$this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]);
|
||||||
|
$this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]);
|
||||||
|
$this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]);
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
@ -4563,6 +4567,8 @@ trait DatabasesBase
|
||||||
$document = $response['body']['documents'][0];
|
$document = $response['body']['documents'][0];
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertArrayHasKey('libraries', $document);
|
$this->assertArrayHasKey('libraries', $document);
|
||||||
|
$this->assertArrayNotHasKey('$databaseId', $document);
|
||||||
|
$this->assertArrayNotHasKey('$collectionId', $document);
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
|
|
@ -281,19 +281,22 @@ class BatchTest extends Scope
|
||||||
public function testQueryBatchedMutations()
|
public function testQueryBatchedMutations()
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
$email = 'tester' . \uniqid() . '@example.com';
|
$email1 = 'tester' . \uniqid() . '@example.com';
|
||||||
|
$email2 = 'tester' . \uniqid() . '@example.com';
|
||||||
$graphQLPayload = [
|
$graphQLPayload = [
|
||||||
'query' => 'mutation CreateAndLogin($userId: String!, $email: String!, $password: String!, $name: String) {
|
'query' => 'mutation CreateAndLogin($user1Id: String!, $user2Id: String!, $email1: String!, $email2: String!, $password: String!, $name: String) {
|
||||||
accountCreate(userId: $userId, email: $email, password: $password, name: $name) {
|
account1: accountCreate(userId: $user1Id, email: $email1, password: $password, name: $name) {
|
||||||
name
|
email
|
||||||
}
|
}
|
||||||
accountCreateEmailPasswordSession(email: $email, password: $password) {
|
account2: accountCreate(userId: $user2Id, email: $email2, password: $password, name: $name) {
|
||||||
expire
|
email
|
||||||
}
|
}
|
||||||
}',
|
}',
|
||||||
'variables' => [
|
'variables' => [
|
||||||
'userId' => ID::unique(),
|
'user1Id' => ID::unique(),
|
||||||
'email' => $email,
|
'user2Id' => ID::unique(),
|
||||||
|
'email1' => $email1,
|
||||||
|
'email2' => $email2,
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
'name' => 'Tester',
|
'name' => 'Tester',
|
||||||
],
|
],
|
||||||
|
@ -304,12 +307,12 @@ class BatchTest extends Scope
|
||||||
'x-appwrite-project' => $projectId,
|
'x-appwrite-project' => $projectId,
|
||||||
], $this->getHeaders()), $graphQLPayload);
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
|
||||||
$this->assertIsArray($response['body']['data']);
|
$this->assertIsArray($response['body']['data']);
|
||||||
$this->assertArrayNotHasKey('errors', $response['body']);
|
$this->assertArrayNotHasKey('errors', $response['body']);
|
||||||
$this->assertArrayHasKey('accountCreate', $response['body']['data']);
|
$this->assertArrayHasKey('account1', $response['body']['data']);
|
||||||
$this->assertArrayHasKey('accountCreateEmailPasswordSession', $response['body']['data']);
|
$this->assertArrayHasKey('account2', $response['body']['data']);
|
||||||
$this->assertEquals('Tester', $response['body']['data']['accountCreate']['name']);
|
$this->assertEquals($email1, $response['body']['data']['account1']['email']);
|
||||||
|
$this->assertEquals($email2, $response['body']['data']['account2']['email']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testQueryBatchedMutationsOfSameType()
|
public function testQueryBatchedMutationsOfSameType()
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace Tests\E2E\Services\Messaging;
|
||||||
use Appwrite\Messaging\Status as MessageStatus;
|
use Appwrite\Messaging\Status as MessageStatus;
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
|
@ -799,6 +800,7 @@ trait MessagingBase
|
||||||
'messageId' => ID::unique(),
|
'messageId' => ID::unique(),
|
||||||
'subject' => 'New blog post',
|
'subject' => 'New blog post',
|
||||||
'content' => 'Check out the new blog post at http://localhost',
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'draft' => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
@ -867,6 +869,7 @@ trait MessagingBase
|
||||||
'targets' => [$targetId1, $targetId2],
|
'targets' => [$targetId1, $targetId2],
|
||||||
'subject' => 'New blog post',
|
'subject' => 'New blog post',
|
||||||
'content' => 'Check out the new blog post at http://localhost',
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'draft' => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
@ -876,6 +879,228 @@ trait MessagingBase
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testScheduledMessage(): void
|
||||||
|
{
|
||||||
|
// Create user
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'userId' => ID::unique(),
|
||||||
|
'email' => uniqid() . "@example.com",
|
||||||
|
'password' => 'password',
|
||||||
|
'name' => 'Messaging User 1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$targetId = $response['body']['targets'][0]['$id'];
|
||||||
|
|
||||||
|
// Create scheduled message
|
||||||
|
$message = $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(),
|
||||||
|
'targets' => [$targetId],
|
||||||
|
'subject' => 'New blog post',
|
||||||
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'scheduledAt' => DateTime::addSeconds(new \DateTime(), 3),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']);
|
||||||
|
|
||||||
|
\sleep(8);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::FAILED, $message['body']['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testScheduledToDraftMessage(): void
|
||||||
|
{
|
||||||
|
// Create user
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'userId' => ID::unique(),
|
||||||
|
'email' => uniqid() . "@example.com",
|
||||||
|
'password' => 'password',
|
||||||
|
'name' => 'Messaging User 1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$targetId = $response['body']['targets'][0]['$id'];
|
||||||
|
|
||||||
|
// Create scheduled message
|
||||||
|
$message = $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(),
|
||||||
|
'targets' => [$targetId],
|
||||||
|
'subject' => 'New blog post',
|
||||||
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'scheduledAt' => DateTime::addSeconds(new \DateTime(), 5),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'draft' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::DRAFT, $message['body']['status']);
|
||||||
|
|
||||||
|
\sleep(8);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::DRAFT, $message['body']['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDraftToScheduledMessage(): void
|
||||||
|
{
|
||||||
|
// Create user
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'userId' => ID::unique(),
|
||||||
|
'email' => uniqid() . "@example.com",
|
||||||
|
'password' => 'password',
|
||||||
|
'name' => 'Messaging User 1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$targetId = $response['body']['targets'][0]['$id'];
|
||||||
|
|
||||||
|
// Create draft message
|
||||||
|
$message = $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(),
|
||||||
|
'targets' => [$targetId],
|
||||||
|
'subject' => 'New blog post',
|
||||||
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'draft' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::DRAFT, $message['body']['status']);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'draft' => false,
|
||||||
|
'scheduledAt' => DateTime::addSeconds(new \DateTime(), 3),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']);
|
||||||
|
|
||||||
|
\sleep(8);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::FAILED, $message['body']['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateScheduledAt(): void
|
||||||
|
{
|
||||||
|
// Create user
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'userId' => ID::unique(),
|
||||||
|
'email' => uniqid() . "@example.com",
|
||||||
|
'password' => 'password',
|
||||||
|
'name' => 'Messaging User 1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$targetId = $response['body']['targets'][0]['$id'];
|
||||||
|
|
||||||
|
// Create scheduled message
|
||||||
|
$message = $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(),
|
||||||
|
'targets' => [$targetId],
|
||||||
|
'subject' => 'New blog post',
|
||||||
|
'content' => 'Check out the new blog post at http://localhost',
|
||||||
|
'scheduledAt' => DateTime::addSeconds(new \DateTime(), 3),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']);
|
||||||
|
|
||||||
|
$scheduledAt = DateTime::addSeconds(new \DateTime(), 10);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
], [
|
||||||
|
'scheduledAt' => $scheduledAt,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
|
||||||
|
\sleep(8);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']);
|
||||||
|
|
||||||
|
\sleep(8);
|
||||||
|
|
||||||
|
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $message['headers']['status-code']);
|
||||||
|
$this->assertEquals(MessageStatus::FAILED, $message['body']['status']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testSendEmail()
|
public function testSendEmail()
|
||||||
{
|
{
|
||||||
if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) {
|
if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) {
|
||||||
|
@ -1004,7 +1229,7 @@ trait MessagingBase
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'messageId' => ID::unique(),
|
'messageId' => ID::unique(),
|
||||||
'status' => 'draft',
|
'draft' => true,
|
||||||
'topics' => [$email['body']['topics'][0]],
|
'topics' => [$email['body']['topics'][0]],
|
||||||
'subject' => 'Khali beats Undertaker',
|
'subject' => 'Khali beats Undertaker',
|
||||||
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||||
|
@ -1017,7 +1242,7 @@ trait MessagingBase
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'status' => 'processing',
|
'draft' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(200, $email['headers']['status-code']);
|
$this->assertEquals(200, $email['headers']['status-code']);
|
||||||
|
@ -1169,7 +1394,7 @@ trait MessagingBase
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'messageId' => ID::unique(),
|
'messageId' => ID::unique(),
|
||||||
'status' => 'draft',
|
'draft' => true,
|
||||||
'topics' => [$sms['body']['topics'][0]],
|
'topics' => [$sms['body']['topics'][0]],
|
||||||
'content' => 'Your OTP code is 123456',
|
'content' => 'Your OTP code is 123456',
|
||||||
]);
|
]);
|
||||||
|
@ -1181,7 +1406,7 @@ trait MessagingBase
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'status' => 'processing',
|
'draft' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(200, $sms['headers']['status-code']);
|
$this->assertEquals(200, $sms['headers']['status-code']);
|
||||||
|
@ -1330,7 +1555,7 @@ trait MessagingBase
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'messageId' => ID::unique(),
|
'messageId' => ID::unique(),
|
||||||
'status' => 'draft',
|
'draft' => true,
|
||||||
'topics' => [$push['body']['topics'][0]],
|
'topics' => [$push['body']['topics'][0]],
|
||||||
'title' => 'Test-Notification',
|
'title' => 'Test-Notification',
|
||||||
'body' => 'Test-Notification-Body',
|
'body' => 'Test-Notification-Body',
|
||||||
|
@ -1343,7 +1568,7 @@ trait MessagingBase
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'status' => 'processing',
|
'draft' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(200, $push['headers']['status-code']);
|
$this->assertEquals(200, $push['headers']['status-code']);
|
||||||
|
@ -1387,7 +1612,6 @@ trait MessagingBase
|
||||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
], [
|
], [
|
||||||
'messageId' => ID::unique(),
|
'messageId' => ID::unique(),
|
||||||
'status' => 'processing',
|
|
||||||
'topics' => [$topic['$id']],
|
'topics' => [$topic['$id']],
|
||||||
'subject' => 'Test subject',
|
'subject' => 'Test subject',
|
||||||
'content' => 'Test content',
|
'content' => 'Test content',
|
||||||
|
|
|
@ -1731,6 +1731,19 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
|
|
||||||
$this->assertEquals(201, $response['headers']['status-code']);
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $id,
|
||||||
|
'x-appwrite-mode' => 'admin',
|
||||||
|
]), [
|
||||||
|
// Empty password
|
||||||
|
'email' => uniqid() . 'user@localhost.test',
|
||||||
|
'name' => 'User',
|
||||||
|
'userId' => ID::unique(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response['headers']['status-code']);
|
||||||
|
|
||||||
$email = uniqid() . 'user@localhost.test';
|
$email = uniqid() . 'user@localhost.test';
|
||||||
$userId = ID::unique();
|
$userId = ID::unique();
|
||||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge($this->getHeaders(), [
|
||||||
|
|
|
@ -73,6 +73,8 @@ class V17Test extends TestCase
|
||||||
'remove targets' => [
|
'remove targets' => [
|
||||||
[
|
[
|
||||||
'targets' => 'test',
|
'targets' => 'test',
|
||||||
|
'mfa' => 'test',
|
||||||
|
'totp' => 'test',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
],
|
],
|
||||||
|
@ -116,4 +118,70 @@ class V17Test extends TestCase
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function membershipProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'remove mfa' => [
|
||||||
|
[
|
||||||
|
'mfa' => 'test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider membershipProvider
|
||||||
|
*/
|
||||||
|
public function testMembership(array $content, array $expected): void
|
||||||
|
{
|
||||||
|
$model = Response::MODEL_MEMBERSHIP;
|
||||||
|
|
||||||
|
$result = $this->filter->parse($content, $model);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sessionProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'remove factors and secrets' => [
|
||||||
|
[
|
||||||
|
'factors' => 'test',
|
||||||
|
'secret' => 'test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider sessionProvider
|
||||||
|
*/
|
||||||
|
public function testSession(array $content, array $expected): void
|
||||||
|
{
|
||||||
|
$model = Response::MODEL_SESSION;
|
||||||
|
|
||||||
|
$result = $this->filter->parse($content, $model);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function webhookProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'remove webhook additions' => [
|
||||||
|
[
|
||||||
|
'enabled' => true,
|
||||||
|
'logs' => ['test', 'test'],
|
||||||
|
'attempts' => 1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue