Merge branch '1.5.x' into fix-limit-failed-webhook-attempts
This commit is contained in:
commit
2bcb6c18fb
46 changed files with 1449 additions and 1324 deletions
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
|
@ -99,6 +99,7 @@ jobs:
|
|||
Users,
|
||||
Webhooks,
|
||||
VCS,
|
||||
Messaging,
|
||||
]
|
||||
|
||||
steps:
|
||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,4 +1,4 @@
|
|||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = 3.2.6
|
||||
branch = 3.2.7
|
||||
|
|
14
CHANGES.md
14
CHANGES.md
|
@ -1,3 +1,17 @@
|
|||
# Version 1.4.12
|
||||
|
||||
## Miscellaneous
|
||||
* Bump console to version 3.2.7 [#7148](https://github.com/appwrite/appwrite/pull/7148)
|
||||
* Chore update database to 0.45.2 [#7138](https://github.com/appwrite/appwrite/pull/7138)
|
||||
* Implement queue thresholds for the health API [#7123](https://github.com/appwrite/appwrite/pull/7123)
|
||||
* Add Authorization::skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124)
|
||||
|
||||
## Bug fixes
|
||||
* fix: use queueForDeletes in git installation delete endpoint [#7140](https://github.com/appwrite/appwrite/pull/7140)
|
||||
* fix: patch script, make errors silent [#7134](https://github.com/appwrite/appwrite/pull/7134)
|
||||
* fix: repositories recreation script [#7133](https://github.com/appwrite/appwrite/pull/7133)
|
||||
* fix: Only delete repositories linked to the particular project [#7131](https://github.com/appwrite/appwrite/pull/7131)
|
||||
|
||||
# Version 1.4.11
|
||||
|
||||
## Miscellaneous
|
||||
|
|
|
@ -100,6 +100,7 @@ RUN chmod +x /usr/local/bin/doctor && \
|
|||
RUN chmod +x /usr/local/bin/hamster && \
|
||||
chmod +x /usr/local/bin/volume-sync && \
|
||||
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
|
||||
chmod +x /usr/local/bin/patch-recreate-repositories-documents && \
|
||||
chmod +x /usr/local/bin/patch-delete-project-collections && \
|
||||
chmod +x /usr/local/bin/delete-orphaned-projects && \
|
||||
chmod +x /usr/local/bin/clear-card-cache && \
|
||||
|
|
|
@ -55,7 +55,7 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读
|
|||
|
||||
## 安装
|
||||
|
||||
Appwrite 的容器化服务器只需要一行指令就可以运行。您可以使用 docker-compose 在本地主机上运行 Appwrite,也可以在任何其他容器化工具(如 Kubernetes、Docker Swarm 或 Rancher)上运行 Appwrite。
|
||||
Appwrite 的容器化服务器只需要一行指令就可以运行。您可以使用 docker-compose 在本地主机上运行 Appwrite,也可以在任何其他容器化工具(如 [Kubernetes](https://kubernetes.io/docs/home/)、[Docker Swarm](https://docs.docker.com/engine/swarm/) 或 [Rancher](https://rancher.com/docs/))上运行 Appwrite。
|
||||
|
||||
启动 Appwrite 服务器的最简单方法是运行我们的 docker-compose 文件。在运行安装命令之前,请确保您的机器上安装了 [Docker](https://dockerdocs.cn/get-docker/index.html):
|
||||
|
||||
|
@ -66,7 +66,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
@ -78,7 +78,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
@ -88,7 +88,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
|
|
@ -76,7 +76,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
@ -88,7 +88,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
@ -98,7 +98,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.4.11
|
||||
appwrite/appwrite:1.4.12
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
|
|
|
@ -1416,17 +1416,6 @@ $commonCollections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('internal'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('enabled'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
|
@ -1467,9 +1456,9 @@ $commonCollections = [
|
|||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['providerSearch'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
@ -1495,16 +1484,9 @@ $commonCollections = [
|
|||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_internal'),
|
||||
'$id' => ID::custom('_key_enabled_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['internal'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_internal_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['internal','type'],
|
||||
'attributes' => ['enabled','type'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
|
@ -1640,9 +1622,9 @@ $commonCollections = [
|
|||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['messageSearch'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
@ -1712,9 +1694,9 @@ $commonCollections = [
|
|||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['topicSearch'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
@ -1740,6 +1722,28 @@ $commonCollections = [
|
|||
'$id' => ID::custom('subscribers'),
|
||||
'name' => 'Subscribers',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('userId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('userInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('targetId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
|
@ -1786,6 +1790,20 @@ $commonCollections = [
|
|||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_userId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['userId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_userInternalId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['userInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_targetId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
|
@ -1845,7 +1863,7 @@ $commonCollections = [
|
|||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerId'),
|
||||
'$id' => ID::custom('providerType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
|
@ -1855,13 +1873,24 @@ $commonCollections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
|
@ -1877,6 +1906,17 @@ $commonCollections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('name'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
@ -1909,18 +1949,11 @@ $commonCollections = [
|
|||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_identifier'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['identifier'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_identifier_providerId'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['providerId', 'identifier'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -240,6 +240,16 @@ return [
|
|||
'description' => 'OAuth2 provider returned some error.',
|
||||
'code' => 424,
|
||||
],
|
||||
Exception::USER_EMAIL_ALREADY_VERIFIED => [
|
||||
'name' => Exception::USER_EMAIL_ALREADY_VERIFIED,
|
||||
'description' => 'User email is already verified',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_PHONE_ALREADY_VERIFIED => [
|
||||
'name' => Exception::USER_PHONE_ALREADY_VERIFIED,
|
||||
'description' => 'User phone is already verified',
|
||||
'code' => 409
|
||||
],
|
||||
Exception::USER_TARGET_NOT_FOUND => [
|
||||
'name' => Exception::USER_TARGET_NOT_FOUND,
|
||||
'description' => 'The target could not be found.',
|
||||
|
|
|
@ -440,7 +440,7 @@ return [
|
|||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_SMS_PROVIDER',
|
||||
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, text-magic, telesign, msg91, and vonage.",
|
||||
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, textmagic, telesign, msg91, and vonage.",
|
||||
'introduction' => '0.15.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f7c34a1b37d53dd5f28c83b4e12a4e68fcd9b484
|
||||
Subproject commit 49d039ed07628155e7f56e2c997fcef90ecde267
|
|
@ -148,7 +148,20 @@ App::post('/v1/account')
|
|||
'accessedAt' => DateTime::now(),
|
||||
]);
|
||||
$user->removeAttribute('$internalId');
|
||||
Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
||||
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
||||
$target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'providerType' => 'email',
|
||||
'identifier' => $email,
|
||||
])));
|
||||
$user->setAttribute('targets', [$target]);
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
@ -656,7 +669,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
'accessedAt' => DateTime::now(),
|
||||
]);
|
||||
$user->removeAttribute('$internalId');
|
||||
Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
||||
$userDoc = Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
||||
$dbForProject->createDocument('targets', new Document([
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $userDoc->getId(),
|
||||
'userInternalId' => $userDoc->getInternalId(),
|
||||
'providerType' => 'email',
|
||||
'identifier' => $email,
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
@ -1236,11 +1260,7 @@ App::post('/v1/account/sessions/phone')
|
|||
->inject('queueForMessaging')
|
||||
->inject('locale')
|
||||
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) {
|
||||
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]));
|
||||
if ($provider === false || $provider->isEmpty()) {
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
|
@ -1326,31 +1346,18 @@ App::post('/v1/account/sessions/phone')
|
|||
$message = $message->setParam('{{token}}', $secret);
|
||||
$message = $message->render();
|
||||
|
||||
$target = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$phone]),
|
||||
Query::equal('providerInternalId', [$provider->getInternalId()])
|
||||
]);
|
||||
|
||||
if (!$target) {
|
||||
$target = $dbForProject->createDocument('targets', new Document([
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'providerId' => $provider->getId(),
|
||||
'providerInternalId' => $provider->getInternalId(),
|
||||
'identifier' => $phone,
|
||||
]));
|
||||
}
|
||||
|
||||
$messageDoc = $dbForProject->createDocument('messages', new Document([
|
||||
$messageDoc = new Document([
|
||||
'$id' => $token->getId(),
|
||||
'targets' => [$target->getId()],
|
||||
'data' => [
|
||||
'content' => $message,
|
||||
],
|
||||
]));
|
||||
]);
|
||||
|
||||
$queueForMessaging
|
||||
->setMessageId($messageDoc->getId())
|
||||
->setMessage($messageDoc)
|
||||
->setRecipients([$phone])
|
||||
->setProviderType('SMS')
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
|
@ -1670,6 +1677,81 @@ App::post('/v1/account/jwt')
|
|||
])]), Response::MODEL_JWT);
|
||||
});
|
||||
|
||||
App::post('/v1/account/targets/push')
|
||||
->desc('Create Account\'s push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('audits.event', 'target.create')
|
||||
->label('audits.resource', 'target/response.$id')
|
||||
->label('event', 'users.[userId].targets.[targetId].create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createPushTarget')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||
->label('docs', false)
|
||||
->param('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.')
|
||||
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||
->inject('queueForEvents')
|
||||
->inject('user')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $targetId, string $providerId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
|
||||
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
|
||||
|
||||
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
throw new Exception(Exception::PROVIDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||
|
||||
if (!$target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$detector = new Detector($request->getUserAgent());
|
||||
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$device = $detector->getDevice();
|
||||
|
||||
try {
|
||||
$target = $dbForProject->createDocument('targets', new Document([
|
||||
'$id' => $targetId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
],
|
||||
'providerId' => $providerId ?? null,
|
||||
'providerInternalId' => $provider->getInternalId() ?? null,
|
||||
'providerType' => 'push',
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'identifier' => $identifier,
|
||||
'name' => "{$device['deviceBrand']} {$device['deviceModel']}"
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('targetId', $target->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::get('/v1/account')
|
||||
->desc('Get account')
|
||||
->groups(['api', 'account'])
|
||||
|
@ -1995,6 +2077,7 @@ App::patch('/v1/account/email')
|
|||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$oldEmail = $user->getAttribute('email');
|
||||
$email = \strtolower($email);
|
||||
|
||||
// Makes sure this email is not already used in another identity
|
||||
|
@ -2019,6 +2102,23 @@ App::patch('/v1/account/email')
|
|||
->setAttribute('passwordUpdate', DateTime::now());
|
||||
}
|
||||
|
||||
$target = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$email]),
|
||||
]);
|
||||
|
||||
if ($target && !$target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
|
||||
|
||||
if ($oldTarget !== false && !$oldTarget->isEmpty()) {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email));
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
} catch (Duplicate) {
|
||||
|
@ -2065,6 +2165,19 @@ App::patch('/v1/account/phone')
|
|||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||
}
|
||||
|
||||
$target = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$phone]),
|
||||
]);
|
||||
|
||||
if ($target && !$target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
$oldTarget = $user->find('identifier', $user->getAttribute('phone'), 'targets');
|
||||
|
||||
$user
|
||||
->setAttribute('phone', $phone)
|
||||
->setAttribute('phoneVerification', false) // After this user needs to confirm phone number again
|
||||
|
@ -2078,6 +2191,10 @@ App::patch('/v1/account/phone')
|
|||
->setAttribute('passwordUpdate', DateTime::now());
|
||||
}
|
||||
|
||||
if ($oldTarget !== false && !$oldTarget->isEmpty()) {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone));
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
} catch (Duplicate $th) {
|
||||
|
@ -2688,6 +2805,10 @@ App::post('/v1/account/verification')
|
|||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
|
||||
if ($user->getAttribute('emailVerification')) {
|
||||
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
@ -2900,11 +3021,7 @@ App::post('/v1/account/verification/phone')
|
|||
->inject('project')
|
||||
->inject('locale')
|
||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) {
|
||||
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]));
|
||||
if ($provider === false || $provider->isEmpty()) {
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
|
@ -2912,6 +3029,10 @@ App::post('/v1/account/verification/phone')
|
|||
throw new Exception(Exception::USER_PHONE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($user->getAttribute('phoneVerification')) {
|
||||
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
@ -2950,31 +3071,17 @@ App::post('/v1/account/verification/phone')
|
|||
$message = $message->setParam('{{token}}', $secret);
|
||||
$message = $message->render();
|
||||
|
||||
$target = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$user->getAttribute('phone')]),
|
||||
Query::equal('providerInternalId', [$provider->getInternalId()])
|
||||
]);
|
||||
|
||||
if (!$target) {
|
||||
$target = $dbForProject->createDocument('targets', new Document([
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'providerId' => $provider->getId(),
|
||||
'providerInternalId' => $provider->getInternalId(),
|
||||
'identifier' => $user->getAttribute('phone'),
|
||||
]));
|
||||
}
|
||||
|
||||
$messageDoc = $dbForProject->createDocument('messages', new Document([
|
||||
$messageDoc = new Document([
|
||||
'$id' => $verification->getId(),
|
||||
'targets' => [$target->getId()],
|
||||
'data' => [
|
||||
'content' => $message,
|
||||
],
|
||||
]));
|
||||
]);
|
||||
|
||||
$queueForMessaging
|
||||
->setMessageId($messageDoc->getId())
|
||||
->setMessage($messageDoc)
|
||||
->setRecipients([$user->getAttribute('phone')])
|
||||
->setProviderType('SMS')
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
|
@ -3053,3 +3160,61 @@ App::put('/v1/account/verification/phone')
|
|||
|
||||
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::put('/v1/account/targets/:targetId/push')
|
||||
->desc('Update Account\'s push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('audits.event', 'target.update')
|
||||
->label('audits.resource', 'target/response.$id')
|
||||
->label('event', 'users.[userId].targets.[targetId].update')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePushTarget')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||
->label('docs', false)
|
||||
->param('targetId', '', new UID(), 'Target ID.')
|
||||
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||
->inject('queueForEvents')
|
||||
->inject('user')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
|
||||
|
||||
if ($target->isEmpty()) {
|
||||
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($user->getId() !== $target->getAttribute('userId')) {
|
||||
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($identifier) {
|
||||
$target->setAttribute('identifier', $identifier);
|
||||
}
|
||||
|
||||
$detector = new Detector($request->getUserAgent());
|
||||
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$device = $detector->getDevice();
|
||||
|
||||
$target->setAttribute('name', "{$device['deviceBrand']} {$device['deviceModel']}");
|
||||
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('targetId', $target->getId());
|
||||
|
||||
$response
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
|
|
@ -1571,7 +1571,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
|||
->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', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new DatetimeValidator(), 'Default value for the attribute in ISO 8601 format. Cannot be set when attribute is required.', true)
|
||||
->param('default', null, new DatetimeValidator(), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true)
|
||||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
|
@ -242,12 +242,16 @@ App::post('/v1/functions')
|
|||
|
||||
// Git connect logic
|
||||
if (!empty($providerRepositoryId)) {
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
|
||||
$repository = $dbForConsole->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'installationId' => $installation->getId(),
|
||||
'installationInternalId' => $installation->getInternalId(),
|
||||
|
|
|
@ -14,6 +14,7 @@ use Utopia\Registry\Registry;
|
|||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
App::get('/v1/health')
|
||||
|
@ -344,11 +345,20 @@ App::get('/v1/health/queue/webhooks')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::WEBHOOK_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/logs')
|
||||
|
@ -362,11 +372,20 @@ App::get('/v1/health/queue/logs')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::AUDITS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/certificates')
|
||||
|
@ -380,11 +399,20 @@ App::get('/v1/health/queue/certificates')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/builds')
|
||||
|
@ -398,11 +426,20 @@ App::get('/v1/health/queue/builds')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::BUILDS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/databases')
|
||||
|
@ -417,11 +454,20 @@ App::get('/v1/health/queue/databases')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('name', 'database_db_main', new Text(256), 'Queue name for which to check the queue size', true)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (string $name, Connection $queue, Response $response) {
|
||||
->action(function (string $name, int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client($name, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/deletes')
|
||||
|
@ -435,11 +481,20 @@ App::get('/v1/health/queue/deletes')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::DELETE_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/mails')
|
||||
|
@ -453,11 +508,20 @@ App::get('/v1/health/queue/mails')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::MAILS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/messaging')
|
||||
|
@ -471,11 +535,20 @@ App::get('/v1/health/queue/messaging')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::MESSAGING_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/migrations')
|
||||
|
@ -489,11 +562,20 @@ App::get('/v1/health/queue/migrations')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::MIGRATIONS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/queue/functions')
|
||||
|
@ -507,11 +589,20 @@ App::get('/v1/health/queue/functions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
->action(function (int|string $threshold, Connection $queue, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
$size = $client->getQueueSize();
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/storage/local')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
|
@ -35,6 +36,8 @@ use Utopia\Validator\Text;
|
|||
use MaxMind\Db\Reader;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
use function Swoole\Coroutine\batch;
|
||||
|
||||
App::post('/v1/messaging/providers/mailgun')
|
||||
->desc('Create Mailgun provider')
|
||||
->groups(['api', 'messaging'])
|
||||
|
@ -68,7 +71,6 @@ App::post('/v1/messaging/providers/mailgun')
|
|||
'provider' => 'mailgun',
|
||||
'type' => 'email',
|
||||
'enabled' => $enabled,
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'mailgun' . ' ' . 'email',
|
||||
'credentials' => [
|
||||
'apiKey' => $apiKey,
|
||||
'domain' => $domain,
|
||||
|
@ -79,16 +81,6 @@ App::post('/v1/messaging/providers/mailgun')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['email'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -119,7 +111,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender email address.')
|
||||
->param('from', '', new Email(), 'Sender email address.')
|
||||
->param('apiKey', '', new Text(0), 'Sendgrid API key.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
->inject('queueForEvents')
|
||||
|
@ -133,7 +125,6 @@ App::post('/v1/messaging/providers/sendgrid')
|
|||
'provider' => 'sendgrid',
|
||||
'type' => 'email',
|
||||
'enabled' => $enabled,
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'sendgrid' . ' ' . 'email',
|
||||
'credentials' => [
|
||||
'apiKey' => $apiKey,
|
||||
],
|
||||
|
@ -142,16 +133,6 @@ App::post('/v1/messaging/providers/sendgrid')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -182,7 +163,7 @@ App::post('/v1/messaging/providers/msg91')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender number.')
|
||||
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('senderId', '', new Text(0), 'Msg91 Sender ID.')
|
||||
->param('authKey', '', new Text(0), 'Msg91 Auth Key.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
|
@ -196,7 +177,6 @@ App::post('/v1/messaging/providers/msg91')
|
|||
'name' => $name,
|
||||
'provider' => 'msg91',
|
||||
'type' => 'sms',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'msg91' . ' ' . 'sms',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'senderId' => $senderId,
|
||||
|
@ -207,16 +187,6 @@ App::post('/v1/messaging/providers/msg91')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -247,7 +217,7 @@ App::post('/v1/messaging/providers/telesign')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender number.')
|
||||
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('username', '', new Text(0), 'Telesign username.')
|
||||
->param('password', '', new Text(0), 'Telesign password.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
|
@ -261,7 +231,6 @@ App::post('/v1/messaging/providers/telesign')
|
|||
'name' => $name,
|
||||
'provider' => 'telesign',
|
||||
'type' => 'sms',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'telesign' . ' ' . 'sms',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'username' => $username,
|
||||
|
@ -272,16 +241,6 @@ App::post('/v1/messaging/providers/telesign')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -312,7 +271,7 @@ App::post('/v1/messaging/providers/textmagic')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender number.')
|
||||
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('username', '', new Text(0), 'Textmagic username.')
|
||||
->param('apiKey', '', new Text(0), 'Textmagic apiKey.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
|
@ -324,9 +283,8 @@ App::post('/v1/messaging/providers/textmagic')
|
|||
$provider = new Document([
|
||||
'$id' => $providerId,
|
||||
'name' => $name,
|
||||
'provider' => 'text-magic',
|
||||
'provider' => 'textmagic',
|
||||
'type' => 'sms',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'text-magic' . ' ' . 'sms',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'username' => $username,
|
||||
|
@ -337,16 +295,6 @@ App::post('/v1/messaging/providers/textmagic')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -377,7 +325,7 @@ App::post('/v1/messaging/providers/twilio')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender number.')
|
||||
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('accountSid', '', new Text(0), 'Twilio account secret ID.')
|
||||
->param('authToken', '', new Text(0), 'Twilio authentication token.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
|
@ -391,7 +339,6 @@ App::post('/v1/messaging/providers/twilio')
|
|||
'name' => $name,
|
||||
'provider' => 'twilio',
|
||||
'type' => 'sms',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'twilio' . ' ' . 'sms',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'accountSid' => $accountSid,
|
||||
|
@ -402,16 +349,6 @@ App::post('/v1/messaging/providers/twilio')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -442,7 +379,7 @@ App::post('/v1/messaging/providers/vonage')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('from', '', new Text(256), 'Sender number.')
|
||||
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
|
||||
->param('apiKey', '', new Text(0), 'Vonage API key.')
|
||||
->param('apiSecret', '', new Text(0), 'Vonage API secret.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
|
@ -456,7 +393,6 @@ App::post('/v1/messaging/providers/vonage')
|
|||
'name' => $name,
|
||||
'provider' => 'vonage',
|
||||
'type' => 'sms',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'vonage' . ' ' . 'sms',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'apiKey' => $apiKey,
|
||||
|
@ -467,16 +403,6 @@ App::post('/v1/messaging/providers/vonage')
|
|||
]
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -507,35 +433,24 @@ App::post('/v1/messaging/providers/fcm')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
->param('serverKey', '', new Text(0), 'FCM server key.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, bool $enabled, string $serverKey, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, string $serverKey, bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
|
||||
$provider = new Document([
|
||||
'$id' => $providerId,
|
||||
'name' => $name,
|
||||
'provider' => 'fcm',
|
||||
'type' => 'push',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'fcm' . ' ' . 'push',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'serverKey' => $serverKey,
|
||||
],
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['push'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -566,23 +481,22 @@ App::post('/v1/messaging/providers/apns')
|
|||
->label('sdk.response.model', Response::MODEL_PROVIDER)
|
||||
->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Provider name.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
->param('authKey', '', new Text(0), 'APNS authentication key.')
|
||||
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.')
|
||||
->param('teamId', '', new Text(0), 'APNS team ID.')
|
||||
->param('bundleId', '', new Text(0), 'APNS bundle ID.')
|
||||
->param('endpoint', '', new Text(0), 'APNS endpoint.')
|
||||
->param('enabled', true, new Boolean(), 'Set as enabled.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, bool $enabled, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, bool $enabled, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$providerId = $providerId == 'unique()' ? ID::unique() : $providerId;
|
||||
$provider = new Document([
|
||||
'$id' => $providerId,
|
||||
'name' => $name,
|
||||
'provider' => 'apns',
|
||||
'type' => 'push',
|
||||
'search' => $providerId . ' ' . $name . ' ' . 'apns' . ' ' . 'push',
|
||||
'enabled' => $enabled,
|
||||
'credentials' => [
|
||||
'authKey' => $authKey,
|
||||
|
@ -593,16 +507,6 @@ App::post('/v1/messaging/providers/apns')
|
|||
],
|
||||
]);
|
||||
|
||||
// Check if a internal provider exists, if not, set this one as internal
|
||||
if (
|
||||
empty($dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['push'])
|
||||
]))
|
||||
) {
|
||||
$provider->setAttribute('internal', true);
|
||||
}
|
||||
|
||||
try {
|
||||
$provider = $dbForProject->createDocument('providers', $provider);
|
||||
} catch (DuplicateException) {
|
||||
|
@ -784,15 +688,14 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('isEuRegion', null, new Boolean(), 'Set as eu region.', true)
|
||||
->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true)
|
||||
->param('from', '', new Text(256), 'Sender email address.', true)
|
||||
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
|
||||
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, ?bool $isEuRegion, string $from, string $apiKey, string $domain, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $isEuRegion, string $from, string $apiKey, string $domain, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -806,7 +709,6 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'mailgun' . ' ' . 'email');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -819,10 +721,6 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if ($isEuRegion === true || $isEuRegion === false) {
|
||||
|
@ -841,15 +739,6 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -874,13 +763,12 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
|
||||
->param('from', '', new Text(256), 'Sender email address.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $apiKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $apiKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -894,7 +782,6 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'sendgrid' . ' ' . 'email');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -907,10 +794,6 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
if (!empty($apiKey)) {
|
||||
$provider->setAttribute('credentials', [
|
||||
'apiKey' => $apiKey,
|
||||
|
@ -919,15 +802,6 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -952,14 +826,13 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('senderId', '', new Text(0), 'Msg91 Sender ID.', true)
|
||||
->param('authKey', '', new Text(0), 'Msg91 Auth Key.', true)
|
||||
->param('from', '', new Text(256), 'Sender number.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $senderId, string $authKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $senderId, string $authKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -973,7 +846,6 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'msg91' . ' ' . 'sms');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -986,10 +858,6 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($senderId)) {
|
||||
|
@ -1004,15 +872,6 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1037,14 +896,13 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('username', '', new Text(0), 'Telesign username.', true)
|
||||
->param('password', '', new Text(0), 'Telesign password.', true)
|
||||
->param('from', '', new Text(256), 'Sender number.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $username, string $password, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $username, string $password, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1058,7 +916,6 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'telesign' . ' ' . 'sms');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -1071,10 +928,6 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($username)) {
|
||||
|
@ -1089,15 +942,6 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1122,14 +966,13 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('username', '', new Text(0), 'Textmagic username.', true)
|
||||
->param('apiKey', '', new Text(0), 'Textmagic apiKey.', true)
|
||||
->param('from', '', new Text(256), 'Sender number.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $username, string $apiKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $username, string $apiKey, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1137,13 +980,12 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
|||
}
|
||||
$providerAttr = $provider->getAttribute('provider');
|
||||
|
||||
if ($providerAttr !== 'text-magic') {
|
||||
if ($providerAttr !== 'textmagic') {
|
||||
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||
}
|
||||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'textmagic' . ' ' . 'sms');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -1156,10 +998,6 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($username)) {
|
||||
|
@ -1174,15 +1012,6 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1207,14 +1036,13 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('accountSid', null, new Text(0), 'Twilio account secret ID.', true)
|
||||
->param('authToken', null, new Text(0), 'Twilio authentication token.', true)
|
||||
->param('from', '', new Text(256), 'Sender number.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $accountSid, string $authToken, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $accountSid, string $authToken, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1228,7 +1056,6 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'twilio' . ' ' . 'sms');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -1241,10 +1068,6 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($accountSid)) {
|
||||
|
@ -1259,15 +1082,6 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1292,14 +1106,13 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
|
||||
->param('apiSecret', '', new Text(0), 'Vonage API secret.', true)
|
||||
->param('from', '', new Text(256), 'Sender number.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $apiKey, string $apiSecret, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $apiKey, string $apiSecret, string $from, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1313,7 +1126,6 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'vonage' . ' ' . 'sms');
|
||||
}
|
||||
|
||||
if (!empty($from)) {
|
||||
|
@ -1326,10 +1138,6 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
|||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($apiKey)) {
|
||||
|
@ -1344,15 +1152,6 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1377,12 +1176,11 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('serverKey', '', new Text(0), 'FCM Server Key.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $serverKey, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $serverKey, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1396,32 +1194,18 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'fcm' . ' ' . 'push');
|
||||
}
|
||||
|
||||
if ($enabled === true || $enabled === false) {
|
||||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
if (!empty($serverKey)) {
|
||||
$provider->setAttribute('credentials', ['serverKey' => $serverKey]);
|
||||
}
|
||||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1447,7 +1231,6 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('name', '', new Text(128), 'Provider name.', true)
|
||||
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
|
||||
->param('internal', null, new Boolean(), 'Set as internal. Internal providers are used in services other than Messaging service such as Authentication service', true)
|
||||
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
|
||||
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
|
||||
->param('teamId', '', new Text(0), 'APNS team ID.', true)
|
||||
|
@ -1456,7 +1239,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, ?bool $internal, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
->action(function (string $providerId, string $name, ?bool $enabled, string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint, Event $queueForEvents, Database $dbForProject, Response $response) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
|
@ -1470,17 +1253,12 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
|
||||
if (!empty($name)) {
|
||||
$provider->setAttribute('name', $name);
|
||||
$provider->setAttribute('search', $provider->getId() . ' ' . $name . ' ' . 'apns' . ' ' . 'push');
|
||||
}
|
||||
|
||||
if ($enabled === true || $enabled === false) {
|
||||
$provider->setAttribute('enabled', $enabled);
|
||||
}
|
||||
|
||||
if ($internal === true) {
|
||||
$provider->setAttribute('internal', $internal);
|
||||
}
|
||||
|
||||
$credentials = $provider->getAttribute('credentials');
|
||||
|
||||
if (!empty($authKey)) {
|
||||
|
@ -1507,15 +1285,6 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
|||
|
||||
$provider = $dbForProject->updateDocument('providers', $provider->getId(), $provider);
|
||||
|
||||
if ($internal === true) {
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
'internal' => true,
|
||||
'type' => 'email',
|
||||
]);
|
||||
$internalProvider->setAttribute('internal', false);
|
||||
$dbForProject->updateDocument('providers', $internalProvider->getId(), $internalProvider);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('providerId', $provider->getId());
|
||||
|
||||
|
@ -1588,9 +1357,6 @@ App::post('/v1/messaging/topics')
|
|||
|
||||
if ($description) {
|
||||
$topic->setAttribute('description', $description);
|
||||
$topic->setAttribute('search', $topic->getId() . ' ' . $name . ' ' . $description);
|
||||
} else {
|
||||
$topic->setAttribute('search', $topic->getId() . ' ' . $name);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -1796,16 +1562,6 @@ App::patch('/v1/messaging/topics/:topicId')
|
|||
$topic->setAttribute('description', $description);
|
||||
}
|
||||
|
||||
if (!empty($name) || !empty($description)) {
|
||||
if (!empty($name) && !empty($description)) {
|
||||
$topic->setAttribute('search', $topic->getId() . ' ' . $name . ' ' . $description);
|
||||
} elseif (!empty($name)) {
|
||||
$topic->setAttribute('search', $topic->getId() . ' ' . $name . ' ' . $topic->getAttribute('description'));
|
||||
} else {
|
||||
$topic->setAttribute('search', $topic->getId() . ' ' . $topic->getAttribute('name') . ' ' . $description);
|
||||
}
|
||||
}
|
||||
|
||||
$topic = $dbForProject->updateDocument('topics', $topicId, $topic);
|
||||
|
||||
$queueForEvents
|
||||
|
@ -1856,7 +1612,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
|||
});
|
||||
|
||||
App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('Adds a subscriber to a topic.')
|
||||
->desc('Create a subscriber.')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.create')
|
||||
->label('audits.resource', 'subscriber/{response.$id}')
|
||||
|
@ -1869,9 +1625,9 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SUBSCRIBER)
|
||||
->param('subscriberId', '', new CustomId(), 'Subscriber ID. Choose a custom Topic ID or a new Topic ID.')
|
||||
->param('topicId', '', new UID(), 'Topic ID.')
|
||||
->param('targetId', '', new UID(), 'Target ID.')
|
||||
->param('subscriberId', '', new CustomId(), 'Subscriber ID. Choose a custom Subscriber ID or a new Subscriber ID.')
|
||||
->param('topicId', '', new UID(), 'Topic ID. The topic ID to subscribe to.')
|
||||
->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.')
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
|
@ -1890,12 +1646,16 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
|||
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
|
||||
|
||||
$subscriber = new Document([
|
||||
'$id' => $subscriberId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($target->getAttribute('userId'))),
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($target->getAttribute('userId'))),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'topicId' => $topicId,
|
||||
'topicInternalId' => $topic->getInternalId(),
|
||||
'targetId' => $targetId,
|
||||
|
@ -1913,13 +1673,15 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
|||
->setParam('topicId', $topic->getId())
|
||||
->setParam('subscriberId', $subscriber->getId());
|
||||
|
||||
$subscriber->setAttribute('userName', $user->getAttribute('name'));
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('List topic\'s subscribers.')
|
||||
->desc('List subscribers.')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
|
@ -1929,7 +1691,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SUBSCRIBER_LIST)
|
||||
->param('topicId', '', new UID(), 'Topic ID.')
|
||||
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
|
||||
->param('queries', [], new Subscribers(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Providers::ALLOWED_ATTRIBUTES), true)
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
|
@ -1959,9 +1721,20 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
|||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||
|
||||
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
|
||||
return function () use ($subscriber, $dbForProject) {
|
||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $subscriber->getAttribute('userId')));
|
||||
|
||||
return $subscriber
|
||||
->setAttribute('userName', $user->getAttribute('name'));
|
||||
};
|
||||
}, $subscribers));
|
||||
|
||||
$response
|
||||
->dynamic(new Document([
|
||||
'subscribers' => $dbForProject->find('subscribers', $queries),
|
||||
'subscribers' => $subscribers,
|
||||
'total' => $dbForProject->count('subscribers', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_SUBSCRIBER_LIST);
|
||||
});
|
||||
|
@ -2050,8 +1823,8 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
|||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/subscriber/:subscriberId')
|
||||
->desc('Get a topic\'s subscriber.')
|
||||
App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Get a subscriber.')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
|
@ -2061,7 +1834,7 @@ App::get('/v1/messaging/topics/:topicId/subscriber/:subscriberId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SUBSCRIBER)
|
||||
->param('topicId', '', new UID(), 'Topic ID.')
|
||||
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
|
||||
->param('subscriberId', '', new UID(), 'Subscriber ID.')
|
||||
->inject('dbForProject')
|
||||
->inject('response')
|
||||
|
@ -2078,12 +1851,16 @@ App::get('/v1/messaging/topics/:topicId/subscriber/:subscriberId')
|
|||
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $subscriber->getAttribute('userId')));
|
||||
|
||||
$subscriber->setAttribute('userName', $user->getAttribute('name'));
|
||||
|
||||
$response
|
||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||
});
|
||||
|
||||
App::delete('/v1/messaging/topics/:topicId/subscriber/:subscriberId')
|
||||
->desc('Delete a subscriber from a topic.')
|
||||
App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Delete a subscriber.')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.delete')
|
||||
->label('audits.resource', 'subscriber/{request.$subscriberId}')
|
||||
|
@ -2096,7 +1873,7 @@ App::delete('/v1/messaging/topics/:topicId/subscriber/:subscriberId')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('topicId', '', new UID(), 'Topic ID.')
|
||||
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
|
||||
->param('subscriberId', '', new UID(), 'Subscriber ID.')
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForProject')
|
||||
|
@ -2149,7 +1926,7 @@ App::post('/v1/messaging/messages/email')
|
|||
->param('description', '', new Text(256), 'Description for message.', true)
|
||||
->param('status', 'processing', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('html', false, new Boolean(), 'Is content of type HTML', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2174,7 +1951,6 @@ App::post('/v1/messaging/messages/email')
|
|||
'html' => $html,
|
||||
],
|
||||
'status' => $status,
|
||||
'search' => $messageId . ' ' . $description . ' ' . $subject,
|
||||
]));
|
||||
|
||||
if ($status === 'processing') {
|
||||
|
@ -2213,7 +1989,7 @@ App::post('/v1/messaging/messages/sms')
|
|||
->param('targets', [], new ArrayList(new Text(Database::LENGTH_KEY), 1), 'List of Targets IDs.', true)
|
||||
->param('description', '', new Text(256), 'Description for Message.', true)
|
||||
->param('status', 'processing', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2236,7 +2012,6 @@ App::post('/v1/messaging/messages/sms')
|
|||
'content' => $content,
|
||||
],
|
||||
'status' => $status,
|
||||
'search' => $messageId . ' ' . $description,
|
||||
]));
|
||||
|
||||
if ($status === 'processing') {
|
||||
|
@ -2283,7 +2058,7 @@ App::post('/v1/messaging/messages/push')
|
|||
->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('status', 'processing', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2338,7 +2113,6 @@ App::post('/v1/messaging/messages/push')
|
|||
'deliveryTime' => $deliveryTime,
|
||||
'data' => $pushData,
|
||||
'status' => $status,
|
||||
'search' => $messageId . ' ' . $description . ' ' . $title,
|
||||
]));
|
||||
|
||||
if ($status === 'processing') {
|
||||
|
@ -2530,7 +2304,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
|||
->param('content', '', new Text(64230), 'Email Content.', true)
|
||||
->param('status', '', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('html', false, new Boolean(), 'Is content of type HTML', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2583,8 +2357,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
|||
$message->setAttribute('description', $description);
|
||||
}
|
||||
|
||||
$message->setAttribute('search', $message->getId() . ' ' . $message->getAttribute('description') . ' ' . $data['subject'] . ' ' . $message->getAttribute('providerId'));
|
||||
|
||||
if (!empty($status)) {
|
||||
$message->setAttribute('status', $status);
|
||||
}
|
||||
|
@ -2630,7 +2402,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
|||
->param('description', '', new Text(256), 'Description for Message.', true)
|
||||
->param('content', '', new Text(64230), 'Email Content.', true)
|
||||
->param('status', '', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2683,8 +2455,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
|||
$message->setAttribute('deliveryTime', $deliveryTime);
|
||||
}
|
||||
|
||||
$message->setAttribute('search', $message->getId() . ' ' . $message->getAttribute('description') . ' ' . $message->getAttribute('providerId'));
|
||||
|
||||
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||
|
||||
if ($status === 'processing') {
|
||||
|
@ -2729,7 +2499,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
|||
->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('badge', '', new Text(256), 'Badge for push notification. Available only for IOS Platform.', true) ->param('status', 'processing', new WhiteList(['draft', 'processing']), 'Message Status. Value must be either draft or processing.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), 'Delivery time for message in ISO 8601 format. DateTime value must be in future.', true)
|
||||
->param('deliveryTime', null, new DatetimeValidator(requireDateInFuture: true), '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('dbForProject')
|
||||
->inject('project')
|
||||
|
@ -2814,8 +2584,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
|||
$message->setAttribute('deliveryTime', $deliveryTime);
|
||||
}
|
||||
|
||||
$message->setAttribute('search', $message->getId() . ' ' . $message->getAttribute('description') . ' ' . $pushData['title'] . ' ' . $message->getAttribute('providerId'));
|
||||
|
||||
$message = $dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||
|
||||
if ($status === 'processing') {
|
||||
|
|
|
@ -1154,7 +1154,7 @@ App::post('/v1/projects/:projectId/keys')
|
|||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
|
||||
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||
|
@ -1271,7 +1271,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
->param('keyId', '', new UID(), 'Key unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
|
||||
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
|
||||
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||
|
|
|
@ -628,12 +628,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
->trigger()
|
||||
;
|
||||
} elseif (!empty($phone)) {
|
||||
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
|
||||
Query::equal('internal', [true]),
|
||||
Query::equal('type', ['sms'])
|
||||
]));
|
||||
|
||||
if ($provider === false || $provider->isEmpty()) {
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
|
@ -647,28 +642,17 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$message = $message->setParam('{{token}}', $url);
|
||||
$message = $message->render();
|
||||
|
||||
$target = $dbForProject->createDocument('targets', new Document([
|
||||
'userId' => $invitee->getId(),
|
||||
'userInternalId' => $invitee->getInternalId(),
|
||||
'providerId' => $provider->getId(),
|
||||
'providerInternalId' => $provider->getInternalId(),
|
||||
'identifier' => $phone,
|
||||
]));
|
||||
|
||||
$messageDoc = $dbForProject->createDocument('messages', new Document([
|
||||
// Here membership ID is used as message ID so that it can be used in test cases to verify the message
|
||||
'$id' => $membership->getId(),
|
||||
'targets' => [$target->getId()],
|
||||
$messageDoc = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'data' => [
|
||||
'content' => $message,
|
||||
],
|
||||
'providerId' => $provider->getId(),
|
||||
'providerInternalId' => $provider->getInternalId(),
|
||||
'deliveryTime' => Datetime::now(),
|
||||
]));
|
||||
]);
|
||||
|
||||
$queueForMessaging
|
||||
->setMessageId($messageDoc->getId())
|
||||
->setMessage($messageDoc)
|
||||
->setRecipients([$phone])
|
||||
->setProviderType('SMS')
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
}
|
||||
|
|
|
@ -394,19 +394,26 @@ App::post('/v1/users/:userId/targets')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||
->param('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('targetId', '', new UID(), 'Target ID.')
|
||||
->param('providerId', '', new UID(), 'Provider ID.')
|
||||
->param('providerType', '', new WhiteList(['email', 'sms', 'push']), 'The target provider type. Can be one of the following: `email`, `sms` or `push`.')
|
||||
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $userId, string $targetId, string $providerId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||
->action(function (string $targetId, string $userId, string $providerType, string $identifier, string $providerId, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
|
||||
|
||||
$provider = new Document();
|
||||
|
||||
if ($providerType === 'push') {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
throw new Exception(Exception::PROVIDER_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
@ -423,8 +430,9 @@ App::post('/v1/users/:userId/targets')
|
|||
try {
|
||||
$target = $dbForProject->createDocument('targets', new Document([
|
||||
'$id' => $targetId,
|
||||
'providerId' => $providerId,
|
||||
'providerInternalId' => $provider->getInternalId(),
|
||||
'providerId' => $providerId ?? null,
|
||||
'providerInternalId' => $provider->getInternalId() ?? null,
|
||||
'providerType' => $providerType,
|
||||
'userId' => $userId,
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'identifier' => $identifier,
|
||||
|
@ -1223,8 +1231,8 @@ App::patch('/v1/users/:userId/prefs')
|
|||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/targets/:targetId/identifier')
|
||||
->desc('Update user target\'s identifier')
|
||||
App::patch('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Update User target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.update')
|
||||
->label('audits.resource', 'target/{response.$id}')
|
||||
|
@ -1232,19 +1240,19 @@ App::patch('/v1/users/:userId/targets/:targetId/identifier')
|
|||
->label('scope', 'targets.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateTargetIdentifier')
|
||||
->label('sdk.description', '/docs/references/users/update-target-identifier.md')
|
||||
->label('sdk.method', 'updateTarget')
|
||||
->label('sdk.description', '/docs/references/users/update-target.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TARGET)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('targetId', '', new UID(), 'Target ID.')
|
||||
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
|
||||
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
|
||||
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)', true)
|
||||
->inject('queueForEvents')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $userId, string $targetId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||
|
||||
->action(function (string $userId, string $targetId, string $providerId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
|
@ -1261,7 +1269,20 @@ App::patch('/v1/users/:userId/targets/:targetId/identifier')
|
|||
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($identifier) {
|
||||
$target->setAttribute('identifier', $identifier);
|
||||
}
|
||||
|
||||
if ($providerId) {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
|
||||
if ($provider->isEmpty()) {
|
||||
throw new Exception(Exception::PROVIDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$target->setAttribute('providerId', $provider->getId());
|
||||
$target->setAttribute('providerInternalId', $provider->getInternalId());
|
||||
}
|
||||
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
|
|
@ -857,10 +857,10 @@ App::post('/v1/vcs/github/events')
|
|||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
//find functionId from functions table
|
||||
$repositories = $dbForConsole->find('repositories', [
|
||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::limit(100),
|
||||
]);
|
||||
]));
|
||||
|
||||
// create new deployment only on push and not when branch is created
|
||||
if (!$providerBranchCreated) {
|
||||
|
@ -877,13 +877,13 @@ App::post('/v1/vcs/github/events')
|
|||
]);
|
||||
|
||||
foreach ($installations as $installation) {
|
||||
$repositories = $dbForConsole->find('repositories', [
|
||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
||||
Query::equal('installationInternalId', [$installation->getInternalId()]),
|
||||
Query::limit(1000)
|
||||
]);
|
||||
]));
|
||||
|
||||
foreach ($repositories as $repository) {
|
||||
$dbForConsole->deleteDocument('repositories', $repository->getId());
|
||||
Authorization::skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId()));
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('installations', $installation->getId());
|
||||
|
@ -915,10 +915,10 @@ App::post('/v1/vcs/github/events')
|
|||
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
|
||||
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
|
||||
|
||||
$repositories = $dbForConsole->find('repositories', [
|
||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::orderDesc('$createdAt')
|
||||
]);
|
||||
]));
|
||||
|
||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request);
|
||||
} elseif ($parsedPayload["action"] == "closed") {
|
||||
|
@ -929,10 +929,10 @@ App::post('/v1/vcs/github/events')
|
|||
$external = $parsedPayload["external"] ?? true;
|
||||
|
||||
if ($external) {
|
||||
$repositories = $dbForConsole->find('repositories', [
|
||||
$repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::orderDesc('$createdAt')
|
||||
]);
|
||||
]));
|
||||
|
||||
foreach ($repositories as $repository) {
|
||||
$providerPullRequestIds = $repository->getAttribute('providerPullRequestIds', []);
|
||||
|
@ -1046,8 +1046,8 @@ App::delete('/v1/vcs/installations/:installationId')
|
|||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForConsole')
|
||||
->inject('deletes')
|
||||
->action(function (string $installationId, Response $response, Document $project, Database $dbForConsole, Delete $deletes) {
|
||||
->inject('queueForDeletes')
|
||||
->action(function (string $installationId, Response $response, Document $project, Database $dbForConsole, Delete $queueForDeletes) {
|
||||
$installation = $dbForConsole->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
|
@ -1058,7 +1058,7 @@ App::delete('/v1/vcs/installations/:installationId')
|
|||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove installation from DB');
|
||||
}
|
||||
|
||||
$deletes
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($installation);
|
||||
|
||||
|
@ -1092,9 +1092,9 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
|||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$repository = $dbForConsole->getDocument('repositories', $repositoryId, [
|
||||
$repository = Authorization::skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
]);
|
||||
]));
|
||||
|
||||
if ($repository->isEmpty()) {
|
||||
throw new Exception(Exception::REPOSITORY_NOT_FOUND);
|
||||
|
@ -1109,7 +1109,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
|||
|
||||
// TODO: Delete from array when PR is closed
|
||||
|
||||
$repository = $dbForConsole->updateDocument('repositories', $repository->getId(), $repository);
|
||||
$repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository));
|
||||
|
||||
$privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
|
|
70
app/init.php
70
app/init.php
|
@ -105,8 +105,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return
|
|||
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 516;
|
||||
const APP_VERSION_STABLE = '1.4.11';
|
||||
const APP_CACHE_BUSTER = 327;
|
||||
const APP_VERSION_STABLE = '1.4.12';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
@ -557,6 +557,72 @@ Database::addFilter(
|
|||
return [];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'providerSearch',
|
||||
function (mixed $value, Document $provider) {
|
||||
$searchValues = [
|
||||
$provider->getId(),
|
||||
$provider->getAttribute('name', ''),
|
||||
$provider->getAttribute('provider', ''),
|
||||
$provider->getAttribute('type', '')
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'topicSearch',
|
||||
function (mixed $value, Document $topic) {
|
||||
$searchValues = [
|
||||
$topic->getId(),
|
||||
$topic->getAttribute('name', ''),
|
||||
$topic->getAttribute('description', ''),
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'messageSearch',
|
||||
function (mixed $value, Document $message) {
|
||||
$searchValues = [
|
||||
$message->getId(),
|
||||
$message->getAttribute('description', ''),
|
||||
$message->getAttribute('status', ''),
|
||||
];
|
||||
|
||||
$data = \json_decode($message->getAttribute('data', []), true);
|
||||
|
||||
if (\array_key_exists('subject', $data)) {
|
||||
$searchValues = \array_merge($searchValues, [$data['subject'], 'email']);
|
||||
} elseif (\array_key_exists('content', $data)) {
|
||||
$searchValues = \array_merge($searchValues, [$data['content'], 'sms']);
|
||||
} else {
|
||||
$searchValues = \array_merge($searchValues, [$data['title'], 'push']);
|
||||
}
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* DB Formats
|
||||
*/
|
||||
|
|
3
bin/patch-recreate-repositories-documents
Normal file
3
bin/patch-recreate-repositories-documents
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-recreate-repositories-documents $@
|
14
composer.lock
generated
14
composer.lock
generated
|
@ -1906,16 +1906,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.45.1",
|
||||
"version": "0.45.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "0e76f996439b80794ab73c2fffdb51ebd6676e4b"
|
||||
"reference": "dc789f2c1fd8b5ee07ff883e11c9ad7970824788"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/0e76f996439b80794ab73c2fffdb51ebd6676e4b",
|
||||
"reference": "0e76f996439b80794ab73c2fffdb51ebd6676e4b",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/dc789f2c1fd8b5ee07ff883e11c9ad7970824788",
|
||||
"reference": "dc789f2c1fd8b5ee07ff883e11c9ad7970824788",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1956,9 +1956,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.45.1"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.45.2"
|
||||
},
|
||||
"time": "2023-11-01T08:30:19+00:00"
|
||||
"time": "2023-11-15T03:38:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -5822,5 +5822,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
|
|
@ -587,6 +587,8 @@ services:
|
|||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_SMS_FROM
|
||||
- _APP_SMS_PROVIDER
|
||||
|
||||
appwrite-worker-migrations:
|
||||
entrypoint: worker-migrations
|
||||
|
|
|
@ -9,7 +9,11 @@ use Utopia\Queue\Client;
|
|||
class Messaging extends Event
|
||||
{
|
||||
protected ?string $messageId = null;
|
||||
private ?string $deliveryTime = null;
|
||||
protected ?Document $message = null;
|
||||
protected ?array $recipients = null;
|
||||
protected ?string $deliveryTime = null;
|
||||
protected ?string $providerType = null;
|
||||
|
||||
|
||||
public function __construct(protected Connection $connection)
|
||||
{
|
||||
|
@ -20,6 +24,52 @@ class Messaging extends Event
|
|||
->setClass(Event::MESSAGING_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets recipient for the messaging event.
|
||||
*
|
||||
* @param string[] $recipients
|
||||
* @return self
|
||||
*/
|
||||
public function setRecipients(array $recipients): self
|
||||
{
|
||||
$this->recipients = $recipients;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set recipient for messaging event.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getRecipient(): array
|
||||
{
|
||||
return $this->recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets message document for the messaging event.
|
||||
*
|
||||
* @param Document $message
|
||||
* @return self
|
||||
*/
|
||||
public function setMessage(Document $message): self
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message document for the messaging event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): Document
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets message ID for the messaging event.
|
||||
*
|
||||
|
@ -43,6 +93,29 @@ class Messaging extends Event
|
|||
return $this->messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets provider type for the messaging event.
|
||||
*
|
||||
* @param string $providerType
|
||||
* @return self
|
||||
*/
|
||||
public function setProviderType(string $providerType): self
|
||||
{
|
||||
$this->providerType = $providerType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set provider type for the messaging event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProviderType(): string
|
||||
{
|
||||
return $this->providerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Delivery time for the messaging event.
|
||||
*
|
||||
|
@ -92,6 +165,9 @@ class Messaging extends Event
|
|||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'messageId' => $this->messageId,
|
||||
'message' => $this->message,
|
||||
'recipients' => $this->recipients,
|
||||
'providerType' => $this->providerType,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,8 +84,11 @@ class Exception extends \Exception
|
|||
public const USER_OAUTH2_BAD_REQUEST = 'user_oauth2_bad_request';
|
||||
public const USER_OAUTH2_UNAUTHORIZED = 'user_oauth2_unauthorized';
|
||||
public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error';
|
||||
public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_alread_verified';
|
||||
public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified';
|
||||
public const USER_TARGET_NOT_FOUND = 'user_target_not_found';
|
||||
public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists';
|
||||
|
||||
/** Teams */
|
||||
public const TEAM_NOT_FOUND = 'team_not_found';
|
||||
public const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
|
||||
|
@ -236,6 +239,7 @@ class Exception extends \Exception
|
|||
public const PROVIDER_NOT_FOUND = 'provider_not_found';
|
||||
public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists';
|
||||
public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type';
|
||||
public const PROVIDER_INTERNAL_UPDATE_DISABLED = 'provider_internal_update_disabled';
|
||||
|
||||
/** Topic */
|
||||
public const TOPIC_NOT_FOUND = 'topic_not_found';
|
||||
|
|
|
@ -76,6 +76,7 @@ abstract class Migration
|
|||
'1.4.9' => 'V19',
|
||||
'1.4.10' => 'V19',
|
||||
'1.4.11' => 'V19',
|
||||
'1.4.12' => 'V19'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ use Appwrite\Platform\Tasks\VolumeSync;
|
|||
use Appwrite\Platform\Tasks\CalcTierStats;
|
||||
use Appwrite\Platform\Tasks\Upgrade;
|
||||
use Appwrite\Platform\Tasks\DeleteOrphanedProjects;
|
||||
use Appwrite\Platform\Tasks\PatchRecreateRepositoriesDocuments;
|
||||
|
||||
class Tasks extends Service
|
||||
{
|
||||
|
@ -42,6 +43,7 @@ class Tasks extends Service
|
|||
->addAction(Specs::getName(), new Specs())
|
||||
->addAction(CalcTierStats::getName(), new CalcTierStats())
|
||||
->addAction(DeleteOrphanedProjects::getName(), new DeleteOrphanedProjects())
|
||||
->addAction(PatchRecreateRepositoriesDocuments::getName(), new PatchRecreateRepositoriesDocuments())
|
||||
|
||||
;
|
||||
}
|
||||
|
|
|
@ -2,17 +2,16 @@
|
|||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class DeleteOrphanedProjects extends Action
|
||||
{
|
||||
|
@ -25,18 +24,19 @@ class DeleteOrphanedProjects extends Action
|
|||
{
|
||||
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->desc('Delete orphaned projects')
|
||||
->param('commit', false, new Boolean(true), 'Commit project deletion', true)
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('register')
|
||||
->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
|
||||
$this->action($pools, $cache, $dbForConsole, $register);
|
||||
->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
|
||||
$this->action($commit, $pools, $cache, $dbForConsole, $register);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
|
||||
public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
|
||||
{
|
||||
|
||||
Console::title('Delete orphaned projects V1');
|
||||
|
@ -55,6 +55,7 @@ class DeleteOrphanedProjects extends Action
|
|||
Console::success("Found a total of: {$totalProjects} projects");
|
||||
|
||||
$orphans = 0;
|
||||
$cnt = 0;
|
||||
$count = 0;
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
|
@ -79,19 +80,43 @@ class DeleteOrphanedProjects extends Action
|
|||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDefaultDatabase('appwrite');
|
||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||
$collectionsCreated = 0;
|
||||
$cnt++;
|
||||
if ($dbForProject->exists($dbForProject->getDefaultDatabase(), Database::METADATA)) {
|
||||
$collectionsCreated = $dbForProject->count(Database::METADATA);
|
||||
$message = ' (' . $collectionsCreated . ') collections where found on project (' . $project->getId() . '))';
|
||||
if ($collectionsCreated < (count($collectionsConfig) + 2)) {
|
||||
Console::error($message);
|
||||
$orphans++;
|
||||
} else {
|
||||
Console::log($message);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
//$dbForConsole->deleteDocument('projects', $project->getId());
|
||||
//Console::success('Deleting project (' . $project->getId() . ')');
|
||||
Console::error(' (0) collections where found for project (' . $project->getId() . ')');
|
||||
|
||||
$msg = '(' . $cnt . ') found (' . $collectionsCreated . ') collections on project (' . $project->getInternalId() . ') , database (' . $project['database'] . ')';
|
||||
/**
|
||||
* +2 = audit+abuse
|
||||
*/
|
||||
if ($collectionsCreated >= (count($collectionsConfig) + 2)) {
|
||||
Console::log($msg . ' ignoring....');
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::log($msg);
|
||||
|
||||
if ($collectionsCreated > 0) {
|
||||
$collections = $dbForProject->find(Database::METADATA, []);
|
||||
foreach ($collections as $collection) {
|
||||
if ($commit) {
|
||||
$dbForProject->deleteCollection($collection->getId());
|
||||
$dbForConsole->deleteCachedCollection($collection->getId());
|
||||
}
|
||||
Console::info('--Deleting collection (' . $collection->getId() . ') project no (' . $project->getInternalId() . ')');
|
||||
}
|
||||
}
|
||||
if ($commit) {
|
||||
$dbForConsole->deleteDocument('projects', $project->getId());
|
||||
$dbForConsole->deleteCachedDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
Console::info('--Deleting project no (' . $project->getInternalId() . ')');
|
||||
|
||||
$orphans++;
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Error: ' . $th->getMessage());
|
||||
} finally {
|
||||
$pools
|
||||
->get($db)
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class PatchRecreateRepositoriesDocuments extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'patch-recreate-repositories-documents';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Recreate missing repositories in consoleDB from projectDBs. They can be missing if you used Appwrite 1.4.10 or 1.4.11, and deleted a function.')
|
||||
->param('after', '', new Text(36), 'After cursor', true)
|
||||
->param('projectId', '', new Text(36), 'Select project to validate', true)
|
||||
->inject('dbForConsole')
|
||||
->inject('getProjectDB')
|
||||
->callback(fn ($after, $projectId, $dbForConsole, $getProjectDB) => $this->action($after, $projectId, $dbForConsole, $getProjectDB));
|
||||
}
|
||||
|
||||
public function action($after, $projectId, Database $dbForConsole, callable $getProjectDB): void
|
||||
{
|
||||
Console::info("Starting the patch");
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
if (!empty($projectId)) {
|
||||
try {
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
} else {
|
||||
$queries = [];
|
||||
if (!empty($after)) {
|
||||
Console::info("Iterating remaining projects after project with ID {$after}");
|
||||
$project = $dbForConsole->getDocument('projects', $after);
|
||||
$queries = [Query::cursorAfter($project)];
|
||||
} else {
|
||||
Console::info("Iterating all projects");
|
||||
}
|
||||
$this->foreachDocument($dbForConsole, 'projects', $queries, function (Document $project) use ($getProjectDB, $dbForConsole) {
|
||||
$projectId = $project->getId();
|
||||
|
||||
try {
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$endTime = microtime(true);
|
||||
$timeTaken = $endTime - $startTime;
|
||||
|
||||
$hours = (int)($timeTaken / 3600);
|
||||
$timeTaken -= $hours * 3600;
|
||||
$minutes = (int)($timeTaken / 60);
|
||||
$timeTaken -= $minutes * 60;
|
||||
$seconds = (int)$timeTaken;
|
||||
$milliseconds = ($timeTaken - $seconds) * 1000;
|
||||
Console::info("Recreate patch completed in $hours h, $minutes m, $seconds s, $milliseconds mis ( total $timeTaken milliseconds)");
|
||||
}
|
||||
|
||||
protected function foreachDocument(Database $database, string $collection, array $queries = [], callable $callback = null): void
|
||||
{
|
||||
$limit = 1000;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
$latestDocument = null;
|
||||
|
||||
while ($sum === $limit) {
|
||||
$newQueries = $queries;
|
||||
|
||||
if ($latestDocument != null) {
|
||||
array_unshift($newQueries, Query::cursorAfter($latestDocument));
|
||||
}
|
||||
$newQueries[] = Query::limit($limit);
|
||||
$results = $database->find($collection, $newQueries);
|
||||
|
||||
if (empty($results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
foreach ($results as $document) {
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
}
|
||||
}
|
||||
$latestDocument = $results[array_key_last($results)];
|
||||
}
|
||||
}
|
||||
|
||||
public function recreateRepositories(Database $dbForConsole, Database $dbForProject, Document $project): void
|
||||
{
|
||||
$projectId = $project->getId();
|
||||
Console::log("Running patch for project {$projectId}");
|
||||
|
||||
$this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $dbForConsole, $project) {
|
||||
$isConnected = !empty($function->getAttribute('providerRepositoryId', ''));
|
||||
|
||||
if ($isConnected) {
|
||||
$repository = $dbForConsole->getDocument('repositories', $function->getAttribute('repositoryId', ''));
|
||||
|
||||
if ($repository->isEmpty()) {
|
||||
$projectId = $project->getId();
|
||||
$functionId = $function->getId();
|
||||
Console::success("Recreating repositories document for project ID {$projectId}, function ID {$functionId}");
|
||||
|
||||
$repository = $dbForConsole->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'installationId' => $function->getAttribute('installationId', ''),
|
||||
'installationInternalId' => $function->getAttribute('installationInternalId', ''),
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'providerRepositoryId' => $function->getAttribute('providerRepositoryId', ''),
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceInternalId' => $function->getInternalId(),
|
||||
'resourceType' => 'function',
|
||||
'providerPullRequestIds' => []
|
||||
]));
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function
|
||||
->setAttribute('repositoryId', $repository->getId())
|
||||
->setAttribute('repositoryInternalId', $repository->getInternalId()));
|
||||
|
||||
$this->foreachDocument($dbForProject, 'deployments', [
|
||||
Query::equal('resourceInternalId', [$function->getInternalId()]),
|
||||
Query::equal('resourceType', ['functions'])
|
||||
], function (Document $deployment) use ($dbForProject, $repository) {
|
||||
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment
|
||||
->setAttribute('repositoryId', $repository->getId())
|
||||
->setAttribute('repositoryInternalId', $repository->getInternalId()));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -753,14 +753,15 @@ class Deletes extends Action
|
|||
*/
|
||||
Console::info("Deleting VCS repositories and comments linked to function " . $functionId);
|
||||
$this->deleteByGroup('repositories', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('resourceInternalId', [$functionInternalId]),
|
||||
Query::equal('resourceType', ['function']),
|
||||
], $dbForConsole, function (Document $document) use ($dbForConsole) {
|
||||
$providerRepositoryId = $document->getAttribute('providerRepositoryId', '');
|
||||
$projectId = $document->getAttribute('projectId', '');
|
||||
$projectInternalId = $document->getAttribute('projectInternalId', '');
|
||||
$this->deleteByGroup('vcsComments', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::equal('projectId', [$projectId]),
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
], $dbForConsole);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
namespace Appwrite\Platform\Workers;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Messaging\Adapters\SMS as SMSAdapter;
|
||||
use Utopia\Messaging\Adapters\SMS\Mock;
|
||||
use Utopia\Messaging\Adapters\SMS\Msg91;
|
||||
|
@ -64,10 +66,16 @@ class Messaging extends Action
|
|||
return;
|
||||
}
|
||||
|
||||
if (!\is_null($payload['message']) && !\is_null($payload['recipients'])) {
|
||||
if ($payload['providerType'] === 'SMS') {
|
||||
$this->processInternalSMSMessage(new Document($payload['message']), $payload['recipients']);
|
||||
}
|
||||
} else {
|
||||
$message = $dbForProject->getDocument('messages', $payload['messageId']);
|
||||
|
||||
$this->processMessage($dbForProject, $message);
|
||||
}
|
||||
}
|
||||
|
||||
private function processMessage(Database $dbForProject, Document $message): void
|
||||
{
|
||||
|
@ -99,28 +107,56 @@ class Messaging extends Action
|
|||
$recipients = \array_merge($recipients, $targets);
|
||||
}
|
||||
|
||||
$internalProvider = $dbForProject->findOne('providers', [
|
||||
Query::equal('enabled', [true]),
|
||||
Query::equal('type', [$recipients[0]->getAttribute('providerType')]),
|
||||
]);
|
||||
|
||||
/**
|
||||
* @var array<string, array<string>> $identifiersByProviderId
|
||||
*/
|
||||
$identifiersByProviderId = [];
|
||||
|
||||
/**
|
||||
* @var Document[] $providers
|
||||
*/
|
||||
$providers = [];
|
||||
foreach ($recipients as $recipient) {
|
||||
$providerId = $recipient->getAttribute('providerId');
|
||||
if (!isset($providers[$providerId])) {
|
||||
$providers[$providerId] = [];
|
||||
|
||||
if (!$providerId) {
|
||||
$providerId = $internalProvider->getId();
|
||||
}
|
||||
$providers[$providerId][] = $recipient->getAttribute('identifier');
|
||||
|
||||
if (!isset($identifiersByProviderId[$providerId])) {
|
||||
$identifiersByProviderId[$providerId] = [];
|
||||
}
|
||||
$identifiersByProviderId[$providerId][] = $recipient->getAttribute('identifier');
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array[] $results
|
||||
*/
|
||||
$results = batch(\array_map(function ($providerId) use ($providers, $message, $dbForProject) {
|
||||
return function () use ($providerId, $providers, $message, $dbForProject) {
|
||||
$results = batch(\array_map(function ($providerId) use ($identifiersByProviderId, $providers, $internalProvider, $message, $dbForProject) {
|
||||
return function () use ($providerId, $identifiersByProviderId, $providers, $internalProvider, $message, $dbForProject) {
|
||||
$provider = new Document();
|
||||
|
||||
if ($internalProvider->getId() === $providerId) {
|
||||
$provider = $internalProvider;
|
||||
} else {
|
||||
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||
$identifiers = $providers[$providerId];
|
||||
}
|
||||
|
||||
$providers[] = $provider;
|
||||
$identifiers = $identifiersByProviderId[$providerId];
|
||||
|
||||
$adapter = match ($provider->getAttribute('type')) {
|
||||
'sms' => $this->sms($provider),
|
||||
'push' => $this->push($provider),
|
||||
'email' => $this->email($provider),
|
||||
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
||||
};
|
||||
|
||||
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
||||
$batches = \array_chunk($identifiers, $maxBatchSize);
|
||||
$batchIndex = 0;
|
||||
|
@ -131,12 +167,14 @@ class Messaging extends Action
|
|||
$deliveryErrors = [];
|
||||
$messageData = clone $message;
|
||||
$messageData->setAttribute('to', $batch);
|
||||
|
||||
$data = match ($provider->getAttribute('type')) {
|
||||
'sms' => $this->buildSMSMessage($messageData, $provider),
|
||||
'push' => $this->buildPushMessage($messageData),
|
||||
'email' => $this->buildEmailMessage($messageData, $provider),
|
||||
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
||||
};
|
||||
|
||||
try {
|
||||
$adapter->send($data);
|
||||
$deliveredTotal += \count($batch);
|
||||
|
@ -154,16 +192,18 @@ class Messaging extends Action
|
|||
|
||||
return $results;
|
||||
};
|
||||
}, \array_keys($providers)));
|
||||
}, \array_keys($identifiersByProviderId)));
|
||||
|
||||
$results = array_merge(...$results);
|
||||
|
||||
$deliveredTotal = 0;
|
||||
$deliveryErrors = [];
|
||||
|
||||
foreach ($results as $result) {
|
||||
$deliveredTotal += $result['deliveredTotal'];
|
||||
$deliveryErrors = \array_merge($deliveryErrors, $result['deliveryErrors']);
|
||||
}
|
||||
|
||||
$message->setAttribute('deliveryErrors', $deliveryErrors);
|
||||
|
||||
if (\count($message->getAttribute('deliveryErrors')) > 0) {
|
||||
|
@ -171,13 +211,88 @@ class Messaging extends Action
|
|||
} else {
|
||||
$message->setAttribute('status', 'sent');
|
||||
}
|
||||
|
||||
$message->removeAttribute('to');
|
||||
|
||||
foreach ($providers as $provider) {
|
||||
$message->setAttribute('search', "{$message->getAttribute('search')} {$provider->getAttribute('name')} {$provider->getAttribute('provider')} {$provider->getAttribute('type')}");
|
||||
}
|
||||
|
||||
$message->setAttribute('deliveredTotal', $deliveredTotal);
|
||||
$message->setAttribute('deliveredAt', DateTime::now());
|
||||
|
||||
$dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||
}
|
||||
|
||||
private function processInternalSMSMessage(Document $message, array $recipients): void
|
||||
{
|
||||
if (empty(App::getEnv('_APP_SMS_PROVIDER')) || empty(App::getEnv('_APP_SMS_FROM'))) {
|
||||
Console::info('Skipped SMS processing. No Phone configuration has been set.');
|
||||
return;
|
||||
}
|
||||
|
||||
$smsDSN = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
|
||||
$host = $smsDSN->getHost();
|
||||
$password = $smsDSN->getPassword();
|
||||
$user = $smsDSN->getUser();
|
||||
|
||||
$from = App::getEnv('_APP_SMS_FROM');
|
||||
|
||||
$provider = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'provider' => $host,
|
||||
'type' => 'sms',
|
||||
'name' => 'Internal SMS',
|
||||
'enabled' => true,
|
||||
'credentials' => match ($host) {
|
||||
'twilio' => [
|
||||
'accountSid' => $user,
|
||||
'authToken' => $password
|
||||
],
|
||||
'textmagic' => [
|
||||
'username' => $user,
|
||||
'apiKey' => $password
|
||||
],
|
||||
'telesign' => [
|
||||
'username' => $user,
|
||||
'password' => $password
|
||||
],
|
||||
'msg91' => [
|
||||
'senderId' => $user,
|
||||
'authKey' => $password
|
||||
],
|
||||
'vonage' => [
|
||||
'apiKey' => $user,
|
||||
'apiSecret' => $password
|
||||
],
|
||||
default => null
|
||||
},
|
||||
'options' => [
|
||||
'from' => $from
|
||||
]
|
||||
]);
|
||||
|
||||
$adapter = $this->sms($provider);
|
||||
|
||||
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
||||
$batches = \array_chunk($recipients, $maxBatchSize);
|
||||
$batchIndex = 0;
|
||||
|
||||
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex) {
|
||||
return function () use ($batch, $message, $provider, $adapter, $batchIndex) {
|
||||
$message->setAttribute('to', $batch);
|
||||
|
||||
$data = $this->buildSMSMessage($message, $provider);
|
||||
|
||||
try {
|
||||
$adapter->send($data);
|
||||
} catch (\Exception $e) {
|
||||
Console::error('Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage());
|
||||
}
|
||||
};
|
||||
}, $batches));
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
}
|
||||
|
@ -188,7 +303,7 @@ class Messaging extends Action
|
|||
return match ($provider->getAttribute('provider')) {
|
||||
'mock' => new Mock('username', 'password'),
|
||||
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
|
||||
'text-magic' => new TextMagic($credentials['username'], $credentials['apiKey']),
|
||||
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
|
||||
'telesign' => new Telesign($credentials['username'], $credentials['password']),
|
||||
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey']),
|
||||
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
|
||||
|
|
|
@ -8,6 +8,7 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Document;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use DateTime;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
class TimeSeries extends Calculator
|
||||
|
@ -426,6 +427,7 @@ class TimeSeries extends Calculator
|
|||
$project = $this->database->getDocument('projects', $projectId);
|
||||
$database = call_user_func($this->getProjectDB, $project);
|
||||
|
||||
Authorization::skip(function () use ($database, $id, $period, $time, $metric, $value, $type, $projectId) {
|
||||
try {
|
||||
$document = $database->getDocument('stats', $id);
|
||||
if ($document->isEmpty()) {
|
||||
|
@ -452,6 +454,7 @@ class TimeSeries extends Calculator
|
|||
throw $e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$this->register->get('pools')->reclaim();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ class Providers extends Base
|
|||
'name',
|
||||
'provider',
|
||||
'type',
|
||||
'internal',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
|
|
|
@ -40,12 +40,6 @@ class Provider extends Model
|
|||
'default' => '',
|
||||
'example' => 'mailgun',
|
||||
])
|
||||
->addRule('internal', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Is this a pre-configured provider instance?',
|
||||
'default' => false,
|
||||
'example' => true,
|
||||
])
|
||||
->addRule('enabled', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Is provider enabled?',
|
||||
|
|
|
@ -34,6 +34,18 @@ class Subscriber extends Model
|
|||
'default' => '',
|
||||
'example' => '259125845563242502',
|
||||
])
|
||||
->addRule('userId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'User ID.',
|
||||
'default' => '',
|
||||
'example' => '5e5ea5c16897e',
|
||||
])
|
||||
->addRule('userName', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'User Name.',
|
||||
'default' => '',
|
||||
'example' => 'Aegon Targaryen',
|
||||
])
|
||||
->addRule('topicId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Topic ID.',
|
||||
|
|
|
@ -41,6 +41,12 @@ class Target extends Model
|
|||
'default' => '',
|
||||
'example' => '259125845563242502',
|
||||
])
|
||||
->addRule('providerType', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The target provider type. Can be one of the following: `email`, `sms` or `push`.',
|
||||
'default' => '',
|
||||
'example' => 'email',
|
||||
])
|
||||
->addRule('identifier', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The target identifier.',
|
||||
|
|
|
@ -945,6 +945,32 @@ trait AccountBase
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateAccountVerification
|
||||
*/
|
||||
public function testCreateAccountVerificationForVerifiedEmail($data): array
|
||||
{
|
||||
$email = $data['email'] ?? '';
|
||||
$name = $data['name'] ?? '';
|
||||
$session = $data['session'] ?? '';
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'url' => 'http://localhost/verification',
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateAccountVerification
|
||||
*/
|
||||
|
|
|
@ -744,33 +744,8 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
public function testCreatePhone(): array
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) {
|
||||
$this->markTestSkipped('SMS DSN not provided');
|
||||
}
|
||||
$number = '+123456789';
|
||||
|
||||
$smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'));
|
||||
$to = $smsDSN->getParam('to');
|
||||
$from = $smsDSN->getParam('from');
|
||||
$authKey = $smsDSN->getPassword();
|
||||
$senderId = $smsDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($authKey) || empty($senderId)) {
|
||||
$this->markTestSkipped('SMS provider not configured');
|
||||
}
|
||||
|
||||
$number = $to;
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/providers/msg91', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Sms provider',
|
||||
'senderId' => $senderId,
|
||||
'authKey' => $authKey,
|
||||
'from' => $from,
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
@ -781,7 +756,6 @@ class AccountCustomClientTest extends Scope
|
|||
]), [
|
||||
'userId' => ID::unique(),
|
||||
'phone' => $number,
|
||||
'from' => $from,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
@ -807,19 +781,17 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
\sleep(5);
|
||||
|
||||
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $messageId, [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
$smsRequest = $this->getLastRequest();
|
||||
|
||||
$this->assertEquals(200, $message['headers']['status-code']);
|
||||
$this->assertEquals(1, $message['body']['deliveredTotal']);
|
||||
$this->assertEquals(0, \count($message['body']['deliveryErrors']));
|
||||
$this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']);
|
||||
$this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']);
|
||||
$this->assertEquals('username', $smsRequest['headers']['X-Username']);
|
||||
$this->assertEquals('password', $smsRequest['headers']['X-Key']);
|
||||
$this->assertEquals('POST', $smsRequest['method']);
|
||||
$this->assertEquals('+123456789', $smsRequest['data']['from']);
|
||||
$this->assertEquals($number, $smsRequest['data']['to']);
|
||||
|
||||
|
||||
$data['token'] = $message['body']['data']['content'];
|
||||
$data['token'] = $smsRequest['data']['message'];
|
||||
$data['id'] = $userId;
|
||||
$data['number'] = $number;
|
||||
|
||||
|
@ -1018,8 +990,6 @@ class AccountCustomClientTest extends Scope
|
|||
public function testPhoneVerification(array $data): array
|
||||
{
|
||||
$session = $data['session'] ?? '';
|
||||
$smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'));
|
||||
$from = $smsDSN->getParam('from');
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -1030,28 +1000,19 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
|
||||
]), ['from' => $from]);
|
||||
]));
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertEmpty($response['body']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
|
||||
|
||||
\sleep(3);
|
||||
\sleep(2);
|
||||
|
||||
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $response['body']['$id'], [
|
||||
'origin' => 'http://localhost',
|
||||
'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(1, $message['body']['deliveredTotal']);
|
||||
$this->assertEquals(0, \count($message['body']['deliveryErrors']));
|
||||
$smsRequest = $this->getLastRequest();
|
||||
|
||||
return \array_merge($data, [
|
||||
'token' => $message['body']['data']['content']
|
||||
'token' => $smsRequest['data']['secret']
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -1108,4 +1069,27 @@ class AccountCustomClientTest extends Scope
|
|||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testPhoneVerification
|
||||
*/
|
||||
#[Retry(count: 1)]
|
||||
public function testPhoneVerificationForVerifiedPhone(array $data): array
|
||||
{
|
||||
$session = $data['session'] ?? '';
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/verification/phone', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals(409, $response['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,38 +124,7 @@ class AccountTest extends Scope
|
|||
*/
|
||||
public function testCreatePhoneVerification(): array
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) {
|
||||
$this->markTestSkipped('SMS DSN not provided');
|
||||
}
|
||||
|
||||
$smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'));
|
||||
$to = $smsDSN->getParam('to');
|
||||
$from = $smsDSN->getParam('from');
|
||||
$authKey = $smsDSN->getPassword();
|
||||
$senderId = $smsDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($authKey) || empty($senderId)) {
|
||||
$this->markTestSkipped('SMS provider not configured');
|
||||
}
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$CREATE_MSG91_PROVIDER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Sms Provider',
|
||||
'from' => $from,
|
||||
'senderId' => $senderId,
|
||||
'authKey' => $authKey,
|
||||
],
|
||||
];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/graphql', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], $graphQLPayload);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_PHONE_VERIFICATION);
|
||||
$graphQLPayload = [
|
||||
|
|
|
@ -934,10 +934,11 @@ trait Base
|
|||
}
|
||||
}';
|
||||
case self::$CREATE_USER_TARGET:
|
||||
return 'mutation createUserTarget($userId: String!, $targetId: String!, $providerId: String!, $identifier: String!){
|
||||
usersCreateTarget(userId: $userId, targetId: $targetId, providerId: $providerId, identifier: $identifier) {
|
||||
return 'mutation createUserTarget($userId: String!, $targetId: String!, $providerType: String!, $identifier: String! $providerId: String){
|
||||
usersCreateTarget(userId: $userId, targetId: $targetId, providerType: $providerType, identifier: $identifier, providerId: $providerId) {
|
||||
_id
|
||||
userId
|
||||
providerType
|
||||
providerId
|
||||
identifier
|
||||
}
|
||||
|
@ -949,6 +950,7 @@ trait Base
|
|||
targets {
|
||||
_id
|
||||
userId
|
||||
providerType
|
||||
providerId
|
||||
identifier
|
||||
}
|
||||
|
@ -959,15 +961,17 @@ trait Base
|
|||
usersGetTarget(userId: $userId, targetId: $targetId) {
|
||||
_id
|
||||
userId
|
||||
providerType
|
||||
providerId
|
||||
identifier
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_USER_TARGET:
|
||||
return 'mutation updateUserTarget($userId: String!, $targetId: String!, $identifier: String!){
|
||||
usersUpdateTargetIdentifier(userId: $userId, targetId: $targetId, identifier: $identifier) {
|
||||
return 'mutation updateUserTarget($userId: String!, $targetId: String!, $providerId: String, $identifier: String){
|
||||
usersUpdateTarget(userId: $userId, targetId: $targetId, providerId: $providerId, identifier: $identifier) {
|
||||
_id
|
||||
userId
|
||||
providerType
|
||||
providerId
|
||||
identifier
|
||||
}
|
||||
|
@ -1792,7 +1796,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1803,7 +1806,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1814,7 +1816,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1825,7 +1826,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1836,7 +1836,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1847,7 +1846,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1858,7 +1856,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1869,7 +1866,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1880,7 +1876,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1893,7 +1888,7 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
|
||||
enabled
|
||||
}
|
||||
}
|
||||
|
@ -1905,7 +1900,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1916,7 +1910,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1927,7 +1920,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1938,7 +1930,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1949,7 +1940,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1960,7 +1950,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1971,7 +1960,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1982,7 +1970,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -1993,7 +1980,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -2004,7 +1990,6 @@ trait Base
|
|||
name
|
||||
provider
|
||||
type
|
||||
internal
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
|
@ -2059,6 +2044,7 @@ trait Base
|
|||
return 'mutation createSubscriber($subscriberId: String!, $targetId: String!, $topicId: String!) {
|
||||
messagingCreateSubscriber(subscriberId: $subscriberId, targetId: $targetId, topicId: $topicId) {
|
||||
_id
|
||||
userId
|
||||
targetId
|
||||
topicId
|
||||
}
|
||||
|
@ -2069,6 +2055,7 @@ trait Base
|
|||
total
|
||||
subscribers {
|
||||
_id
|
||||
userId
|
||||
targetId
|
||||
topicId
|
||||
}
|
||||
|
@ -2078,6 +2065,7 @@ trait Base
|
|||
return 'query getSubscriber($topicId: String!, $subscriberId: String!) {
|
||||
messagingGetSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
|
||||
_id
|
||||
userId
|
||||
targetId
|
||||
topicId
|
||||
}
|
||||
|
|
|
@ -395,6 +395,7 @@ class MessagingTest extends Scope
|
|||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'email',
|
||||
'userId' => $userId,
|
||||
'providerId' => $providerId,
|
||||
'identifier' => 'token',
|
||||
|
@ -427,20 +428,23 @@ class MessagingTest extends Scope
|
|||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($response['body']['data']['messagingCreateSubscriber']['topicId'], $topicId);
|
||||
$this->assertEquals($response['body']['data']['messagingCreateSubscriber']['targetId'], $targetId);
|
||||
$this->assertEquals($response['body']['data']['messagingCreateSubscriber']['userId'], $userId);
|
||||
|
||||
return $response['body']['data']['messagingCreateSubscriber'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateTopic
|
||||
* @depends testCreateSubscriber
|
||||
*/
|
||||
public function testListSubscribers(string $topicId)
|
||||
public function testListSubscribers(array $subscriber)
|
||||
{
|
||||
$query = $this->getQuery(self::$LIST_SUBSCRIBERS);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'topicId' => $topicId,
|
||||
'topicId' => $subscriber['topicId'],
|
||||
],
|
||||
];
|
||||
$response = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
|
@ -450,6 +454,9 @@ class MessagingTest extends Scope
|
|||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($response['body']['data']['messagingListSubscribers']['subscribers'][0]['topicId'], $subscriber['topicId']);
|
||||
$this->assertEquals($response['body']['data']['messagingListSubscribers']['subscribers'][0]['targetId'], $subscriber['targetId']);
|
||||
$this->assertEquals($response['body']['data']['messagingListSubscribers']['subscribers'][0]['userId'], $subscriber['userId']);
|
||||
$this->assertEquals(1, \count($response['body']['data']['messagingListSubscribers']['subscribers']));
|
||||
}
|
||||
|
||||
|
@ -478,6 +485,9 @@ class MessagingTest extends Scope
|
|||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($subscriberId, $response['body']['data']['messagingGetSubscriber']['_id']);
|
||||
$this->assertEquals($topicId, $response['body']['data']['messagingGetSubscriber']['topicId']);
|
||||
$this->assertEquals($subscriber['targetId'], $response['body']['data']['messagingGetSubscriber']['targetId']);
|
||||
$this->assertEquals($subscriber['userId'], $response['body']['data']['messagingGetSubscriber']['userId']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -604,6 +614,7 @@ class MessagingTest extends Scope
|
|||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'email',
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
|
@ -678,119 +689,13 @@ class MessagingTest extends Scope
|
|||
*/
|
||||
public function testUpdateEmail(array $email)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) {
|
||||
$this->markTestSkipped('Email DSN not provided');
|
||||
}
|
||||
|
||||
$emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'));
|
||||
$to = $emailDSN->getParam('to');
|
||||
$from = $emailDSN->getParam('from');
|
||||
$isEuRegion = $emailDSN->getParam('isEuRegion');
|
||||
$apiKey = $emailDSN->getPassword();
|
||||
$domain = $emailDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($apiKey) || empty($domain) || empty($isEuRegion)) {
|
||||
$this->markTestSkipped('Email provider not configured');
|
||||
}
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_MAILGUN_PROVIDER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Mailgun2',
|
||||
'apiKey' => $apiKey,
|
||||
'domain' => $domain,
|
||||
'from' => $from,
|
||||
'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN),
|
||||
],
|
||||
];
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $provider['headers']['status-code']);
|
||||
|
||||
$providerId = $provider['body']['data']['messagingCreateMailgunProvider']['_id'];
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_TOPIC);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Active users',
|
||||
],
|
||||
];
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $topic['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'userId' => ID::unique(),
|
||||
'email' => 'random2-mail@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]
|
||||
];
|
||||
$user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $user['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER_TARGET);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
],
|
||||
];
|
||||
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $target['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_SUBSCRIBER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'subscriberId' => ID::unique(),
|
||||
'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'],
|
||||
'targetId' => $target['body']['data']['usersCreateTarget']['_id'],
|
||||
],
|
||||
];
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $subscriber['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_EMAIL);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']],
|
||||
'topics' => [$email['topics'][0]],
|
||||
'subject' => 'Khali beats Undertaker',
|
||||
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||
],
|
||||
|
@ -916,6 +821,7 @@ class MessagingTest extends Scope
|
|||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'sms',
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
|
@ -988,117 +894,13 @@ class MessagingTest extends Scope
|
|||
*/
|
||||
public function testUpdateSMS(array $sms)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) {
|
||||
$this->markTestSkipped('SMS DSN not provided');
|
||||
}
|
||||
|
||||
$smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'));
|
||||
$to = $smsDSN->getParam('to');
|
||||
$from = $smsDSN->getParam('from');
|
||||
$authKey = $smsDSN->getPassword();
|
||||
$senderId = $smsDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($senderId) || empty($authKey)) {
|
||||
$this->markTestSkipped('SMS provider not configured');
|
||||
}
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_MSG91_PROVIDER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Msg91-2',
|
||||
'senderId' => $senderId,
|
||||
'authKey' => $authKey,
|
||||
'from' => $from,
|
||||
],
|
||||
];
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $provider['headers']['status-code']);
|
||||
|
||||
$providerId = $provider['body']['data']['messagingCreateMsg91Provider']['_id'];
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_TOPIC);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Active users',
|
||||
],
|
||||
];
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $topic['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'userId' => ID::unique(),
|
||||
'email' => 'random4-email@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]
|
||||
];
|
||||
$user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $user['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER_TARGET);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
],
|
||||
];
|
||||
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $target['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_SUBSCRIBER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'subscriberId' => ID::unique(),
|
||||
'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'],
|
||||
'targetId' => $target['body']['data']['usersCreateTarget']['_id'],
|
||||
],
|
||||
];
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $subscriber['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_SMS);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']],
|
||||
'topics' => [$sms['topics'][0]],
|
||||
'content' => '345463',
|
||||
],
|
||||
];
|
||||
|
@ -1219,6 +1021,7 @@ class MessagingTest extends Scope
|
|||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'push',
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
|
@ -1293,112 +1096,13 @@ class MessagingTest extends Scope
|
|||
*/
|
||||
public function testUpdatePushNotification(array $push)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) {
|
||||
$this->markTestSkipped('Push DSN empty');
|
||||
}
|
||||
|
||||
$pushDSN = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'));
|
||||
$to = $pushDSN->getParam('to');
|
||||
$serverKey = $pushDSN->getPassword();
|
||||
|
||||
if (empty($to) || empty($serverKey)) {
|
||||
$this->markTestSkipped('Push provider not configured');
|
||||
}
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_FCM_PROVIDER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'FCM2',
|
||||
'serverKey' => $serverKey,
|
||||
],
|
||||
];
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $provider['headers']['status-code']);
|
||||
$providerId = $provider['body']['data']['messagingCreateFcmProvider']['_id'];
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_TOPIC);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Active users',
|
||||
],
|
||||
];
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $topic['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'userId' => ID::unique(),
|
||||
'email' => 'random5-email@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]
|
||||
];
|
||||
$user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $user['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_USER_TARGET);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'userId' => $user['body']['data']['usersCreate']['_id'],
|
||||
'providerId' => $providerId,
|
||||
'identifier' => $to,
|
||||
],
|
||||
];
|
||||
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $target['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_SUBSCRIBER);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'subscriberId' => ID::unique(),
|
||||
'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'],
|
||||
'targetId' => $target['body']['data']['usersCreateTarget']['_id'],
|
||||
],
|
||||
];
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $subscriber['headers']['status-code']);
|
||||
|
||||
$query = $this->getQuery(self::$CREATE_PUSH_NOTIFICATION);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']],
|
||||
'topics' => [$push['topics'][0]],
|
||||
'title' => 'Push Notification Title',
|
||||
'body' => 'Push Notifiaction Body',
|
||||
],
|
||||
|
|
|
@ -78,6 +78,7 @@ class UsersTest extends Scope
|
|||
'variables' => [
|
||||
'targetId' => ID::unique(),
|
||||
'userId' => $user['_id'],
|
||||
'providerType' => 'email',
|
||||
'providerId' => $providerId,
|
||||
'identifier' => 'identifier',
|
||||
]
|
||||
|
@ -479,7 +480,7 @@ class UsersTest extends Scope
|
|||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $target['headers']['status-code']);
|
||||
$this->assertEquals('newidentifier', $target['body']['data']['usersUpdateTargetIdentifier']['identifier']);
|
||||
$this->assertEquals('newidentifier', $target['body']['data']['usersUpdateTarget']['identifier']);
|
||||
}
|
||||
|
||||
public function testDeleteUserSessions()
|
||||
|
|
|
@ -138,6 +138,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/webhooks?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -155,6 +164,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/logs?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -172,6 +190,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/certificates?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -189,6 +216,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/functions?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -206,6 +242,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/builds?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -225,6 +270,18 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/databases', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'database_db_main',
|
||||
'threshold' => '0'
|
||||
]);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -242,6 +299,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/deletes?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -259,6 +325,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/mails?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -276,6 +351,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/messaging?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -293,6 +377,15 @@ class HealthCustomServerTest extends Scope
|
|||
$this->assertIsInt($response['body']['size']);
|
||||
$this->assertLessThan(100, $response['body']['size']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health/queue/migrations?threshold=0', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
$this->assertEquals(500, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -317,9 +317,11 @@ trait MessagingBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'email',
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => 'my-token',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $target['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['$id'] . '/subscribers', \array_merge([
|
||||
|
@ -329,13 +331,16 @@ trait MessagingBase
|
|||
'subscriberId' => ID::unique(),
|
||||
'targetId' => $target['body']['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertEquals($target['body']['userId'], $response['body']['userId']);
|
||||
|
||||
$topic = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $topic['headers']['status-code']);
|
||||
$this->assertEquals('android-app', $topic['body']['name']);
|
||||
$this->assertEquals('updated-description', $topic['body']['description']);
|
||||
|
@ -344,6 +349,7 @@ trait MessagingBase
|
|||
return [
|
||||
'topicId' => $topic['body']['$id'],
|
||||
'targetId' => $target['body']['$id'],
|
||||
'userId' => $target['body']['userId'],
|
||||
'subscriberId' => $response['body']['$id']
|
||||
];
|
||||
}
|
||||
|
@ -353,14 +359,16 @@ trait MessagingBase
|
|||
*/
|
||||
public function testGetSubscriber(array $data)
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $data['topicId'] . '/subscriber/' . $data['subscriberId'], \array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $data['topicId'] . '/subscribers/' . $data['subscriberId'], \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($data['topicId'], $response['body']['topicId']);
|
||||
$this->assertEquals($data['targetId'], $response['body']['targetId']);
|
||||
$this->assertEquals($data['userId'], $response['body']['userId']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,6 +384,7 @@ trait MessagingBase
|
|||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['total']);
|
||||
$this->assertEquals($data['userId'], $response['body']['subscribers'][0]['userId']);
|
||||
$this->assertEquals(\count($response['body']['subscribers']), $response['body']['total']);
|
||||
|
||||
return $data;
|
||||
|
@ -496,7 +505,7 @@ trait MessagingBase
|
|||
*/
|
||||
public function testDeleteSubscriber(array $data)
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/messaging/topics/' . $data['topicId'] . '/subscriber/' . $data['subscriberId'], \array_merge([
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/messaging/topics/' . $data['topicId'] . '/subscribers/' . $data['subscriberId'], \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
@ -558,6 +567,7 @@ trait MessagingBase
|
|||
'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN),
|
||||
'from' => $from
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
|
@ -570,6 +580,7 @@ trait MessagingBase
|
|||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
|
@ -593,6 +604,7 @@ trait MessagingBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'email',
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
@ -645,21 +657,6 @@ trait MessagingBase
|
|||
*/
|
||||
public function testUpdateEmail(array $email)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) {
|
||||
$this->markTestSkipped('Email DSN not provided');
|
||||
}
|
||||
|
||||
$emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'));
|
||||
$to = $emailDSN->getParam('to');
|
||||
$from = $emailDSN->getParam('from');
|
||||
$isEuRegion = $emailDSN->getParam('isEuRegion');
|
||||
$apiKey = $emailDSN->getPassword();
|
||||
$domain = $emailDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($apiKey) || empty($domain) || empty($isEuRegion)) {
|
||||
$this->markTestSkipped('Email provider not configured');
|
||||
}
|
||||
|
||||
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -669,71 +666,6 @@ trait MessagingBase
|
|||
// Test failure as the message has already been sent.
|
||||
$this->assertEquals(400, $message['headers']['status-code']);
|
||||
|
||||
// Create provider
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/mailgun', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Mailgun-provider-2',
|
||||
'apiKey' => $apiKey,
|
||||
'domain' => $domain,
|
||||
'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN),
|
||||
'from' => $from
|
||||
]);
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
$user = $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' => 'random-email@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $user['headers']['status-code']);
|
||||
|
||||
// Create Target
|
||||
$target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $target['headers']['status-code']);
|
||||
|
||||
// Create Subscriber
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'subscriberId' => ID::unique(),
|
||||
'targetId' => $target['body']['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $subscriber['headers']['status-code']);
|
||||
|
||||
// Create Email
|
||||
$email = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -742,7 +674,7 @@ trait MessagingBase
|
|||
], [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['$id']],
|
||||
'topics' => [$email['body']['topics'][0]],
|
||||
'subject' => 'Khali beats Undertaker',
|
||||
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||
]);
|
||||
|
@ -801,6 +733,7 @@ trait MessagingBase
|
|||
'authKey' => $authKey,
|
||||
'from' => $from
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
|
@ -813,6 +746,7 @@ trait MessagingBase
|
|||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
|
@ -836,6 +770,7 @@ trait MessagingBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'sms',
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
@ -887,20 +822,6 @@ trait MessagingBase
|
|||
*/
|
||||
public function testUpdateSMS(array $sms)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) {
|
||||
$this->markTestSkipped('SMS DSN not provided');
|
||||
}
|
||||
|
||||
$smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'));
|
||||
$to = $smsDSN->getParam('to');
|
||||
$from = $smsDSN->getParam('from');
|
||||
$authKey = $smsDSN->getPassword();
|
||||
$senderId = $smsDSN->getUser();
|
||||
|
||||
if (empty($to) || empty($from) || empty($senderId) || empty($authKey)) {
|
||||
$this->markTestSkipped('SMS provider not configured');
|
||||
}
|
||||
|
||||
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/sms/' . $sms['body']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -910,70 +831,6 @@ trait MessagingBase
|
|||
// Test failure as the message has already been sent.
|
||||
$this->assertEquals(400, $message['headers']['status-code']);
|
||||
|
||||
// Create provider
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/msg91', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Msg91-2',
|
||||
'senderId' => $senderId,
|
||||
'authKey' => $authKey,
|
||||
'from' => $from
|
||||
]);
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
$user = $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' => 'random2-email@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $user['headers']['status-code']);
|
||||
|
||||
// Create Target
|
||||
$target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $target['headers']['status-code']);
|
||||
|
||||
// Create Subscriber
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'subscriberId' => ID::unique(),
|
||||
'targetId' => $target['body']['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $subscriber['headers']['status-code']);
|
||||
|
||||
// Create SMS
|
||||
$sms = $this->client->call(Client::METHOD_POST, '/messaging/messages/sms', [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -982,7 +839,7 @@ trait MessagingBase
|
|||
], [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['$id']],
|
||||
'topics' => [$sms['body']['topics'][0]],
|
||||
'content' => '047487',
|
||||
]);
|
||||
|
||||
|
@ -1036,6 +893,7 @@ trait MessagingBase
|
|||
'name' => 'FCM-1',
|
||||
'serverKey' => $serverKey,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
|
@ -1048,6 +906,7 @@ trait MessagingBase
|
|||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
|
@ -1071,6 +930,7 @@ trait MessagingBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerType' => 'push',
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
@ -1123,18 +983,6 @@ trait MessagingBase
|
|||
*/
|
||||
public function testUpdatePushNotification(array $push)
|
||||
{
|
||||
if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) {
|
||||
$this->markTestSkipped('Push DSN empty');
|
||||
}
|
||||
|
||||
$pushDSN = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'));
|
||||
$to = $pushDSN->getParam('to');
|
||||
$serverKey = $pushDSN->getPassword();
|
||||
|
||||
if (empty($to) || empty($serverKey)) {
|
||||
$this->markTestSkipped('Push provider not configured');
|
||||
}
|
||||
|
||||
$message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/push/' . $push['body']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -1144,68 +992,6 @@ trait MessagingBase
|
|||
// Test failure as the message has already been sent.
|
||||
$this->assertEquals(400, $message['headers']['status-code']);
|
||||
|
||||
// Create provider
|
||||
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/fcm', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'FCM-2',
|
||||
'serverKey' => $serverKey,
|
||||
]);
|
||||
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||
|
||||
// Create Topic
|
||||
$topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'topicId' => ID::unique(),
|
||||
'name' => 'topic1',
|
||||
'description' => 'Test Topic'
|
||||
]);
|
||||
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||
|
||||
// Create User
|
||||
$user = $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' => 'random4-email@mail.org',
|
||||
'password' => 'password',
|
||||
'name' => 'Messaging User',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $user['headers']['status-code']);
|
||||
|
||||
// Create Target
|
||||
$target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'targetId' => ID::unique(),
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'identifier' => $to,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $target['headers']['status-code']);
|
||||
|
||||
// Create Subscriber
|
||||
$subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'subscriberId' => ID::unique(),
|
||||
'targetId' => $target['body']['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $subscriber['headers']['status-code']);
|
||||
|
||||
// Create push notification
|
||||
$push = $this->client->call(Client::METHOD_POST, '/messaging/messages/push', [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -1214,7 +1000,7 @@ trait MessagingBase
|
|||
], [
|
||||
'messageId' => ID::unique(),
|
||||
'status' => 'draft',
|
||||
'topics' => [$topic['body']['$id']],
|
||||
'topics' => [$push['body']['topics'][0]],
|
||||
'title' => 'Test-Notification',
|
||||
'body' => 'Test-Notification-Body',
|
||||
]);
|
||||
|
|
|
@ -1232,7 +1232,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'providerId' => 'unique()',
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Sengrid1',
|
||||
'apiKey' => 'my-apikey',
|
||||
'from' => 'from@domain.com',
|
||||
|
@ -1244,6 +1244,7 @@ trait UsersBase
|
|||
], $this->getHeaders()), [
|
||||
'targetId' => ID::unique(),
|
||||
'providerId' => $provider['body']['$id'],
|
||||
'providerType' => 'email',
|
||||
'identifier' => 'my-token',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
@ -1257,7 +1258,7 @@ trait UsersBase
|
|||
*/
|
||||
public function testUpdateUserTarget(array $data): array
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'] . '/identifier', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
|
@ -1303,11 +1304,14 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(0, $response['body']['total']);
|
||||
}
|
||||
|
|
|
@ -636,6 +636,120 @@ class WebhooksCustomClientTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateAccountPrefs
|
||||
*/
|
||||
public function testCreateAccountVerification($data): array
|
||||
{
|
||||
$id = $data['id'] ?? '';
|
||||
$email = $data['email'] ?? '';
|
||||
$session = $data['session'] ?? '';
|
||||
|
||||
$verification = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'url' => 'http://localhost/verification',
|
||||
]);
|
||||
|
||||
$verificationId = $verification['body']['$id'];
|
||||
|
||||
$this->assertEquals(201, $verification['headers']['status-code']);
|
||||
$this->assertIsArray($verification['body']);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
$signatureKey = $this->getProject()['signatureKey'];
|
||||
$payload = json_encode($webhook['data']);
|
||||
$url = $webhook['url'];
|
||||
$signatureExpected = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
|
||||
|
||||
$this->assertEquals($webhook['method'], 'POST');
|
||||
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
|
||||
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
|
||||
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertNotEmpty($webhook['data']['userId']);
|
||||
$this->assertNotEmpty($webhook['data']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['expire']));
|
||||
|
||||
$data['secret'] = $webhook['data']['secret'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateAccountVerification
|
||||
*/
|
||||
public function testUpdateAccountVerification($data): array
|
||||
{
|
||||
$id = $data['id'] ?? '';
|
||||
$email = $data['email'] ?? '';
|
||||
$session = $data['session'] ?? '';
|
||||
$secret = $data['secret'] ?? '';
|
||||
|
||||
$verification = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => $id,
|
||||
'secret' => $secret,
|
||||
]);
|
||||
|
||||
$verificationId = $verification['body']['$id'];
|
||||
|
||||
$this->assertEquals(200, $verification['headers']['status-code']);
|
||||
$this->assertIsArray($verification['body']);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
$signatureKey = $this->getProject()['signatureKey'];
|
||||
$payload = json_encode($webhook['data']);
|
||||
$url = $webhook['url'];
|
||||
$signatureExpected = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
|
||||
|
||||
$this->assertEquals($webhook['method'], 'POST');
|
||||
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
|
||||
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
|
||||
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertNotEmpty($webhook['data']['userId']);
|
||||
$this->assertNotEmpty($webhook['data']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['expire']));
|
||||
|
||||
$data['secret'] = $webhook['data']['secret'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateAccountPrefs
|
||||
*/
|
||||
|
@ -751,120 +865,6 @@ class WebhooksCustomClientTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateAccountPrefs
|
||||
*/
|
||||
public function testCreateAccountVerification($data): array
|
||||
{
|
||||
$id = $data['id'] ?? '';
|
||||
$email = $data['email'] ?? '';
|
||||
$session = $data['session'] ?? '';
|
||||
|
||||
$verification = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'url' => 'http://localhost/verification',
|
||||
]);
|
||||
|
||||
$verificationId = $verification['body']['$id'];
|
||||
|
||||
$this->assertEquals(201, $verification['headers']['status-code']);
|
||||
$this->assertIsArray($verification['body']);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
$signatureKey = $this->getProject()['signatureKey'];
|
||||
$payload = json_encode($webhook['data']);
|
||||
$url = $webhook['url'];
|
||||
$signatureExpected = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
|
||||
|
||||
$this->assertEquals($webhook['method'], 'POST');
|
||||
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
|
||||
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
|
||||
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertNotEmpty($webhook['data']['userId']);
|
||||
$this->assertNotEmpty($webhook['data']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['expire']));
|
||||
|
||||
$data['secret'] = $webhook['data']['secret'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateAccountVerification
|
||||
*/
|
||||
public function testUpdateAccountVerification($data): array
|
||||
{
|
||||
$id = $data['id'] ?? '';
|
||||
$email = $data['email'] ?? '';
|
||||
$session = $data['session'] ?? '';
|
||||
$secret = $data['secret'] ?? '';
|
||||
|
||||
$verification = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'userId' => $id,
|
||||
'secret' => $secret,
|
||||
]);
|
||||
|
||||
$verificationId = $verification['body']['$id'];
|
||||
|
||||
$this->assertEquals(200, $verification['headers']['status-code']);
|
||||
$this->assertIsArray($verification['body']);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
$signatureKey = $this->getProject()['signatureKey'];
|
||||
$payload = json_encode($webhook['data']);
|
||||
$url = $webhook['url'];
|
||||
$signatureExpected = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
|
||||
|
||||
$this->assertEquals($webhook['method'], 'POST');
|
||||
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
|
||||
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
|
||||
$this->assertStringContainsString('users.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString('users.*.verification.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.*.verification.{$verificationId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertStringContainsString("users.{$id}.verification.{$verificationId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
|
||||
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
|
||||
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), ('server' === $this->getSide()));
|
||||
$this->assertNotEmpty($webhook['data']['$id']);
|
||||
$this->assertNotEmpty($webhook['data']['userId']);
|
||||
$this->assertNotEmpty($webhook['data']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($webhook['data']['expire']));
|
||||
|
||||
$data['secret'] = $webhook['data']['secret'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateTeamMembership
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue