Merge remote-tracking branch 'origin/1.5.x' into feat-improve-logging
This commit is contained in:
commit
0ba768125d
5
.env
5
.env
|
@ -87,7 +87,7 @@ _APP_LOGGING_PROVIDER=
|
||||||
_APP_LOGGING_CONFIG=
|
_APP_LOGGING_CONFIG=
|
||||||
_APP_GRAPHQL_MAX_BATCH_SIZE=10
|
_APP_GRAPHQL_MAX_BATCH_SIZE=10
|
||||||
_APP_GRAPHQL_MAX_COMPLEXITY=250
|
_APP_GRAPHQL_MAX_COMPLEXITY=250
|
||||||
_APP_GRAPHQL_MAX_DEPTH=3
|
_APP_GRAPHQL_MAX_DEPTH=4
|
||||||
_APP_DOCKER_HUB_USERNAME=
|
_APP_DOCKER_HUB_USERNAME=
|
||||||
_APP_DOCKER_HUB_PASSWORD=
|
_APP_DOCKER_HUB_PASSWORD=
|
||||||
_APP_VCS_GITHUB_APP_NAME=
|
_APP_VCS_GITHUB_APP_NAME=
|
||||||
|
@ -99,3 +99,6 @@ _APP_VCS_GITHUB_WEBHOOK_SECRET=
|
||||||
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
|
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
|
||||||
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
|
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
|
||||||
_APP_ASSISTANT_OPENAI_API_KEY=
|
_APP_ASSISTANT_OPENAI_API_KEY=
|
||||||
|
_APP_MESSAGE_SMS_TEST_DSN=
|
||||||
|
_APP_MESSAGE_EMAIL_TEST_DSN=
|
||||||
|
_APP_MESSAGE_PUSH_TEST_DSN=
|
||||||
|
|
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
|
@ -99,6 +99,7 @@ jobs:
|
||||||
Users,
|
Users,
|
||||||
Webhooks,
|
Webhooks,
|
||||||
VCS,
|
VCS,
|
||||||
|
Messaging,
|
||||||
]
|
]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ debug/
|
||||||
app/sdks
|
app/sdks
|
||||||
dev/yasd_init.php
|
dev/yasd_init.php
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
Makefile
|
||||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,4 +1,4 @@
|
||||||
[submodule "app/console"]
|
[submodule "app/console"]
|
||||||
path = app/console
|
path = app/console
|
||||||
url = https://github.com/appwrite/console
|
url = https://github.com/appwrite/console
|
||||||
branch = 3.2.9
|
branch = 3.2.15
|
||||||
|
|
|
@ -484,7 +484,7 @@
|
||||||
## Features
|
## Features
|
||||||
- Added Phone Authentication by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3357
|
- Added Phone Authentication by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3357
|
||||||
- Added Twilio Support
|
- Added Twilio Support
|
||||||
- Added TextMagic Support
|
- Added Textmagic Support
|
||||||
- Added Telesign Support
|
- Added Telesign Support
|
||||||
- Added Endpoint to create Phone Session (`POST:/v1/account/sessions/phone`)
|
- Added Endpoint to create Phone Session (`POST:/v1/account/sessions/phone`)
|
||||||
- Added Endpoint to confirm Phone Session (`PUT:/v1/account/sessions/phone`)
|
- Added Endpoint to confirm Phone Session (`PUT:/v1/account/sessions/phone`)
|
||||||
|
|
|
@ -94,7 +94,8 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||||
chmod +x /usr/local/bin/worker-mails && \
|
chmod +x /usr/local/bin/worker-mails && \
|
||||||
chmod +x /usr/local/bin/worker-messaging && \
|
chmod +x /usr/local/bin/worker-messaging && \
|
||||||
chmod +x /usr/local/bin/worker-webhooks && \
|
chmod +x /usr/local/bin/worker-webhooks && \
|
||||||
chmod +x /usr/local/bin/worker-migrations
|
chmod +x /usr/local/bin/worker-migrations && \
|
||||||
|
chmod +x /usr/local/bin/worker-hamster
|
||||||
|
|
||||||
# Cloud Executabless
|
# Cloud Executabless
|
||||||
RUN chmod +x /usr/local/bin/hamster && \
|
RUN chmod +x /usr/local/bin/hamster && \
|
||||||
|
|
|
@ -6,6 +6,7 @@ require_once __DIR__ . '/controllers/general.php';
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Certificate;
|
use Appwrite\Event\Certificate;
|
||||||
use Appwrite\Event\Func;
|
use Appwrite\Event\Func;
|
||||||
|
use Appwrite\Event\Hamster;
|
||||||
use Appwrite\Platform\Appwrite;
|
use Appwrite\Platform\Appwrite;
|
||||||
use Utopia\CLI\CLI;
|
use Utopia\CLI\CLI;
|
||||||
use Utopia\Database\Validator\Authorization;
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
@ -154,6 +155,9 @@ CLI::setResource('queue', function (Group $pools) {
|
||||||
CLI::setResource('queueForFunctions', function (Connection $queue) {
|
CLI::setResource('queueForFunctions', function (Connection $queue) {
|
||||||
return new Func($queue);
|
return new Func($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
|
CLI::setResource('queueForHamster', function (Connection $queue) {
|
||||||
|
return new Hamster($queue);
|
||||||
|
}, ['queue']);
|
||||||
CLI::setResource('queueForDeletes', function (Connection $queue) {
|
CLI::setResource('queueForDeletes', function (Connection $queue) {
|
||||||
return new Delete($queue);
|
return new Delete($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
|
|
||||||
$providers = Config::getParam('providers', []);
|
$providers = Config::getParam('oAuthProviders', []);
|
||||||
$auth = Config::getParam('auth', []);
|
$auth = Config::getParam('auth', []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -221,6 +221,17 @@ $commonCollections = [
|
||||||
'array' => false,
|
'array' => false,
|
||||||
'filters' => ['subQueryMemberships'],
|
'filters' => ['subQueryMemberships'],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('targets'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['subQueryTargets'],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'$id' => ID::custom('search'),
|
'$id' => ID::custom('search'),
|
||||||
'type' => Database::VAR_STRING,
|
'type' => Database::VAR_STRING,
|
||||||
|
@ -1366,6 +1377,607 @@ $commonCollections = [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'providers' => [
|
||||||
|
'$collection' => ID::custom(DATABASE::METADATA),
|
||||||
|
'$id' => ID::custom('providers'),
|
||||||
|
'name' => 'Providers',
|
||||||
|
'attributes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('name'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 128,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('provider'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('type'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 128,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('enabled'),
|
||||||
|
'type' => Database::VAR_BOOLEAN,
|
||||||
|
'signed' => true,
|
||||||
|
'size' => 0,
|
||||||
|
'format' => '',
|
||||||
|
'filters' => [],
|
||||||
|
'required' => true,
|
||||||
|
'default' => true,
|
||||||
|
'array' => false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('credentials'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['json', 'encrypt'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('options'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => [],
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['json'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('search'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 65535,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['providerSearch'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'indexes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_provider'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['provider'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_name'),
|
||||||
|
'type' => Database::INDEX_FULLTEXT,
|
||||||
|
'attributes' => ['name'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_type'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['type'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_enabled_type'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['enabled','type'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_search'),
|
||||||
|
'type' => Database::INDEX_FULLTEXT,
|
||||||
|
'attributes' => ['search'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'messages' => [
|
||||||
|
'$collection' => ID::custom(DATABASE::METADATA),
|
||||||
|
'$id' => ID::custom('messages'),
|
||||||
|
'name' => 'Messages',
|
||||||
|
'attributes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('providerType'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('description'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 256,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('status'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => 'processing',
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('data'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 65535,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['json'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('topics'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 21845,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => [],
|
||||||
|
'array' => true,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('users'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 21845,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => [],
|
||||||
|
'array' => true,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('targets'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 21845,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => [],
|
||||||
|
'array' => true,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('scheduledAt'),
|
||||||
|
'type' => Database::VAR_DATETIME,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => false,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['datetime'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('deliveredAt'),
|
||||||
|
'type' => Database::VAR_DATETIME,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => false,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['datetime'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('deliveryErrors'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 65535,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => true,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('deliveredTotal'),
|
||||||
|
'type' => Database::VAR_INTEGER,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => 0,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('search'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['messageSearch'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'indexes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_search'),
|
||||||
|
'type' => Database::INDEX_FULLTEXT,
|
||||||
|
'attributes' => ['search'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'topics' => [
|
||||||
|
'$collection' => ID::custom(DATABASE::METADATA),
|
||||||
|
'$id' => ID::custom('topics'),
|
||||||
|
'name' => 'Topics',
|
||||||
|
'attributes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('name'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 128,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('description'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 2048,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('total'),
|
||||||
|
'type' => Database::VAR_INTEGER,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => 0,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('targets'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['subQueryTopicTargets'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('search'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 16384,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'array' => false,
|
||||||
|
'filters' => ['topicSearch'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'indexes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_name'),
|
||||||
|
'type' => Database::INDEX_FULLTEXT,
|
||||||
|
'attributes' => ['name'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_search'),
|
||||||
|
'type' => Database::INDEX_FULLTEXT,
|
||||||
|
'attributes' => ['search'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'subscribers' => [
|
||||||
|
'$collection' => ID::custom(DATABASE::METADATA),
|
||||||
|
'$id' => ID::custom('subscribers'),
|
||||||
|
'name' => 'Subscribers',
|
||||||
|
'attributes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('targetId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('targetInternalId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$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('topicId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('topicInternalId'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('providerType'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => 128,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'indexes' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_targetId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['targetId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_targetInternalId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['targetInternalId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$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_topicId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['topicId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_topicInternalId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['topicInternalId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'targets' => [
|
||||||
|
'$collection' => ID::custom(DATABASE::METADATA),
|
||||||
|
'$id' => ID::custom('targets'),
|
||||||
|
'name' => 'Targets',
|
||||||
|
'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('providerType'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'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' => false,
|
||||||
|
'default' => null,
|
||||||
|
'array' => false,
|
||||||
|
'filters' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('identifier'),
|
||||||
|
'type' => Database::VAR_STRING,
|
||||||
|
'format' => '',
|
||||||
|
'size' => Database::LENGTH_KEY,
|
||||||
|
'signed' => true,
|
||||||
|
'required' => true,
|
||||||
|
'default' => null,
|
||||||
|
'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' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_userId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['userId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_userInternalId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['userInternalId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [Database::ORDER_ASC],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_providerId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['providerId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_providerInternalId'),
|
||||||
|
'type' => Database::INDEX_KEY,
|
||||||
|
'attributes' => ['providerInternalId'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::custom('_key_identifier'),
|
||||||
|
'type' => Database::INDEX_UNIQUE,
|
||||||
|
'attributes' => ['identifier'],
|
||||||
|
'lengths' => [],
|
||||||
|
'orders' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$projectCollections = array_merge([
|
$projectCollections = array_merge([
|
||||||
|
@ -3412,7 +4024,7 @@ $consoleCollections = array_merge([
|
||||||
'filters' => ['json'],
|
'filters' => ['json'],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'$id' => ID::custom('authProviders'),
|
'$id' => ID::custom('oAuthProviders'),
|
||||||
'type' => Database::VAR_STRING,
|
'type' => Database::VAR_STRING,
|
||||||
'format' => '',
|
'format' => '',
|
||||||
'size' => 16384,
|
'size' => 16384,
|
||||||
|
|
|
@ -103,6 +103,16 @@ return [
|
||||||
'description' => 'This method was not fully implemented yet. If you believe this is a mistake, please upgrade your Appwrite server version.',
|
'description' => 'This method was not fully implemented yet. If you believe this is a mistake, please upgrade your Appwrite server version.',
|
||||||
'code' => 405,
|
'code' => 405,
|
||||||
],
|
],
|
||||||
|
Exception::GENERAL_INVALID_EMAIL => [
|
||||||
|
'name' => Exception::GENERAL_INVALID_EMAIL,
|
||||||
|
'description' => 'Value must be a valid email address.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::GENERAL_INVALID_PHONE => [
|
||||||
|
'name' => Exception::GENERAL_INVALID_PHONE,
|
||||||
|
'description' => 'Value must be a valid phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
|
||||||
/** User Errors */
|
/** User Errors */
|
||||||
Exception::USER_COUNT_EXCEEDED => [
|
Exception::USER_COUNT_EXCEEDED => [
|
||||||
|
@ -251,6 +261,16 @@ return [
|
||||||
'description' => 'User phone is already verified',
|
'description' => 'User phone is already verified',
|
||||||
'code' => 409
|
'code' => 409
|
||||||
],
|
],
|
||||||
|
Exception::USER_TARGET_NOT_FOUND => [
|
||||||
|
'name' => Exception::USER_TARGET_NOT_FOUND,
|
||||||
|
'description' => 'The target could not be found.',
|
||||||
|
'code' => 404,
|
||||||
|
],
|
||||||
|
Exception::USER_TARGET_ALREADY_EXISTS => [
|
||||||
|
'name' => Exception::USER_TARGET_ALREADY_EXISTS,
|
||||||
|
'description' => 'A target with the same ID already exists.',
|
||||||
|
'code' => 409,
|
||||||
|
],
|
||||||
|
|
||||||
/** Teams */
|
/** Teams */
|
||||||
Exception::TEAM_NOT_FOUND => [
|
Exception::TEAM_NOT_FOUND => [
|
||||||
|
@ -773,4 +793,83 @@ return [
|
||||||
'code' => 503,
|
'code' => 503,
|
||||||
'publish' => false
|
'publish' => false
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/** Providers */
|
||||||
|
Exception::PROVIDER_NOT_FOUND => [
|
||||||
|
'name' => Exception::PROVIDER_NOT_FOUND,
|
||||||
|
'description' => 'Provider with the requested ID could not be found.',
|
||||||
|
'code' => 404,
|
||||||
|
],
|
||||||
|
Exception::PROVIDER_ALREADY_EXISTS => [
|
||||||
|
'name' => Exception::PROVIDER_ALREADY_EXISTS,
|
||||||
|
'description' => 'Provider with the requested ID already exists.',
|
||||||
|
'code' => 409,
|
||||||
|
],
|
||||||
|
Exception::PROVIDER_INCORRECT_TYPE => [
|
||||||
|
'name' => Exception::PROVIDER_INCORRECT_TYPE,
|
||||||
|
'description' => 'Provider with the requested ID is of incorrect type: ',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
|
||||||
|
/** Topics */
|
||||||
|
Exception::TOPIC_NOT_FOUND => [
|
||||||
|
'name' => Exception::TOPIC_NOT_FOUND,
|
||||||
|
'description' => 'Topic with the request ID could not be found.',
|
||||||
|
'code' => 404,
|
||||||
|
],
|
||||||
|
Exception::TOPIC_ALREADY_EXISTS => [
|
||||||
|
'name' => Exception::TOPIC_ALREADY_EXISTS,
|
||||||
|
'description' => 'Topic with the request ID already exists.',
|
||||||
|
'code' => 409,
|
||||||
|
],
|
||||||
|
|
||||||
|
/** Subscribers */
|
||||||
|
Exception::SUBSCRIBER_NOT_FOUND => [
|
||||||
|
'name' => Exception::SUBSCRIBER_NOT_FOUND,
|
||||||
|
'description' => 'Subscriber with the request ID could not be found.',
|
||||||
|
'code' => 404,
|
||||||
|
],
|
||||||
|
Exception::SUBSCRIBER_ALREADY_EXISTS => [
|
||||||
|
'name' => Exception::SUBSCRIBER_ALREADY_EXISTS,
|
||||||
|
'description' => 'Subscriber with the request ID already exists.',
|
||||||
|
'code' => 409,
|
||||||
|
],
|
||||||
|
|
||||||
|
/** Messages */
|
||||||
|
Exception::MESSAGE_NOT_FOUND => [
|
||||||
|
'name' => Exception::MESSAGE_NOT_FOUND,
|
||||||
|
'description' => 'Message with the requested ID could not be found.',
|
||||||
|
'code' => 404,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_MISSING_TARGET => [
|
||||||
|
'name' => Exception::MESSAGE_MISSING_TARGET,
|
||||||
|
'description' => 'Message with the requested ID is missing a target (Topics or Users or Targets).',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_ALREADY_SENT => [
|
||||||
|
'name' => Exception::MESSAGE_ALREADY_SENT,
|
||||||
|
'description' => 'Message with the requested ID has already been sent.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_ALREADY_SCHEDULED => [
|
||||||
|
'name' => Exception::MESSAGE_ALREADY_SCHEDULED,
|
||||||
|
'description' => 'Message with the requested ID has already been scheduled for delivery.',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_TARGET_NOT_EMAIL => [
|
||||||
|
'name' => Exception::MESSAGE_TARGET_NOT_EMAIL,
|
||||||
|
'description' => 'Message with the target ID is not an email target:',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_TARGET_NOT_SMS => [
|
||||||
|
'name' => Exception::MESSAGE_TARGET_NOT_SMS,
|
||||||
|
'description' => 'Message with the target ID is not an SMS target:',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
Exception::MESSAGE_TARGET_NOT_PUSH => [
|
||||||
|
'name' => Exception::MESSAGE_TARGET_NOT_PUSH,
|
||||||
|
'description' => 'Message with the target ID is not a push target:',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -44,6 +44,20 @@ return [
|
||||||
'$description' => 'This event triggers when a verification token for a user is validated.'
|
'$description' => 'This event triggers when a verification token for a user is validated.'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'targets' => [
|
||||||
|
'$model' => Response::MODEL_TARGET,
|
||||||
|
'$resource' => true,
|
||||||
|
'$description' => 'This event triggers on any user\'s target event.',
|
||||||
|
'create' => [
|
||||||
|
'$description' => 'This event triggers when a user\'s target is created.',
|
||||||
|
],
|
||||||
|
'update' => [
|
||||||
|
'$description' => 'This event triggers when a user\'s target is updated.',
|
||||||
|
],
|
||||||
|
'delete' => [
|
||||||
|
'$description' => 'This event triggers when a user\'s target is deleted.',
|
||||||
|
],
|
||||||
|
],
|
||||||
'create' => [
|
'create' => [
|
||||||
'$description' => 'This event triggers when a user is created.'
|
'$description' => 'This event triggers when a user is created.'
|
||||||
],
|
],
|
||||||
|
@ -237,6 +251,56 @@ return [
|
||||||
'$description' => 'This event triggers when a function is updated.',
|
'$description' => 'This event triggers when a function is updated.',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
'messages' => [
|
||||||
|
'$model' => Response::MODEL_MESSAGE,
|
||||||
|
'$resource' => true,
|
||||||
|
'$description' => 'This event triggers on any messaging event.',
|
||||||
|
'create' => [
|
||||||
|
'$description' => 'This event triggers when a message is created.',
|
||||||
|
],
|
||||||
|
'update' => [
|
||||||
|
'$description' => 'This event triggers when a message is updated.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'topics' => [
|
||||||
|
'$model' => Response::MODEL_TOPIC,
|
||||||
|
'$resource' => true,
|
||||||
|
'$description' => 'This event triggers on any topic event.',
|
||||||
|
'create' => [
|
||||||
|
'$description' => 'This event triggers when a topic is created.',
|
||||||
|
],
|
||||||
|
'update' => [
|
||||||
|
'$description' => 'This event triggers when a topic is updated.',
|
||||||
|
],
|
||||||
|
'delete' => [
|
||||||
|
'$description' => 'This event triggers when a topic is deleted.'
|
||||||
|
],
|
||||||
|
'subscribers' => [
|
||||||
|
'$model' => Response::MODEL_SUBSCRIBER,
|
||||||
|
'$resource' => true,
|
||||||
|
'$description' => 'This event triggers on any subscriber event.',
|
||||||
|
'create' => [
|
||||||
|
'$description' => 'This event triggers when a subscriber is created.',
|
||||||
|
],
|
||||||
|
'delete' => [
|
||||||
|
'$description' => 'This event triggers when a subscriber is deleted.'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'providers' => [
|
||||||
|
'$model' => Response::MODEL_PROVIDER,
|
||||||
|
'$resource' => true,
|
||||||
|
'$description' => 'This event triggers on any provider event.',
|
||||||
|
'create' => [
|
||||||
|
'$description' => 'This event triggers when a provider is created.',
|
||||||
|
],
|
||||||
|
'update' => [
|
||||||
|
'$description' => 'This event triggers when a provider is updated.',
|
||||||
|
],
|
||||||
|
'delete' => [
|
||||||
|
'$description' => 'This event triggers when a provider is deleted.'
|
||||||
|
],
|
||||||
|
],
|
||||||
'rules' => [
|
'rules' => [
|
||||||
'$model' => Response::MODEL_PROXY_RULE,
|
'$model' => Response::MODEL_PROXY_RULE,
|
||||||
'$resource' => true,
|
'$resource' => true,
|
||||||
|
|
|
@ -15,7 +15,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'web',
|
'key' => 'web',
|
||||||
'name' => 'Web',
|
'name' => 'Web',
|
||||||
'version' => '13.0.0',
|
'version' => '13.0.1',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -63,7 +63,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'flutter',
|
'key' => 'flutter',
|
||||||
'name' => 'Flutter',
|
'name' => 'Flutter',
|
||||||
'version' => '11.0.0',
|
'version' => '11.0.1',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||||
'package' => 'https://pub.dev/packages/appwrite',
|
'package' => 'https://pub.dev/packages/appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -81,7 +81,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'apple',
|
'key' => 'apple',
|
||||||
'name' => 'Apple',
|
'name' => 'Apple',
|
||||||
'version' => '4.0.1',
|
'version' => '4.0.2',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -116,7 +116,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'android',
|
'key' => 'android',
|
||||||
'name' => 'Android',
|
'name' => 'Android',
|
||||||
'version' => '4.0.0',
|
'version' => '4.0.1',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -203,7 +203,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'cli',
|
'key' => 'cli',
|
||||||
'name' => 'Command Line',
|
'name' => 'Command Line',
|
||||||
'version' => '4.1.0',
|
'version' => '4.2.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -231,7 +231,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'nodejs',
|
'key' => 'nodejs',
|
||||||
'name' => 'Node.js',
|
'name' => 'Node.js',
|
||||||
'version' => '11.0.0',
|
'version' => '11.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -249,7 +249,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'deno',
|
'key' => 'deno',
|
||||||
'name' => 'Deno',
|
'name' => 'Deno',
|
||||||
'version' => '9.0.0',
|
'version' => '9.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||||
'package' => 'https://deno.land/x/appwrite',
|
'package' => 'https://deno.land/x/appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -267,7 +267,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'php',
|
'key' => 'php',
|
||||||
'name' => 'PHP',
|
'name' => 'PHP',
|
||||||
'version' => '10.0.0',
|
'version' => '10.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -285,7 +285,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'python',
|
'key' => 'python',
|
||||||
'name' => 'Python',
|
'name' => 'Python',
|
||||||
'version' => '4.0.0',
|
'version' => '4.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||||
'package' => 'https://pypi.org/project/appwrite/',
|
'package' => 'https://pypi.org/project/appwrite/',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -303,7 +303,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'ruby',
|
'key' => 'ruby',
|
||||||
'name' => 'Ruby',
|
'name' => 'Ruby',
|
||||||
'version' => '10.0.0',
|
'version' => '10.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||||
'package' => 'https://rubygems.org/gems/appwrite',
|
'package' => 'https://rubygems.org/gems/appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -321,7 +321,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'go',
|
'key' => 'go',
|
||||||
'name' => 'Go',
|
'name' => 'Go',
|
||||||
'version' => '3.0.0',
|
'version' => '3.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||||
'package' => '',
|
'package' => '',
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
@ -339,7 +339,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'java',
|
'key' => 'java',
|
||||||
'name' => 'Java',
|
'name' => 'Java',
|
||||||
'version' => '3.0.0',
|
'version' => '3.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-java',
|
'url' => 'https://github.com/appwrite/sdk-for-java',
|
||||||
'package' => '',
|
'package' => '',
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
@ -357,7 +357,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'dotnet',
|
'key' => 'dotnet',
|
||||||
'name' => '.NET',
|
'name' => '.NET',
|
||||||
'version' => '0.6.0',
|
'version' => '0.7.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -375,7 +375,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'dart',
|
'key' => 'dart',
|
||||||
'name' => 'Dart',
|
'name' => 'Dart',
|
||||||
'version' => '10.0.0',
|
'version' => '10.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -393,7 +393,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'kotlin',
|
'key' => 'kotlin',
|
||||||
'name' => 'Kotlin',
|
'name' => 'Kotlin',
|
||||||
'version' => '4.0.0',
|
'version' => '4.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
@ -415,7 +415,7 @@ return [
|
||||||
[
|
[
|
||||||
'key' => 'swift',
|
'key' => 'swift',
|
||||||
'name' => 'Swift',
|
'name' => 'Swift',
|
||||||
'version' => '4.0.1',
|
'version' => '4.1.0',
|
||||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
|
|
@ -21,6 +21,10 @@ $member = [
|
||||||
'avatars.read',
|
'avatars.read',
|
||||||
'execution.read',
|
'execution.read',
|
||||||
'execution.write',
|
'execution.write',
|
||||||
|
'targets.read',
|
||||||
|
'targets.write',
|
||||||
|
'subscribers.write',
|
||||||
|
'subscribers.read',
|
||||||
'assistant.read',
|
'assistant.read',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -60,6 +64,16 @@ $admins = [
|
||||||
'migrations.write',
|
'migrations.write',
|
||||||
'vcs.read',
|
'vcs.read',
|
||||||
'vcs.write',
|
'vcs.write',
|
||||||
|
'targets.read',
|
||||||
|
'targets.write',
|
||||||
|
'providers.write',
|
||||||
|
'providers.read',
|
||||||
|
'messages.write',
|
||||||
|
'messages.read',
|
||||||
|
'topics.write',
|
||||||
|
'topics.read',
|
||||||
|
'subscribers.write',
|
||||||
|
'subscribers.read'
|
||||||
];
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -76,6 +76,36 @@ return [ // List of publicly visible scopes
|
||||||
'health.read' => [
|
'health.read' => [
|
||||||
'description' => 'Access to read your project\'s health status',
|
'description' => 'Access to read your project\'s health status',
|
||||||
],
|
],
|
||||||
|
'providers.read' => [
|
||||||
|
'description' => 'Access to read your project\'s providers',
|
||||||
|
],
|
||||||
|
'providers.write' => [
|
||||||
|
'description' => 'Access to create, update, and delete your project\'s providers',
|
||||||
|
],
|
||||||
|
'messages.read' => [
|
||||||
|
'description' => 'Access to read your project\'s messages',
|
||||||
|
],
|
||||||
|
'messages.write' => [
|
||||||
|
'description' => 'Access to create, update, and delete your project\'s messages',
|
||||||
|
],
|
||||||
|
'topics.read' => [
|
||||||
|
'description' => 'Access to read your project\'s topics',
|
||||||
|
],
|
||||||
|
'topics.write' => [
|
||||||
|
'description' => 'Access to create, update, and delete your project\'s topics',
|
||||||
|
],
|
||||||
|
'subscribers.read' => [
|
||||||
|
'description' => 'Access to read your project\'s subscribers',
|
||||||
|
],
|
||||||
|
'subscribers.write' => [
|
||||||
|
'description' => 'Access to create, update, and delete your project\'s subscribers',
|
||||||
|
],
|
||||||
|
'targets.read' => [
|
||||||
|
'description' => 'Access to read your project\'s targets',
|
||||||
|
],
|
||||||
|
'targets.write' => [
|
||||||
|
'description' => 'Access to create, update, and delete your project\'s targets',
|
||||||
|
],
|
||||||
'rules.read' => [
|
'rules.read' => [
|
||||||
'description' => 'Access to read your project\'s proxy rules',
|
'description' => 'Access to read your project\'s proxy rules',
|
||||||
],
|
],
|
||||||
|
|
|
@ -251,4 +251,17 @@ return [
|
||||||
'optional' => false,
|
'optional' => false,
|
||||||
'icon' => '/images/services/migrations.png',
|
'icon' => '/images/services/migrations.png',
|
||||||
],
|
],
|
||||||
|
'messaging' => [
|
||||||
|
'key' => 'messaging',
|
||||||
|
'name' => 'Messaging',
|
||||||
|
'subtitle' => 'The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).',
|
||||||
|
'description' => '/docs/services/messaging.md',
|
||||||
|
'controller' => 'api/messaging.php',
|
||||||
|
'sdk' => true,
|
||||||
|
'docs' => true,
|
||||||
|
'docsUrl' => 'https://appwrite.io/docs/server/messaging',
|
||||||
|
'tests' => true,
|
||||||
|
'optional' => true,
|
||||||
|
'icon' => '/images/services/messaging.png',
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -440,7 +440,7 @@ return [
|
||||||
'variables' => [
|
'variables' => [
|
||||||
[
|
[
|
||||||
'name' => '_APP_SMS_PROVIDER',
|
'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',
|
'introduction' => '0.15.0',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 212b7429926d097a31ed71d2410e39c600c56f3b
|
Subproject commit 94e4c1a73024b0e974fbe6077674281f6e973c9d
|
|
@ -8,7 +8,6 @@ use Appwrite\Auth\Validator\Phone;
|
||||||
use Appwrite\Detector\Detector;
|
use Appwrite\Detector\Detector;
|
||||||
use Appwrite\Event\Event;
|
use Appwrite\Event\Event;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
use Appwrite\Event\Phone as EventPhone;
|
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Network\Validator\Email;
|
use Appwrite\Network\Validator\Email;
|
||||||
use Utopia\Validator\Host;
|
use Utopia\Validator\Host;
|
||||||
|
@ -45,6 +44,7 @@ use Utopia\Validator\WhiteList;
|
||||||
use Appwrite\Auth\Validator\PasswordHistory;
|
use Appwrite\Auth\Validator\PasswordHistory;
|
||||||
use Appwrite\Auth\Validator\PasswordDictionary;
|
use Appwrite\Auth\Validator\PasswordDictionary;
|
||||||
use Appwrite\Auth\Validator\PersonalData;
|
use Appwrite\Auth\Validator\PersonalData;
|
||||||
|
use Appwrite\Event\Messaging;
|
||||||
|
|
||||||
$oauthDefaultSuccess = '/auth/oauth2/success';
|
$oauthDefaultSuccess = '/auth/oauth2/success';
|
||||||
$oauthDefaultFailure = '/auth/oauth2/failure';
|
$oauthDefaultFailure = '/auth/oauth2/failure';
|
||||||
|
@ -148,7 +148,22 @@ App::post('/v1/account')
|
||||||
'accessedAt' => DateTime::now(),
|
'accessedAt' => DateTime::now(),
|
||||||
]);
|
]);
|
||||||
$user->removeAttribute('$internalId');
|
$user->removeAttribute('$internalId');
|
||||||
Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
|
||||||
|
try {
|
||||||
|
$target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'providerType' => MESSAGE_TYPE_EMAIL,
|
||||||
|
'identifier' => $email,
|
||||||
|
])));
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
|
||||||
|
} catch (Duplicate) {
|
||||||
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$email]),
|
||||||
|
]);
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate) {
|
} catch (Duplicate) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -299,7 +314,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||||
->label('sdk.methodType', 'webAuth')
|
->label('sdk.methodType', 'webAuth')
|
||||||
->label('abuse-limit', 50)
|
->label('abuse-limit', 50)
|
||||||
->label('abuse-key', 'ip:{ip}')
|
->label('abuse-key', 'ip:{ip}')
|
||||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), fn($node) => (!$node['mock'])))) . '.')
|
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn($node) => (!$node['mock'])))) . '.')
|
||||||
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||||
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||||
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
||||||
|
@ -311,14 +326,14 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||||
$protocol = $request->getProtocol();
|
$protocol = $request->getProtocol();
|
||||||
|
|
||||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||||
$providerEnabled = $project->getAttribute('authProviders', [])[$provider . 'Enabled'] ?? false;
|
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||||
|
|
||||||
if (!$providerEnabled) {
|
if (!$providerEnabled) {
|
||||||
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please enable the provider from your ' . APP_NAME . ' console to continue.');
|
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please enable the provider from your ' . APP_NAME . ' console to continue.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||||
|
|
||||||
if (!empty($appSecret) && isset($appSecret['version'])) {
|
if (!empty($appSecret) && isset($appSecret['version'])) {
|
||||||
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
|
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
|
||||||
|
@ -358,7 +373,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||||
->label('scope', 'public')
|
->label('scope', 'public')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 provider.')
|
||||||
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
||||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||||
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
||||||
|
@ -391,7 +406,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||||
->label('origin', '*')
|
->label('origin', '*')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 provider.')
|
||||||
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
||||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||||
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
||||||
|
@ -430,7 +445,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
->label('docs', false)
|
->label('docs', false)
|
||||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||||
->label('usage.params', ['provider:{request.provider}'])
|
->label('usage.params', ['provider:{request.provider}'])
|
||||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 provider.')
|
||||||
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
||||||
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
|
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
|
||||||
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
|
||||||
|
@ -448,9 +463,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||||
$validateURL = new URL();
|
$validateURL = new URL();
|
||||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||||
$providerEnabled = $project->getAttribute('authProviders', [])[$provider . 'Enabled'] ?? false;
|
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||||
|
|
||||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||||
|
|
||||||
|
@ -458,7 +473,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$providers = Config::getParam('providers');
|
$providers = Config::getParam('oAuthProviders');
|
||||||
$providerName = $providers[$provider]['name'] ?? '';
|
$providerName = $providers[$provider]['name'] ?? '';
|
||||||
|
|
||||||
/** @var Appwrite\Auth\OAuth2 $oauth2 */
|
/** @var Appwrite\Auth\OAuth2 $oauth2 */
|
||||||
|
@ -656,7 +671,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
'accessedAt' => DateTime::now(),
|
'accessedAt' => DateTime::now(),
|
||||||
]);
|
]);
|
||||||
$user->removeAttribute('$internalId');
|
$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' => MESSAGE_TYPE_EMAIL,
|
||||||
|
'identifier' => $email,
|
||||||
|
]));
|
||||||
} catch (Duplicate) {
|
} catch (Duplicate) {
|
||||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -1235,8 +1261,7 @@ App::post('/v1/account/sessions/phone')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging, Locale $locale) {
|
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) {
|
||||||
|
|
||||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||||
}
|
}
|
||||||
|
@ -1286,6 +1311,21 @@ App::post('/v1/account/sessions/phone')
|
||||||
|
|
||||||
$user->removeAttribute('$internalId');
|
$user->removeAttribute('$internalId');
|
||||||
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||||
|
try {
|
||||||
|
$target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'providerType' => MESSAGE_TYPE_SMS,
|
||||||
|
'identifier' => $phone,
|
||||||
|
])));
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
|
||||||
|
} catch (Duplicate) {
|
||||||
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$phone]),
|
||||||
|
]);
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
$secret = Auth::codeGenerator();
|
$secret = Auth::codeGenerator();
|
||||||
|
@ -1323,9 +1363,19 @@ App::post('/v1/account/sessions/phone')
|
||||||
$message = $message->setParam('{{token}}', $secret);
|
$message = $message->setParam('{{token}}', $secret);
|
||||||
$message = $message->render();
|
$message = $message->render();
|
||||||
|
|
||||||
|
|
||||||
|
$messageDoc = new Document([
|
||||||
|
'$id' => $token->getId(),
|
||||||
|
'data' => [
|
||||||
|
'content' => $message,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
$queueForMessaging
|
$queueForMessaging
|
||||||
->setRecipient($phone)
|
->setMessage($messageDoc)
|
||||||
->setMessage($message)
|
->setRecipients([$phone])
|
||||||
|
->setProviderType(MESSAGE_TYPE_SMS)
|
||||||
|
->setProject($project)
|
||||||
->trigger();
|
->trigger();
|
||||||
|
|
||||||
$queueForEvents->setPayload(
|
$queueForEvents->setPayload(
|
||||||
|
@ -1644,6 +1694,81 @@ App::post('/v1/account/jwt')
|
||||||
])]), Response::MODEL_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' => MESSAGE_TYPE_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')
|
App::get('/v1/account')
|
||||||
->desc('Get account')
|
->desc('Get account')
|
||||||
->groups(['api', 'account'])
|
->groups(['api', 'account'])
|
||||||
|
@ -1969,6 +2094,7 @@ App::patch('/v1/account/email')
|
||||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$oldEmail = $user->getAttribute('email');
|
||||||
$email = \strtolower($email);
|
$email = \strtolower($email);
|
||||||
|
|
||||||
// Makes sure this email is not already used in another identity
|
// Makes sure this email is not already used in another identity
|
||||||
|
@ -1993,8 +2119,25 @@ App::patch('/v1/account/email')
|
||||||
->setAttribute('passwordUpdate', DateTime::now());
|
->setAttribute('passwordUpdate', DateTime::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$email]),
|
||||||
|
]));
|
||||||
|
|
||||||
|
if ($target instanceof Document && !$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||||
|
/**
|
||||||
|
* @var Document $oldTarget
|
||||||
|
*/
|
||||||
|
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
|
||||||
|
|
||||||
|
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||||
|
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate) {
|
} catch (Duplicate) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -2039,6 +2182,16 @@ App::patch('/v1/account/phone')
|
||||||
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$phone]),
|
||||||
|
]));
|
||||||
|
|
||||||
|
if ($target instanceof Document && !$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldPhone = $user->getAttribute('phone');
|
||||||
|
|
||||||
$user
|
$user
|
||||||
->setAttribute('phone', $phone)
|
->setAttribute('phone', $phone)
|
||||||
->setAttribute('phoneVerification', false) // After this user needs to confirm phone number again
|
->setAttribute('phoneVerification', false) // After this user needs to confirm phone number again
|
||||||
|
@ -2054,6 +2207,15 @@ App::patch('/v1/account/phone')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||||
|
/**
|
||||||
|
* @var Document $oldTarget
|
||||||
|
*/
|
||||||
|
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
|
||||||
|
|
||||||
|
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||||
|
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -2264,8 +2426,8 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||||
$provider = $session->getAttribute('provider');
|
$provider = $session->getAttribute('provider');
|
||||||
$refreshToken = $session->getAttribute('providerRefreshToken');
|
$refreshToken = $session->getAttribute('providerRefreshToken');
|
||||||
|
|
||||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||||
|
|
||||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||||
|
|
||||||
|
@ -2877,10 +3039,9 @@ App::post('/v1/account/verification/phone')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('locale')
|
->inject('locale')
|
||||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, EventPhone $queueForMessaging, Document $project, Locale $locale) {
|
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) {
|
||||||
|
|
||||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED);
|
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($user->getAttribute('phone'))) {
|
if (empty($user->getAttribute('phone'))) {
|
||||||
|
@ -2929,11 +3090,19 @@ App::post('/v1/account/verification/phone')
|
||||||
$message = $message->setParam('{{token}}', $secret);
|
$message = $message->setParam('{{token}}', $secret);
|
||||||
$message = $message->render();
|
$message = $message->render();
|
||||||
|
|
||||||
|
$messageDoc = new Document([
|
||||||
|
'$id' => $verification->getId(),
|
||||||
|
'data' => [
|
||||||
|
'content' => $message,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
$queueForMessaging
|
$queueForMessaging
|
||||||
->setRecipient($user->getAttribute('phone'))
|
->setMessage($messageDoc)
|
||||||
->setMessage($message)
|
->setRecipients([$user->getAttribute('phone')])
|
||||||
->trigger()
|
->setProviderType(MESSAGE_TYPE_SMS)
|
||||||
;
|
->setProject($project)
|
||||||
|
->trigger();
|
||||||
|
|
||||||
$queueForEvents
|
$queueForEvents
|
||||||
->setParam('userId', $user->getId())
|
->setParam('userId', $user->getId())
|
||||||
|
@ -3010,3 +3179,61 @@ App::put('/v1/account/verification/phone')
|
||||||
|
|
||||||
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
|
$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);
|
||||||
|
});
|
||||||
|
|
|
@ -84,8 +84,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
|
||||||
$accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
|
$accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
|
||||||
$refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
|
$refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
|
||||||
|
|
||||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||||
|
|
||||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||||
|
|
||||||
|
|
|
@ -1561,7 +1561,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('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('key', '', new Key(), 'Attribute Key.')
|
||||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
->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)
|
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
|
|
2961
app/controllers/api/messaging.php
Normal file
2961
app/controllers/api/messaging.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -159,7 +159,7 @@ App::post('/v1/projects')
|
||||||
'legalTaxId' => ID::custom($legalTaxId),
|
'legalTaxId' => ID::custom($legalTaxId),
|
||||||
'services' => new stdClass(),
|
'services' => new stdClass(),
|
||||||
'platforms' => null,
|
'platforms' => null,
|
||||||
'authProviders' => [],
|
'oAuthProviders' => [],
|
||||||
'webhooks' => null,
|
'webhooks' => null,
|
||||||
'keys' => null,
|
'keys' => null,
|
||||||
'auths' => $auths,
|
'auths' => $auths,
|
||||||
|
@ -598,7 +598,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_PROJECT)
|
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'Provider Name')
|
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name')
|
||||||
->param('appId', null, new Text(256), 'Provider app ID. Max length: 256 chars.', true)
|
->param('appId', null, new Text(256), 'Provider app ID. Max length: 256 chars.', true)
|
||||||
->param('secret', null, new text(512), 'Provider secret key. Max length: 512 chars.', true)
|
->param('secret', null, new text(512), 'Provider secret key. Max length: 512 chars.', true)
|
||||||
->param('enabled', null, new Boolean(), 'Provider status. Set to \'false\' to disable new session creation.', true)
|
->param('enabled', null, new Boolean(), 'Provider status. Set to \'false\' to disable new session creation.', true)
|
||||||
|
@ -612,7 +612,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
$providers = $project->getAttribute('authProviders', []);
|
$providers = $project->getAttribute('oAuthProviders', []);
|
||||||
|
|
||||||
if ($appId !== null) {
|
if ($appId !== null) {
|
||||||
$providers[$provider . 'Appid'] = $appId;
|
$providers[$provider . 'Appid'] = $appId;
|
||||||
|
@ -626,7 +626,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||||
$providers[$provider . 'Enabled'] = $enabled;
|
$providers[$provider . 'Enabled'] = $enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('authProviders', $providers));
|
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('oAuthProviders', $providers));
|
||||||
|
|
||||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||||
});
|
});
|
||||||
|
@ -1151,7 +1151,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
->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('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('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||||
|
@ -1268,7 +1268,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||||
->param('keyId', '', new UID(), 'Key unique ID.')
|
->param('keyId', '', new UID(), 'Key unique ID.')
|
||||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
->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('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('response')
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use Appwrite\Detector\Detector;
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Event;
|
use Appwrite\Event\Event;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
use Appwrite\Event\Phone as EventPhone;
|
use Appwrite\Event\Messaging;
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Network\Validator\Email;
|
use Appwrite\Network\Validator\Email;
|
||||||
use Utopia\Validator\Host;
|
use Utopia\Validator\Host;
|
||||||
|
@ -388,7 +388,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
->inject('queueForMails')
|
->inject('queueForMails')
|
||||||
->inject('queueForMessaging')
|
->inject('queueForMessaging')
|
||||||
->inject('queueForEvents')
|
->inject('queueForEvents')
|
||||||
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) {
|
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) {
|
||||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||||
|
|
||||||
|
@ -628,6 +628,10 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
->trigger()
|
->trigger()
|
||||||
;
|
;
|
||||||
} elseif (!empty($phone)) {
|
} elseif (!empty($phone)) {
|
||||||
|
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
||||||
|
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||||
|
}
|
||||||
|
|
||||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
|
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
|
||||||
|
|
||||||
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-' . $locale->default] ?? [];
|
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-' . $locale->default] ?? [];
|
||||||
|
@ -638,9 +642,18 @@ App::post('/v1/teams/:teamId/memberships')
|
||||||
$message = $message->setParam('{{token}}', $url);
|
$message = $message->setParam('{{token}}', $url);
|
||||||
$message = $message->render();
|
$message = $message->render();
|
||||||
|
|
||||||
|
$messageDoc = new Document([
|
||||||
|
'$id' => ID::unique(),
|
||||||
|
'data' => [
|
||||||
|
'content' => $message,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
$queueForMessaging
|
$queueForMessaging
|
||||||
->setRecipient($phone)
|
->setMessage($messageDoc)
|
||||||
->setMessage($message)
|
->setRecipients([$phone])
|
||||||
|
->setProviderType('SMS')
|
||||||
|
->setProject($project)
|
||||||
->trigger();
|
->trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use Appwrite\Event\Event;
|
||||||
use Appwrite\Network\Validator\Email;
|
use Appwrite\Network\Validator\Email;
|
||||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Identities;
|
use Appwrite\Utopia\Database\Validator\Queries\Identities;
|
||||||
|
use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||||
use Utopia\Database\Validator\Queries;
|
use Utopia\Database\Validator\Queries;
|
||||||
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||||
use Utopia\Database\Validator\Query\Limit;
|
use Utopia\Database\Validator\Query\Limit;
|
||||||
|
@ -98,6 +99,42 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||||
'memberships' => null,
|
'memberships' => null,
|
||||||
'search' => implode(' ', [$userId, $email, $phone, $name]),
|
'search' => implode(' ', [$userId, $email, $phone, $name]),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
if ($email) {
|
||||||
|
try {
|
||||||
|
$target = $dbForProject->createDocument('targets', new Document([
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'providerType' => 'email',
|
||||||
|
'identifier' => $email,
|
||||||
|
]));
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
|
||||||
|
} catch (Duplicate) {
|
||||||
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$email]),
|
||||||
|
]);
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($phone) {
|
||||||
|
try {
|
||||||
|
$target = $dbForProject->createDocument('targets', new Document([
|
||||||
|
'userId' => $user->getId(),
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'providerType' => 'sms',
|
||||||
|
'identifier' => $phone,
|
||||||
|
]));
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
|
||||||
|
} catch (Duplicate) {
|
||||||
|
$existingTarget = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$phone]),
|
||||||
|
]);
|
||||||
|
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -379,6 +416,98 @@ App::post('/v1/users/scrypt-modified')
|
||||||
->dynamic($user, Response::MODEL_USER);
|
->dynamic($user, Response::MODEL_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::post('/v1/users/:userId/targets')
|
||||||
|
->desc('Create User Target')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('audits.event', 'target.create')
|
||||||
|
->label('audits.resource', 'target/response.$id')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].create')
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'createTarget')
|
||||||
|
->label('sdk.description', '/docs/references/users/create-target.md')
|
||||||
|
->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('providerType', '', new WhiteList([MESSAGE_TYPE_EMAIL, MESSAGE_TYPE_SMS, MESSAGE_TYPE_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)
|
||||||
|
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $targetId, string $userId, string $providerType, string $identifier, string $providerId, string $name, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||||
|
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
|
||||||
|
|
||||||
|
$provider = new Document();
|
||||||
|
|
||||||
|
if ($providerType === MESSAGE_TYPE_PUSH) {
|
||||||
|
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||||
|
|
||||||
|
if ($provider->isEmpty()) {
|
||||||
|
throw new Exception(Exception::PROVIDER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($providerType) {
|
||||||
|
case 'email':
|
||||||
|
$validator = new Email();
|
||||||
|
if (!$validator->isValid($identifier)) {
|
||||||
|
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE_TYPE_SMS:
|
||||||
|
$validator = new Phone();
|
||||||
|
if (!$validator->isValid($identifier)) {
|
||||||
|
throw new Exception(Exception::GENERAL_INVALID_PHONE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE_TYPE_PUSH:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $dbForProject->getDocument('targets', $targetId);
|
||||||
|
|
||||||
|
if (!$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$target = $dbForProject->createDocument('targets', new Document([
|
||||||
|
'$id' => $targetId,
|
||||||
|
'providerId' => $providerId ?? null,
|
||||||
|
'providerInternalId' => $provider->getInternalId() ?? null,
|
||||||
|
'providerType' => $providerType,
|
||||||
|
'userId' => $userId,
|
||||||
|
'userInternalId' => $user->getInternalId(),
|
||||||
|
'identifier' => $identifier,
|
||||||
|
'name' => ($name !== '') ? $name : null,
|
||||||
|
]));
|
||||||
|
} 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/users')
|
App::get('/v1/users')
|
||||||
->desc('List users')
|
->desc('List users')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
@ -482,6 +611,38 @@ App::get('/v1/users/:userId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::get('/v1/users/:userId/targets/:targetId')
|
||||||
|
->desc('Get User Target')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('scope', 'targets.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'getTarget')
|
||||||
|
->label('sdk.description', '/docs/references/users/get-user-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.')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject) {
|
||||||
|
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $user->find('$id', $targetId, 'targets');
|
||||||
|
|
||||||
|
if (empty($target)) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic($target, Response::MODEL_TARGET);
|
||||||
|
});
|
||||||
|
|
||||||
App::get('/v1/users/:userId/sessions')
|
App::get('/v1/users/:userId/sessions')
|
||||||
->desc('List user sessions')
|
->desc('List user sessions')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
@ -646,6 +807,53 @@ App::get('/v1/users/:userId/logs')
|
||||||
]), Response::MODEL_LOG_LIST);
|
]), Response::MODEL_LOG_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::get('/v1/users/:userId/targets')
|
||||||
|
->desc('List User Targets')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('scope', 'targets.read')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'listTargets')
|
||||||
|
->label('sdk.description', '/docs/references/users/list-user-targets.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_TARGET_LIST)
|
||||||
|
->param('userId', '', new UID(), 'User ID.')
|
||||||
|
->param('queries', [], new Targets(), '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(', ', Users::ALLOWED_ATTRIBUTES), true)
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $userId, array $queries, Response $response, Database $dbForProject) {
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$queries = Query::parseQueries($queries);
|
||||||
|
|
||||||
|
$queries[] = Query::equal('userId', [$userId]);
|
||||||
|
|
||||||
|
// Get cursor document if there was a cursor query
|
||||||
|
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
|
||||||
|
$cursor = reset($cursor);
|
||||||
|
|
||||||
|
if ($cursor) {
|
||||||
|
$targetId = $cursor->getValue();
|
||||||
|
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
|
||||||
|
|
||||||
|
if ($cursorDocument->isEmpty()) {
|
||||||
|
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$cursor->setValue($cursorDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->dynamic(new Document([
|
||||||
|
'targets' => $dbForProject->find('targets', $queries),
|
||||||
|
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
|
||||||
|
]), Response::MODEL_TARGET_LIST);
|
||||||
|
});
|
||||||
|
|
||||||
App::get('/v1/users/identities')
|
App::get('/v1/users/identities')
|
||||||
->desc('List Identities')
|
->desc('List Identities')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
@ -949,6 +1157,16 @@ App::patch('/v1/users/:userId/email')
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$target = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$email]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($target instanceof Document && !$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldEmail = $user->getAttribute('email');
|
||||||
|
|
||||||
$user
|
$user
|
||||||
->setAttribute('email', $email)
|
->setAttribute('email', $email)
|
||||||
->setAttribute('emailVerification', false)
|
->setAttribute('emailVerification', false)
|
||||||
|
@ -957,6 +1175,15 @@ App::patch('/v1/users/:userId/email')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||||
|
/**
|
||||||
|
* @var Document $oldTarget
|
||||||
|
*/
|
||||||
|
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
|
||||||
|
|
||||||
|
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||||
|
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email));
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -994,13 +1221,32 @@ App::patch('/v1/users/:userId/phone')
|
||||||
throw new Exception(Exception::USER_NOT_FOUND);
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$oldPhone = $user->getAttribute('phone');
|
||||||
|
|
||||||
$user
|
$user
|
||||||
->setAttribute('phone', $number)
|
->setAttribute('phone', $number)
|
||||||
->setAttribute('phoneVerification', false)
|
->setAttribute('phoneVerification', false)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$target = $dbForProject->findOne('targets', [
|
||||||
|
Query::equal('identifier', [$number]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($target instanceof Document && !$target->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||||
|
/**
|
||||||
|
* @var Document $oldTarget
|
||||||
|
*/
|
||||||
|
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
|
||||||
|
|
||||||
|
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||||
|
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $number));
|
||||||
|
}
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
} catch (Duplicate $th) {
|
} catch (Duplicate $th) {
|
||||||
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
|
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -1080,6 +1326,100 @@ App::patch('/v1/users/:userId/prefs')
|
||||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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}')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].update')
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->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.)', true)
|
||||||
|
->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('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $userId, string $targetId, string $identifier, string $providerId, string $name, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $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) {
|
||||||
|
$providerType = $target->getAttribute('providerType');
|
||||||
|
|
||||||
|
switch ($providerType) {
|
||||||
|
case 'email':
|
||||||
|
$validator = new Email();
|
||||||
|
if (!$validator->isValid($identifier)) {
|
||||||
|
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE_TYPE_SMS:
|
||||||
|
$validator = new Phone();
|
||||||
|
if (!$validator->isValid($identifier)) {
|
||||||
|
throw new Exception(Exception::GENERAL_INVALID_PHONE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE_TYPE_PUSH:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target->setAttribute('identifier', $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($providerId) {
|
||||||
|
$provider = $dbForProject->getDocument('providers', $providerId);
|
||||||
|
|
||||||
|
if ($provider->isEmpty()) {
|
||||||
|
throw new Exception(Exception::PROVIDER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($provider->getAttribute('type') !== $target->getAttribute('providerType')) {
|
||||||
|
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target->setAttribute('providerId', $provider->getId());
|
||||||
|
$target->setAttribute('providerInternalId', $provider->getInternalId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name) {
|
||||||
|
$target->setAttribute('name', $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
$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);
|
||||||
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/:userId/sessions/:sessionId')
|
App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||||
->desc('Delete user session')
|
->desc('Delete user session')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
@ -1210,6 +1550,53 @@ App::delete('/v1/users/:userId')
|
||||||
$response->noContent();
|
$response->noContent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App::delete('/v1/users/:userId/targets/:targetId')
|
||||||
|
->desc('Delete user target')
|
||||||
|
->groups(['api', 'users'])
|
||||||
|
->label('audits.event', 'target.delete')
|
||||||
|
->label('audits.resource', 'target/{request.$targetId}')
|
||||||
|
->label('event', 'users.[userId].targets.[targetId].delete')
|
||||||
|
->label('scope', 'targets.write')
|
||||||
|
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||||
|
->label('sdk.namespace', 'users')
|
||||||
|
->label('sdk.method', 'deleteTarget')
|
||||||
|
->label('sdk.description', '/docs/references/users/delete-target.md')
|
||||||
|
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||||
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
|
->label('sdk.response.model', Response::MODEL_NONE)
|
||||||
|
->param('userId', '', new UID(), 'User ID.')
|
||||||
|
->param('targetId', '', new UID(), 'Target ID.')
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('response')
|
||||||
|
->inject('dbForProject')
|
||||||
|
->action(function (string $userId, string $targetId, Event $queueForEvents, Response $response, Database $dbForProject) {
|
||||||
|
|
||||||
|
$user = $dbForProject->getDocument('users', $userId);
|
||||||
|
|
||||||
|
if ($user->isEmpty()) {
|
||||||
|
throw new Exception(Exception::USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbForProject->deleteDocument('targets', $target->getId());
|
||||||
|
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||||
|
|
||||||
|
$queueForEvents
|
||||||
|
->setParam('userId', $user->getId())
|
||||||
|
->setParam('targetId', $target->getId());
|
||||||
|
|
||||||
|
$response->noContent();
|
||||||
|
});
|
||||||
|
|
||||||
App::delete('/v1/users/identities/:identityId')
|
App::delete('/v1/users/identities/:identityId')
|
||||||
->desc('Delete Identity')
|
->desc('Delete Identity')
|
||||||
->groups(['api', 'users'])
|
->groups(['api', 'users'])
|
||||||
|
@ -1251,7 +1638,7 @@ App::get('/v1/users/usage')
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
||||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('oAuthProviders', [])))), true), 'Provider Name.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('register')
|
->inject('register')
|
||||||
|
|
139
app/init.php
139
app/init.php
|
@ -25,7 +25,7 @@ use Appwrite\Event\Audit;
|
||||||
use Appwrite\Event\Database as EventDatabase;
|
use Appwrite\Event\Database as EventDatabase;
|
||||||
use Appwrite\Event\Event;
|
use Appwrite\Event\Event;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
use Appwrite\Event\Phone;
|
use Appwrite\Event\Messaging;
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\GraphQL\Schema;
|
use Appwrite\GraphQL\Schema;
|
||||||
use Appwrite\Network\Validator\Email;
|
use Appwrite\Network\Validator\Email;
|
||||||
|
@ -47,13 +47,7 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
use Utopia\Database\Validator\Structure;
|
use Utopia\Database\Validator\Structure;
|
||||||
use Utopia\Locale\Locale;
|
use Utopia\Locale\Locale;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
use Utopia\Messaging\Adapters\SMS\Mock;
|
|
||||||
use Appwrite\GraphQL\Promises\Adapter\Swoole;
|
use Appwrite\GraphQL\Promises\Adapter\Swoole;
|
||||||
use Utopia\Messaging\Adapters\SMS\Msg91;
|
|
||||||
use Utopia\Messaging\Adapters\SMS\Telesign;
|
|
||||||
use Utopia\Messaging\Adapters\SMS\TextMagic;
|
|
||||||
use Utopia\Messaging\Adapters\SMS\Twilio;
|
|
||||||
use Utopia\Messaging\Adapters\SMS\Vonage;
|
|
||||||
use Utopia\Registry\Registry;
|
use Utopia\Registry\Registry;
|
||||||
use Utopia\Storage\Device;
|
use Utopia\Storage\Device;
|
||||||
use Utopia\Storage\Device\Backblaze;
|
use Utopia\Storage\Device\Backblaze;
|
||||||
|
@ -83,6 +77,7 @@ use Utopia\Validator\Range;
|
||||||
use Utopia\Validator\IP;
|
use Utopia\Validator\IP;
|
||||||
use Utopia\Validator\URL;
|
use Utopia\Validator\URL;
|
||||||
use Utopia\Validator\WhiteList;
|
use Utopia\Validator\WhiteList;
|
||||||
|
use Utopia\CLI\Console;
|
||||||
|
|
||||||
const APP_NAME = 'Appwrite';
|
const APP_NAME = 'Appwrite';
|
||||||
const APP_DOMAIN = 'appwrite.io';
|
const APP_DOMAIN = 'appwrite.io';
|
||||||
|
@ -103,13 +98,14 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB
|
||||||
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
||||||
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
|
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
|
||||||
const APP_LIMIT_SUBQUERY = 1000;
|
const APP_LIMIT_SUBQUERY = 1000;
|
||||||
|
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1000000;
|
||||||
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
||||||
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||||
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||||
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||||
const APP_USER_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_UPDATE = 24 * 60 * 60; // 24 hours
|
||||||
const APP_CACHE_BUSTER = 328;
|
const APP_CACHE_BUSTER = 329;
|
||||||
const APP_VERSION_STABLE = '1.4.13';
|
const APP_VERSION_STABLE = '1.4.13';
|
||||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||||
|
@ -173,6 +169,7 @@ const DELETE_TYPE_SESSIONS = 'sessions';
|
||||||
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||||
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
||||||
const DELETE_TYPE_SCHEDULES = 'schedules';
|
const DELETE_TYPE_SCHEDULES = 'schedules';
|
||||||
|
const DELETE_TYPE_TOPIC = 'topic';
|
||||||
// Compression type
|
// Compression type
|
||||||
const COMPRESSION_TYPE_NONE = 'none';
|
const COMPRESSION_TYPE_NONE = 'none';
|
||||||
const COMPRESSION_TYPE_GZIP = 'gzip';
|
const COMPRESSION_TYPE_GZIP = 'gzip';
|
||||||
|
@ -193,6 +190,10 @@ const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
|
||||||
// Function headers
|
// Function headers
|
||||||
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
||||||
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||||
|
// Message types
|
||||||
|
const MESSAGE_TYPE_EMAIL = 'email';
|
||||||
|
const MESSAGE_TYPE_SMS = 'sms';
|
||||||
|
const MESSAGE_TYPE_PUSH = 'push';
|
||||||
// Usage metrics
|
// Usage metrics
|
||||||
const METRIC_TEAMS = 'teams';
|
const METRIC_TEAMS = 'teams';
|
||||||
const METRIC_USERS = 'users';
|
const METRIC_USERS = 'users';
|
||||||
|
@ -237,7 +238,7 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
||||||
Config::load('events', __DIR__ . '/config/events.php');
|
Config::load('events', __DIR__ . '/config/events.php');
|
||||||
Config::load('auth', __DIR__ . '/config/auth.php');
|
Config::load('auth', __DIR__ . '/config/auth.php');
|
||||||
Config::load('errors', __DIR__ . '/config/errors.php');
|
Config::load('errors', __DIR__ . '/config/errors.php');
|
||||||
Config::load('providers', __DIR__ . '/config/providers.php');
|
Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php');
|
||||||
Config::load('platforms', __DIR__ . '/config/platforms.php');
|
Config::load('platforms', __DIR__ . '/config/platforms.php');
|
||||||
Config::load('collections', __DIR__ . '/config/collections.php');
|
Config::load('collections', __DIR__ . '/config/collections.php');
|
||||||
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
|
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
|
||||||
|
@ -526,6 +527,107 @@ Database::addFilter(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryTargets',
|
||||||
|
function (mixed $value) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
return Authorization::skip(fn() => $database
|
||||||
|
->find('targets', [
|
||||||
|
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBQUERY)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Database::addFilter(
|
||||||
|
'subQueryTopicTargets',
|
||||||
|
function (mixed $value) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
function (mixed $value, Document $document, Database $database) {
|
||||||
|
$targetIds = Authorization::skip(fn () => \array_map(
|
||||||
|
fn ($document) => $document->getAttribute('targetId'),
|
||||||
|
$database
|
||||||
|
->find('subscribers', [
|
||||||
|
Query::equal('topicInternalId', [$document->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
|
||||||
|
])
|
||||||
|
));
|
||||||
|
if (\count($targetIds) > 0) {
|
||||||
|
return $database->find('targets', [Query::equal('$id', $targetIds)]);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
$providerType = $message->getAttribute('providerType', '');
|
||||||
|
|
||||||
|
if ($providerType === MESSAGE_TYPE_EMAIL) {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]);
|
||||||
|
} elseif ($providerType === MESSAGE_TYPE_SMS) {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]);
|
||||||
|
} else {
|
||||||
|
$searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = \implode(' ', \array_filter($searchValues));
|
||||||
|
|
||||||
|
return $search;
|
||||||
|
},
|
||||||
|
function (mixed $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DB Formats
|
* DB Formats
|
||||||
*/
|
*/
|
||||||
|
@ -906,7 +1008,7 @@ App::setResource('queue', function (Group $pools) {
|
||||||
return $pools->get('queue')->pop()->getResource();
|
return $pools->get('queue')->pop()->getResource();
|
||||||
}, ['pools']);
|
}, ['pools']);
|
||||||
App::setResource('queueForMessaging', function (Connection $queue) {
|
App::setResource('queueForMessaging', function (Connection $queue) {
|
||||||
return new Phone($queue);
|
return new Messaging($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
App::setResource('queueForMails', function (Connection $queue) {
|
App::setResource('queueForMails', function (Connection $queue) {
|
||||||
return new Mail($queue);
|
return new Mail($queue);
|
||||||
|
@ -1122,7 +1224,7 @@ App::setResource('console', function () {
|
||||||
],
|
],
|
||||||
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
|
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
|
||||||
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
|
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
|
||||||
'authProviders' => [
|
'oAuthProviders' => [
|
||||||
'githubEnabled' => true,
|
'githubEnabled' => true,
|
||||||
'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
|
'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
|
||||||
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
|
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
|
||||||
|
@ -1346,21 +1448,6 @@ App::setResource('passwordsDictionary', function ($register) {
|
||||||
return $register->get('passwordsDictionary');
|
return $register->get('passwordsDictionary');
|
||||||
}, ['register']);
|
}, ['register']);
|
||||||
|
|
||||||
App::setResource('sms', function () {
|
|
||||||
$dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
|
|
||||||
$user = $dsn->getUser();
|
|
||||||
$secret = $dsn->getPassword();
|
|
||||||
|
|
||||||
return match ($dsn->getHost()) {
|
|
||||||
'mock' => new Mock($user, $secret), // used for tests
|
|
||||||
'twilio' => new Twilio($user, $secret),
|
|
||||||
'text-magic' => new TextMagic($user, $secret),
|
|
||||||
'telesign' => new Telesign($user, $secret),
|
|
||||||
'msg91' => new Msg91($user, $secret),
|
|
||||||
'vonage' => new Vonage($user, $secret),
|
|
||||||
default => null
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
App::setResource('servers', function () {
|
App::setResource('servers', function () {
|
||||||
$platforms = Config::getParam('platforms');
|
$platforms = Config::getParam('platforms');
|
||||||
|
|
|
@ -521,14 +521,20 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
- _APP_REDIS_HOST
|
- _APP_REDIS_HOST
|
||||||
- _APP_REDIS_PORT
|
- _APP_REDIS_PORT
|
||||||
- _APP_REDIS_USER
|
- _APP_REDIS_USER
|
||||||
- _APP_REDIS_PASS
|
- _APP_REDIS_PASS
|
||||||
- _APP_SMS_PROVIDER
|
- _APP_DB_HOST
|
||||||
- _APP_SMS_FROM
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_SMS_FROM
|
||||||
|
- _APP_SMS_PROVIDER
|
||||||
|
|
||||||
appwrite-worker-migrations:
|
appwrite-worker-migrations:
|
||||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||||
|
|
|
@ -9,10 +9,10 @@ use Appwrite\Event\Certificate;
|
||||||
use Appwrite\Event\Database as EventDatabase;
|
use Appwrite\Event\Database as EventDatabase;
|
||||||
use Appwrite\Event\Delete;
|
use Appwrite\Event\Delete;
|
||||||
use Appwrite\Event\Func;
|
use Appwrite\Event\Func;
|
||||||
|
use Appwrite\Event\Hamster;
|
||||||
use Appwrite\Event\Mail;
|
use Appwrite\Event\Mail;
|
||||||
use Appwrite\Event\Messaging;
|
use Appwrite\Event\Messaging;
|
||||||
use Appwrite\Event\Migration;
|
use Appwrite\Event\Migration;
|
||||||
use Appwrite\Event\Phone;
|
|
||||||
use Appwrite\Platform\Appwrite;
|
use Appwrite\Platform\Appwrite;
|
||||||
use Appwrite\Usage\Stats;
|
use Appwrite\Usage\Stats;
|
||||||
use Swoole\Runtime;
|
use Swoole\Runtime;
|
||||||
|
@ -129,7 +129,7 @@ Server::setResource('queueForDatabase', function (Connection $queue) {
|
||||||
return new EventDatabase($queue);
|
return new EventDatabase($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
Server::setResource('queueForMessaging', function (Connection $queue) {
|
Server::setResource('queueForMessaging', function (Connection $queue) {
|
||||||
return new Phone($queue);
|
return new Messaging($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
Server::setResource('queueForMails', function (Connection $queue) {
|
Server::setResource('queueForMails', function (Connection $queue) {
|
||||||
return new Mail($queue);
|
return new Mail($queue);
|
||||||
|
@ -155,6 +155,9 @@ Server::setResource('queueForCertificates', function (Connection $queue) {
|
||||||
Server::setResource('queueForMigrations', function (Connection $queue) {
|
Server::setResource('queueForMigrations', function (Connection $queue) {
|
||||||
return new Migration($queue);
|
return new Migration($queue);
|
||||||
}, ['queue']);
|
}, ['queue']);
|
||||||
|
Server::setResource('queueForHamster', function (Connection $queue) {
|
||||||
|
return new Hamster($queue);
|
||||||
|
}, ['queue']);
|
||||||
Server::setResource('logger', function (Registry $register) {
|
Server::setResource('logger', function (Registry $register) {
|
||||||
return $register->get('logger');
|
return $register->get('logger');
|
||||||
}, ['register']);
|
}, ['register']);
|
||||||
|
|
3
bin/worker-hamster
Normal file
3
bin/worker-hamster
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
php /usr/src/code/app/worker.php hamster $@
|
|
@ -52,7 +52,7 @@
|
||||||
"utopia-php/database": "0.45.*",
|
"utopia-php/database": "0.45.*",
|
||||||
"utopia-php/domains": "0.3.*",
|
"utopia-php/domains": "0.3.*",
|
||||||
"utopia-php/dsn": "0.1.*",
|
"utopia-php/dsn": "0.1.*",
|
||||||
"utopia-php/framework": "0.31.0",
|
"utopia-php/framework": "0.31.1",
|
||||||
"utopia-php/image": "0.5.*",
|
"utopia-php/image": "0.5.*",
|
||||||
"utopia-php/locale": "0.4.*",
|
"utopia-php/locale": "0.4.*",
|
||||||
"utopia-php/logger": "0.3.*",
|
"utopia-php/logger": "0.3.*",
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
],
|
],
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"appwrite/sdk-generator": "0.35.*",
|
"appwrite/sdk-generator": "0.36.*",
|
||||||
"phpunit/phpunit": "9.5.20",
|
"phpunit/phpunit": "9.5.20",
|
||||||
"squizlabs/php_codesniffer": "^3.7",
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
"swoole/ide-helper": "5.0.2",
|
"swoole/ide-helper": "5.0.2",
|
||||||
|
|
207
composer.lock
generated
207
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "69bc2e21a65b78344393706b39d789b4",
|
"content-hash": "7041499af2e7b23795d8ef82c9d7a072",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/jwt",
|
"name": "adhocore/jwt",
|
||||||
|
@ -156,11 +156,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "appwrite/php-runtimes",
|
"name": "appwrite/php-runtimes",
|
||||||
"version": "0.13.1",
|
"version": "0.13.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/appwrite/runtimes.git",
|
"url": "https://github.com/appwrite/runtimes.git",
|
||||||
"reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610"
|
"reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.0",
|
||||||
|
@ -195,7 +195,7 @@
|
||||||
"php",
|
"php",
|
||||||
"runtimes"
|
"runtimes"
|
||||||
],
|
],
|
||||||
"time": "2023-10-16T15:39:53+00:00"
|
"time": "2023-11-22T15:36:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-qrcode",
|
"name": "chillerlan/php-qrcode",
|
||||||
|
@ -402,16 +402,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
"version": "7.8.0",
|
"version": "7.8.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/guzzle.git",
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
"reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9"
|
"reference": "41042bc7ab002487b876a0683fc8dce04ddce104"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9",
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104",
|
||||||
"reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9",
|
"reference": "41042bc7ab002487b876a0683fc8dce04ddce104",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -426,11 +426,11 @@
|
||||||
"psr/http-client-implementation": "1.0"
|
"psr/http-client-implementation": "1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
|
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
|
||||||
"php-http/message-factory": "^1.1",
|
"php-http/message-factory": "^1.1",
|
||||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
|
"phpunit/phpunit": "^8.5.36 || ^9.6.15",
|
||||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
@ -508,7 +508,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||||
"source": "https://github.com/guzzle/guzzle/tree/7.8.0"
|
"source": "https://github.com/guzzle/guzzle/tree/7.8.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -524,28 +524,28 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-27T10:20:53+00:00"
|
"time": "2023-12-03T20:35:24+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/promises",
|
"name": "guzzlehttp/promises",
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/promises.git",
|
"url": "https://github.com/guzzle/promises.git",
|
||||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d"
|
"reference": "bbff78d96034045e58e13dedd6ad91b5d1253223"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d",
|
"url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223",
|
||||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d",
|
"reference": "bbff78d96034045e58e13dedd6ad91b5d1253223",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2.5 || ^8.0"
|
"php": "^7.2.5 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
"phpunit/phpunit": "^8.5.36 || ^9.6.15"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@ -591,7 +591,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/promises/issues",
|
"issues": "https://github.com/guzzle/promises/issues",
|
||||||
"source": "https://github.com/guzzle/promises/tree/2.0.1"
|
"source": "https://github.com/guzzle/promises/tree/2.0.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -607,20 +607,20 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-03T15:11:55+00:00"
|
"time": "2023-12-03T20:19:20+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/psr7",
|
"name": "guzzlehttp/psr7",
|
||||||
"version": "2.6.1",
|
"version": "2.6.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/psr7.git",
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
"reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727"
|
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221",
|
||||||
"reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
|
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -634,9 +634,9 @@
|
||||||
"psr/http-message-implementation": "1.0"
|
"psr/http-message-implementation": "1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
"http-interop/http-factory-tests": "^0.9",
|
"http-interop/http-factory-tests": "^0.9",
|
||||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
"phpunit/phpunit": "^8.5.36 || ^9.6.15"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||||
|
@ -707,7 +707,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/psr7/issues",
|
"issues": "https://github.com/guzzle/psr7/issues",
|
||||||
"source": "https://github.com/guzzle/psr7/tree/2.6.1"
|
"source": "https://github.com/guzzle/psr7/tree/2.6.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -723,7 +723,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-27T10:13:57+00:00"
|
"time": "2023-12-03T20:05:35+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "influxdb/influxdb-php",
|
"name": "influxdb/influxdb-php",
|
||||||
|
@ -1465,7 +1465,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
"version": "v3.3.0",
|
"version": "v3.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
|
@ -1512,7 +1512,7 @@
|
||||||
"description": "A generic function and convention to trigger deprecation notices",
|
"description": "A generic function and convention to trigger deprecation notices",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
|
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -2069,16 +2069,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/framework",
|
"name": "utopia-php/framework",
|
||||||
"version": "0.31.0",
|
"version": "0.31.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/framework.git",
|
"url": "https://github.com/utopia-php/framework.git",
|
||||||
"reference": "207f77378965fca9a9bc3783ea379d3549f86bc0"
|
"reference": "e50d2d16f4bc31319043f3f6d3dbea36c6fd6b68"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/207f77378965fca9a9bc3783ea379d3549f86bc0",
|
"url": "https://api.github.com/repos/utopia-php/framework/zipball/e50d2d16f4bc31319043f3f6d3dbea36c6fd6b68",
|
||||||
"reference": "207f77378965fca9a9bc3783ea379d3549f86bc0",
|
"reference": "e50d2d16f4bc31319043f3f6d3dbea36c6fd6b68",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2108,9 +2108,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/framework/issues",
|
"issues": "https://github.com/utopia-php/framework/issues",
|
||||||
"source": "https://github.com/utopia-php/framework/tree/0.31.0"
|
"source": "https://github.com/utopia-php/framework/tree/0.31.1"
|
||||||
},
|
},
|
||||||
"time": "2023-08-30T16:10:04+00:00"
|
"time": "2023-12-08T18:47:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/image",
|
"name": "utopia-php/image",
|
||||||
|
@ -2217,16 +2217,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/logger",
|
"name": "utopia-php/logger",
|
||||||
"version": "0.3.1",
|
"version": "0.3.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/logger.git",
|
"url": "https://github.com/utopia-php/logger.git",
|
||||||
"reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc"
|
"reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc",
|
"url": "https://api.github.com/repos/utopia-php/logger/zipball/ba763c10688fe2ed715ad2bed3f13d18dfec6253",
|
||||||
"reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc",
|
"reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2264,9 +2264,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/logger/issues",
|
"issues": "https://github.com/utopia-php/logger/issues",
|
||||||
"source": "https://github.com/utopia-php/logger/tree/0.3.1"
|
"source": "https://github.com/utopia-php/logger/tree/0.3.2"
|
||||||
},
|
},
|
||||||
"time": "2023-02-10T15:52:50+00:00"
|
"time": "2023-11-22T14:45:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/messaging",
|
"name": "utopia-php/messaging",
|
||||||
|
@ -3136,16 +3136,16 @@
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "appwrite/sdk-generator",
|
"name": "appwrite/sdk-generator",
|
||||||
"version": "0.35.2",
|
"version": "0.36.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||||
"reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b"
|
"reference": "3a10f1f895ed71120442ff71eb6adec3fd6b4e8a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b",
|
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3a10f1f895ed71120442ff71eb6adec3fd6b4e8a",
|
||||||
"reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b",
|
"reference": "3a10f1f895ed71120442ff71eb6adec3fd6b4e8a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3181,9 +3181,9 @@
|
||||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.35.2"
|
"source": "https://github.com/appwrite/sdk-generator/tree/0.36.0"
|
||||||
},
|
},
|
||||||
"time": "2023-09-14T14:59:50+00:00"
|
"time": "2023-11-20T10:03:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/deprecations",
|
"name": "doctrine/deprecations",
|
||||||
|
@ -3822,29 +3822,29 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpspec/prophecy",
|
"name": "phpspec/prophecy",
|
||||||
"version": "v1.17.0",
|
"version": "v1.18.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpspec/prophecy.git",
|
"url": "https://github.com/phpspec/prophecy.git",
|
||||||
"reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2"
|
"reference": "d4f454f7e1193933f04e6500de3e79191648ed0c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2",
|
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d4f454f7e1193933f04e6500de3e79191648ed0c",
|
||||||
"reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2",
|
"reference": "d4f454f7e1193933f04e6500de3e79191648ed0c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/instantiator": "^1.2 || ^2.0",
|
"doctrine/instantiator": "^1.2 || ^2.0",
|
||||||
"php": "^7.2 || 8.0.* || 8.1.* || 8.2.*",
|
"php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*",
|
||||||
"phpdocumentor/reflection-docblock": "^5.2",
|
"phpdocumentor/reflection-docblock": "^5.2",
|
||||||
"sebastian/comparator": "^3.0 || ^4.0",
|
"sebastian/comparator": "^3.0 || ^4.0 || ^5.0",
|
||||||
"sebastian/recursion-context": "^3.0 || ^4.0"
|
"sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpspec/phpspec": "^6.0 || ^7.0",
|
"phpspec/phpspec": "^6.0 || ^7.0",
|
||||||
"phpstan/phpstan": "^1.9",
|
"phpstan/phpstan": "^1.9",
|
||||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
"phpunit/phpunit": "^8.0 || ^9.0 || ^10.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@ -3877,6 +3877,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Double",
|
"Double",
|
||||||
"Dummy",
|
"Dummy",
|
||||||
|
"dev",
|
||||||
"fake",
|
"fake",
|
||||||
"mock",
|
"mock",
|
||||||
"spy",
|
"spy",
|
||||||
|
@ -3884,22 +3885,22 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
"issues": "https://github.com/phpspec/prophecy/issues",
|
||||||
"source": "https://github.com/phpspec/prophecy/tree/v1.17.0"
|
"source": "https://github.com/phpspec/prophecy/tree/v1.18.0"
|
||||||
},
|
},
|
||||||
"time": "2023-02-02T15:41:36+00:00"
|
"time": "2023-12-07T16:22:33+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpdoc-parser",
|
"name": "phpstan/phpdoc-parser",
|
||||||
"version": "1.24.2",
|
"version": "1.24.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||||
"reference": "bcad8d995980440892759db0c32acae7c8e79442"
|
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442",
|
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496",
|
||||||
"reference": "bcad8d995980440892759db0c32acae7c8e79442",
|
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3931,9 +3932,9 @@
|
||||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2"
|
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4"
|
||||||
},
|
},
|
||||||
"time": "2023-09-26T12:28:12+00:00"
|
"time": "2023-11-26T18:29:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
|
@ -5373,16 +5374,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "squizlabs/php_codesniffer",
|
"name": "squizlabs/php_codesniffer",
|
||||||
"version": "3.7.2",
|
"version": "3.8.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
|
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
|
||||||
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
|
"reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
|
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5805f7a4e4958dbb5e944ef1e6edae0a303765e7",
|
||||||
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
|
"reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -5392,7 +5393,7 @@
|
||||||
"php": ">=5.4.0"
|
"php": ">=5.4.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
|
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
|
||||||
},
|
},
|
||||||
"bin": [
|
"bin": [
|
||||||
"bin/phpcs",
|
"bin/phpcs",
|
||||||
|
@ -5411,22 +5412,45 @@
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Greg Sherwood",
|
"name": "Greg Sherwood",
|
||||||
"role": "lead"
|
"role": "Former lead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Juliette Reinders Folmer",
|
||||||
|
"role": "Current lead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Contributors",
|
||||||
|
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
|
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
|
||||||
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
|
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"phpcs",
|
"phpcs",
|
||||||
"standards",
|
"standards",
|
||||||
"static analysis"
|
"static analysis"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
|
"issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
|
||||||
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
|
"security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
|
||||||
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
|
"source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
|
||||||
|
"wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
|
||||||
},
|
},
|
||||||
"time": "2023-02-22T23:07:41+00:00"
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/PHPCSStandards",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/jrfnl",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://opencollective.com/php_codesniffer",
|
||||||
|
"type": "open_collective"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-12-08T12:32:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "swoole/ide-helper",
|
"name": "swoole/ide-helper",
|
||||||
|
@ -5676,16 +5700,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/theseer/tokenizer.git",
|
"url": "https://github.com/theseer/tokenizer.git",
|
||||||
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
|
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
|
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
|
||||||
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
|
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -5714,7 +5738,7 @@
|
||||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/theseer/tokenizer/issues",
|
"issues": "https://github.com/theseer/tokenizer/issues",
|
||||||
"source": "https://github.com/theseer/tokenizer/tree/1.2.1"
|
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -5722,30 +5746,31 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-07-28T10:34:58+00:00"
|
"time": "2023-11-20T00:12:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.7.1",
|
"version": "v3.8.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554"
|
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
|
||||||
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
|
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2.5",
|
"php": ">=7.2.5",
|
||||||
"symfony/polyfill-ctype": "^1.8",
|
"symfony/polyfill-ctype": "^1.8",
|
||||||
"symfony/polyfill-mbstring": "^1.3"
|
"symfony/polyfill-mbstring": "^1.3",
|
||||||
|
"symfony/polyfill-php80": "^1.22"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"psr/container": "^1.0|^2.0",
|
"psr/container": "^1.0|^2.0",
|
||||||
"symfony/phpunit-bridge": "^5.4.9|^6.3"
|
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -5781,7 +5806,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.7.1"
|
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -5793,7 +5818,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-08-28T11:09:02+00:00"
|
"time": "2023-11-21T18:54:41+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
@ -5822,5 +5847,5 @@
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.0"
|
"php": "8.0"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,9 @@ services:
|
||||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
|
- _APP_MESSAGE_SMS_TEST_DSN
|
||||||
|
- _APP_MESSAGE_EMAIL_TEST_DSN
|
||||||
|
- _APP_MESSAGE_PUSH_TEST_DSN
|
||||||
appwrite-realtime:
|
appwrite-realtime:
|
||||||
entrypoint: realtime
|
entrypoint: realtime
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
|
@ -568,14 +570,20 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
- _APP_REDIS_HOST
|
- _APP_REDIS_HOST
|
||||||
- _APP_REDIS_PORT
|
- _APP_REDIS_PORT
|
||||||
- _APP_REDIS_USER
|
- _APP_REDIS_USER
|
||||||
- _APP_REDIS_PASS
|
- _APP_REDIS_PASS
|
||||||
- _APP_SMS_PROVIDER
|
- _APP_DB_HOST
|
||||||
- _APP_SMS_FROM
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_SMS_FROM
|
||||||
|
- _APP_SMS_PROVIDER
|
||||||
|
|
||||||
appwrite-worker-migrations:
|
appwrite-worker-migrations:
|
||||||
entrypoint: worker-migrations
|
entrypoint: worker-migrations
|
||||||
|
@ -717,6 +725,63 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
|
|
||||||
|
appwrite-worker-hamster:
|
||||||
|
entrypoint: worker-hamster
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-worker-hamster
|
||||||
|
image: appwrite-dev
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- ./app:/usr/src/code/app
|
||||||
|
- ./src:/usr/src/code/src
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_MIXPANEL_TOKEN
|
||||||
|
|
||||||
|
appwrite-hamster-scheduler:
|
||||||
|
entrypoint: hamster
|
||||||
|
<<: *x-logging
|
||||||
|
container_name: appwrite-hamster-scheduler
|
||||||
|
image: appwrite-dev
|
||||||
|
networks:
|
||||||
|
- appwrite
|
||||||
|
volumes:
|
||||||
|
- ./app:/usr/src/code/app
|
||||||
|
- ./src:/usr/src/code/src
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- mariadb
|
||||||
|
environment:
|
||||||
|
- _APP_ENV
|
||||||
|
- _APP_WORKER_PER_CORE
|
||||||
|
- _APP_OPENSSL_KEY_V1
|
||||||
|
- _APP_REDIS_HOST
|
||||||
|
- _APP_REDIS_PORT
|
||||||
|
- _APP_REDIS_USER
|
||||||
|
- _APP_REDIS_PASS
|
||||||
|
- _APP_DB_HOST
|
||||||
|
- _APP_DB_PORT
|
||||||
|
- _APP_DB_SCHEMA
|
||||||
|
- _APP_DB_USER
|
||||||
|
- _APP_DB_PASS
|
||||||
|
- _APP_HAMSTER_TIME
|
||||||
|
- _APP_HAMSTER_INTERVAL
|
||||||
|
|
||||||
openruntimes-executor:
|
openruntimes-executor:
|
||||||
container_name: openruntimes-executor
|
container_name: openruntimes-executor
|
||||||
hostname: appwrite-executor
|
hostname: appwrite-executor
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueBuilds
|
appwrite health getQueueBuilds \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueCertificates
|
appwrite health getQueueCertificates \
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
appwrite health getQueueDatabases \
|
appwrite health getQueueDatabases \
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueDeletes
|
appwrite health getQueueDeletes \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueFunctions
|
appwrite health getQueueFunctions \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueLogs
|
appwrite health getQueueLogs \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueMails
|
appwrite health getQueueMails \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueMessaging
|
appwrite health getQueueMessaging \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueMigrations
|
appwrite health getQueueMigrations \
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
appwrite health getQueueWebhooks
|
appwrite health getQueueWebhooks \
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
## 10.1.0
|
||||||
|
|
||||||
|
* Add new queue health endpoints
|
||||||
|
* Fix between queries
|
||||||
|
|
||||||
## 10.0.0
|
## 10.0.0
|
||||||
|
|
||||||
* Parameter `url` is now optional in the `createMembership` endpoint
|
* Parameter `url` is now optional in the `createMembership` endpoint
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
## 11.0.1
|
||||||
|
|
||||||
|
* Fix between queries
|
||||||
|
|
||||||
## 11.0.0
|
## 11.0.0
|
||||||
|
|
||||||
* Parameter `url` is now optional in the `createMembership` endpoint
|
* Parameter `url` is now optional in the `createMembership` endpoint
|
||||||
|
|
|
@ -37,7 +37,7 @@ Finally, you will need to create a `feat-XXX-YYY-oauth` branch based on the `mas
|
||||||
The first step in adding a new OAuth2 provider is to add it to the list of providers located at:
|
The first step in adding a new OAuth2 provider is to add it to the list of providers located at:
|
||||||
|
|
||||||
```
|
```
|
||||||
app/config/providers.php
|
app/config/oAuthProviders.php
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure to fill in all data needed and that your provider array key name:
|
Make sure to fill in all data needed and that your provider array key name:
|
||||||
|
@ -45,7 +45,7 @@ Make sure to fill in all data needed and that your provider array key name:
|
||||||
- is in [`camelCase`](https://en.wikipedia.org/wiki/Camel_case) format for sentence, but lowercase for names. `github` must be all lowercased, but `paypalSandbox` should have uppercase S
|
- is in [`camelCase`](https://en.wikipedia.org/wiki/Camel_case) format for sentence, but lowercase for names. `github` must be all lowercased, but `paypalSandbox` should have uppercase S
|
||||||
- has no spaces or special characters
|
- has no spaces or special characters
|
||||||
|
|
||||||
> Please make sure to keep the list of providers in `providers.php` in the alphabetical order A-Z.
|
> Please make sure to keep the list of providers in `oAuthProviders.php` in the alphabetical order A-Z.
|
||||||
|
|
||||||
### 2.2 Add Provider Logo
|
### 2.2 Add Provider Logo
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ If you need any help with the contribution, feel free to head over to [our Disco
|
||||||
|
|
||||||
If your OAuth provider requires special configuration apart from `clientId` and `clientSecret` you can create a custom form. Currently this is being realized through putting all custom fields as JSON into the `clientSecret` field to keep the project API stable. You can implement your custom form following these steps:
|
If your OAuth provider requires special configuration apart from `clientId` and `clientSecret` you can create a custom form. Currently this is being realized through putting all custom fields as JSON into the `clientSecret` field to keep the project API stable. You can implement your custom form following these steps:
|
||||||
|
|
||||||
1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/providers.php`.
|
1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/oAuthProviders.php`.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<directory>./tests/e2e/Services/Projects</directory>
|
<directory>./tests/e2e/Services/Projects</directory>
|
||||||
<directory>./tests/e2e/Services/Storage</directory>
|
<directory>./tests/e2e/Services/Storage</directory>
|
||||||
<directory>./tests/e2e/Services/Webhooks</directory>
|
<directory>./tests/e2e/Services/Webhooks</directory>
|
||||||
|
<directory>./tests/e2e/Services/Messaging</directory>
|
||||||
<file>./tests/e2e/Services/Functions/FunctionsBase.php</file>
|
<file>./tests/e2e/Services/Functions/FunctionsBase.php</file>
|
||||||
<file>./tests/e2e/Services/Functions/FunctionsCustomServerTest.php</file>
|
<file>./tests/e2e/Services/Functions/FunctionsCustomServerTest.php</file>
|
||||||
<file>./tests/e2e/Services/Functions/FunctionsCustomClientTest.php</file>
|
<file>./tests/e2e/Services/Functions/FunctionsCustomClientTest.php</file>
|
||||||
|
|
|
@ -42,6 +42,9 @@ class Event
|
||||||
public const MIGRATIONS_QUEUE_NAME = 'v1-migrations';
|
public const MIGRATIONS_QUEUE_NAME = 'v1-migrations';
|
||||||
public const MIGRATIONS_CLASS_NAME = 'MigrationsV1';
|
public const MIGRATIONS_CLASS_NAME = 'MigrationsV1';
|
||||||
|
|
||||||
|
public const HAMSTER_QUEUE_NAME = 'v1-hamster';
|
||||||
|
public const HAMSTER_CLASS_NAME = 'HamsterV1';
|
||||||
|
|
||||||
protected string $queue = '';
|
protected string $queue = '';
|
||||||
protected string $class = '';
|
protected string $class = '';
|
||||||
protected string $event = '';
|
protected string $event = '';
|
||||||
|
|
157
src/Appwrite/Event/Hamster.php
Normal file
157
src/Appwrite/Event/Hamster.php
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Event;
|
||||||
|
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Queue\Client;
|
||||||
|
use Utopia\Queue\Connection;
|
||||||
|
|
||||||
|
class Hamster extends Event
|
||||||
|
{
|
||||||
|
protected string $type = '';
|
||||||
|
protected ?Document $project = null;
|
||||||
|
protected ?Document $organization = null;
|
||||||
|
protected ?Document $user = null;
|
||||||
|
|
||||||
|
public const TYPE_PROJECT = 'project';
|
||||||
|
public const TYPE_ORGANISATION = 'organisation';
|
||||||
|
public const TYPE_USER = 'user';
|
||||||
|
|
||||||
|
public function __construct(protected Connection $connection)
|
||||||
|
{
|
||||||
|
parent::__construct($connection);
|
||||||
|
|
||||||
|
$this
|
||||||
|
->setQueue(Event::HAMSTER_QUEUE_NAME)
|
||||||
|
->setClass(Event::HAMSTER_CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type for the hamster event.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setType(string $type): self
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set type for the hamster event.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the project for the hamster event.
|
||||||
|
*
|
||||||
|
* @param Document $project
|
||||||
|
*/
|
||||||
|
public function setProject(Document $project): self
|
||||||
|
{
|
||||||
|
$this->project = $project;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set project for the hamster event.
|
||||||
|
*
|
||||||
|
* @return Document
|
||||||
|
*/
|
||||||
|
public function getProject(): Document
|
||||||
|
{
|
||||||
|
return $this->project;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the organization for the hamster event.
|
||||||
|
*
|
||||||
|
* @param Document $organization
|
||||||
|
*/
|
||||||
|
public function setOrganization(Document $organization): self
|
||||||
|
{
|
||||||
|
$this->organization = $organization;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set organization for the hamster event.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getOrganization(): Document
|
||||||
|
{
|
||||||
|
return $this->organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user for the hamster event.
|
||||||
|
*
|
||||||
|
* @param Document $user
|
||||||
|
*/
|
||||||
|
public function setUser(Document $user): self
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set user for the hamster event.
|
||||||
|
*
|
||||||
|
* @return Document
|
||||||
|
*/
|
||||||
|
public function getUser(): Document
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the function event and sends it to the functions worker.
|
||||||
|
*
|
||||||
|
* @return string|bool
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function trigger(): string|bool
|
||||||
|
{
|
||||||
|
if ($this->paused) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = new Client($this->queue, $this->connection);
|
||||||
|
|
||||||
|
$events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null;
|
||||||
|
|
||||||
|
return $client->enqueue([
|
||||||
|
'type' => $this->type,
|
||||||
|
'project' => $this->project,
|
||||||
|
'organization' => $this->organization,
|
||||||
|
'user' => $this->user,
|
||||||
|
'events' => $events,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a function event from a base event
|
||||||
|
*
|
||||||
|
* @param Event $event
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function from(Event $event): self
|
||||||
|
{
|
||||||
|
$this->event = $event->getEvent();
|
||||||
|
$this->params = $event->getParams();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
173
src/Appwrite/Event/Messaging.php
Normal file
173
src/Appwrite/Event/Messaging.php
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Event;
|
||||||
|
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Queue\Connection;
|
||||||
|
use Utopia\Queue\Client;
|
||||||
|
|
||||||
|
class Messaging extends Event
|
||||||
|
{
|
||||||
|
protected ?string $messageId = null;
|
||||||
|
protected ?Document $message = null;
|
||||||
|
protected ?array $recipients = null;
|
||||||
|
protected ?string $scheduledAt = null;
|
||||||
|
protected ?string $providerType = null;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(protected Connection $connection)
|
||||||
|
{
|
||||||
|
parent::__construct($connection);
|
||||||
|
|
||||||
|
$this
|
||||||
|
->setQueue(Event::MESSAGING_QUEUE_NAME)
|
||||||
|
->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.
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setMessageId(string $messageId): self
|
||||||
|
{
|
||||||
|
$this->messageId = $messageId;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns set message ID for the messaging event.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessageId(): string
|
||||||
|
{
|
||||||
|
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 Scheduled delivery time for the messaging event.
|
||||||
|
*
|
||||||
|
* @param string $scheduledAt
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setScheduledAt(string $scheduledAt): self
|
||||||
|
{
|
||||||
|
$this->scheduledAt = $scheduledAt;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns set Delivery Time for the messaging event.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getScheduledAt(): string
|
||||||
|
{
|
||||||
|
return $this->scheduledAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set project for this event.
|
||||||
|
*
|
||||||
|
* @param Document $project
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setProject(Document $project): self
|
||||||
|
{
|
||||||
|
$this->project = $project;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the event and sends it to the messaging worker.
|
||||||
|
* @return string|bool
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function trigger(): string | bool
|
||||||
|
{
|
||||||
|
$client = new Client($this->queue, $this->connection);
|
||||||
|
|
||||||
|
return $client->enqueue([
|
||||||
|
'project' => $this->project,
|
||||||
|
'user' => $this->user,
|
||||||
|
'messageId' => $this->messageId,
|
||||||
|
'message' => $this->message,
|
||||||
|
'recipients' => $this->recipients,
|
||||||
|
'providerType' => $this->providerType,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,87 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Appwrite\Event;
|
|
||||||
|
|
||||||
use Utopia\Queue\Client;
|
|
||||||
use Utopia\Queue\Connection;
|
|
||||||
|
|
||||||
class Phone extends Event
|
|
||||||
{
|
|
||||||
protected string $recipient = '';
|
|
||||||
protected string $message = '';
|
|
||||||
|
|
||||||
public function __construct(protected Connection $connection)
|
|
||||||
{
|
|
||||||
parent::__construct($connection);
|
|
||||||
|
|
||||||
$this
|
|
||||||
->setQueue(Event::MESSAGING_QUEUE_NAME)
|
|
||||||
->setClass(Event::MESSAGING_CLASS_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets recipient for the messaging event.
|
|
||||||
*
|
|
||||||
* @param string $recipient
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setRecipient(string $recipient): self
|
|
||||||
{
|
|
||||||
$this->recipient = $recipient;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns set recipient for this messaging event.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRecipient(): string
|
|
||||||
{
|
|
||||||
return $this->recipient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets url for the messaging event.
|
|
||||||
*
|
|
||||||
* @param string $message
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setMessage(string $message): self
|
|
||||||
{
|
|
||||||
$this->message = $message;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns set url for the messaging event.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMessage(): string
|
|
||||||
{
|
|
||||||
return $this->message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the event and sends it to the messaging worker.
|
|
||||||
*
|
|
||||||
* @return string|bool
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function trigger(): string|bool
|
|
||||||
{
|
|
||||||
$client = new Client($this->queue, $this->connection);
|
|
||||||
|
|
||||||
return $client->enqueue([
|
|
||||||
'project' => $this->project,
|
|
||||||
'user' => $this->user,
|
|
||||||
'payload' => $this->payload,
|
|
||||||
'recipient' => $this->recipient,
|
|
||||||
'message' => $this->message,
|
|
||||||
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -55,6 +55,8 @@ class Exception extends \Exception
|
||||||
public const GENERAL_CODES_DISABLED = 'general_codes_disabled';
|
public const GENERAL_CODES_DISABLED = 'general_codes_disabled';
|
||||||
public const GENERAL_USAGE_DISABLED = 'general_usage_disabled';
|
public const GENERAL_USAGE_DISABLED = 'general_usage_disabled';
|
||||||
public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented';
|
public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented';
|
||||||
|
public const GENERAL_INVALID_EMAIL = 'general_invalid_email';
|
||||||
|
public const GENERAL_INVALID_PHONE = 'general_invalid_phone';
|
||||||
|
|
||||||
/** Users */
|
/** Users */
|
||||||
public const USER_COUNT_EXCEEDED = 'user_count_exceeded';
|
public const USER_COUNT_EXCEEDED = 'user_count_exceeded';
|
||||||
|
@ -86,6 +88,8 @@ class Exception extends \Exception
|
||||||
public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error';
|
public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error';
|
||||||
public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_alread_verified';
|
public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_alread_verified';
|
||||||
public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_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 */
|
/** Teams */
|
||||||
public const TEAM_NOT_FOUND = 'team_not_found';
|
public const TEAM_NOT_FOUND = 'team_not_found';
|
||||||
|
@ -236,6 +240,30 @@ class Exception extends \Exception
|
||||||
/** Health */
|
/** Health */
|
||||||
public const QUEUE_SIZE_EXCEEDED = 'queue_size_exceeded';
|
public const QUEUE_SIZE_EXCEEDED = 'queue_size_exceeded';
|
||||||
|
|
||||||
|
/** Provider */
|
||||||
|
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';
|
||||||
|
public const TOPIC_ALREADY_EXISTS = 'topic_already_exists';
|
||||||
|
|
||||||
|
/** Subscriber */
|
||||||
|
public const SUBSCRIBER_NOT_FOUND = 'subscriber_not_found';
|
||||||
|
public const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists';
|
||||||
|
|
||||||
|
/** Message */
|
||||||
|
public const MESSAGE_NOT_FOUND = 'message_not_found';
|
||||||
|
public const MESSAGE_MISSING_TARGET = 'message_missing_target';
|
||||||
|
public const MESSAGE_ALREADY_SENT = 'message_already_sent';
|
||||||
|
public const MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled';
|
||||||
|
public const MESSAGE_TARGET_NOT_EMAIL = 'message_target_not_email';
|
||||||
|
public const MESSAGE_TARGET_NOT_SMS = 'message_target_not_sms';
|
||||||
|
public const MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push';
|
||||||
|
|
||||||
|
|
||||||
protected string $type = '';
|
protected string $type = '';
|
||||||
protected array $errors = [];
|
protected array $errors = [];
|
||||||
protected bool $publish = true;
|
protected bool $publish = true;
|
||||||
|
|
|
@ -34,7 +34,7 @@ class V15 extends Migration
|
||||||
['email', 'anonymous'],
|
['email', 'anonymous'],
|
||||||
\array_map(
|
\array_map(
|
||||||
fn ($value) => "oauth-" . $value,
|
fn ($value) => "oauth-" . $value,
|
||||||
\array_keys(Config::getParam('providers', []))
|
\array_keys(Config::getParam('oAuthProviders', []))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -124,23 +124,23 @@ class V16 extends Migration
|
||||||
/**
|
/**
|
||||||
* Enable OAuth providers with data
|
* Enable OAuth providers with data
|
||||||
*/
|
*/
|
||||||
$authProviders = $document->getAttribute('authProviders', []);
|
$oAuthProviders = $document->getAttribute('oAuthProviders', []);
|
||||||
|
|
||||||
foreach (Config::getParam('providers') as $provider => $value) {
|
foreach (Config::getParam('oAuthProviders') as $provider => $value) {
|
||||||
if (!$value['enabled']) {
|
if (!$value['enabled']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($authProviders[$provider . 'Appid'] ?? false) && ($authProviders[$provider . 'Secret'] ?? false)) {
|
if (($oAuthProviders[$provider . 'Appid'] ?? false) && ($oAuthProviders[$provider . 'Secret'] ?? false)) {
|
||||||
if (array_key_exists($provider . 'Enabled', $authProviders)) {
|
if (array_key_exists($provider . 'Enabled', $oAuthProviders)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$authProviders[$provider . 'Enabled'] = true;
|
$oAuthProviders[$provider . 'Enabled'] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->setAttribute('authProviders', $authProviders);
|
$document->setAttribute('oAuthProviders', $oAuthProviders);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ use Appwrite\Platform\Workers\Databases;
|
||||||
use Appwrite\Platform\Workers\Functions;
|
use Appwrite\Platform\Workers\Functions;
|
||||||
use Appwrite\Platform\Workers\Builds;
|
use Appwrite\Platform\Workers\Builds;
|
||||||
use Appwrite\Platform\Workers\Deletes;
|
use Appwrite\Platform\Workers\Deletes;
|
||||||
|
use Appwrite\Platform\Workers\Hamster;
|
||||||
use Appwrite\Platform\Workers\Migrations;
|
use Appwrite\Platform\Workers\Migrations;
|
||||||
|
|
||||||
class Workers extends Service
|
class Workers extends Service
|
||||||
|
@ -30,6 +31,7 @@ class Workers extends Service
|
||||||
->addAction(Builds::getName(), new Builds())
|
->addAction(Builds::getName(), new Builds())
|
||||||
->addAction(Deletes::getName(), new Deletes())
|
->addAction(Deletes::getName(), new Deletes())
|
||||||
->addAction(Migrations::getName(), new Migrations())
|
->addAction(Migrations::getName(), new Migrations())
|
||||||
|
->addAction(Hamster::getName(), new Hamster())
|
||||||
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,45 +2,17 @@
|
||||||
|
|
||||||
namespace Appwrite\Platform\Tasks;
|
namespace Appwrite\Platform\Tasks;
|
||||||
|
|
||||||
use Appwrite\Network\Validator\Origin;
|
use Appwrite\Event\Hamster as EventHamster;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Platform\Action;
|
||||||
use Utopia\Cache\Cache;
|
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Query;
|
use Utopia\Database\Query;
|
||||||
use Utopia\Database\Validator\Authorization;
|
|
||||||
use Utopia\Analytics\Adapter\Mixpanel;
|
|
||||||
use Utopia\Analytics\Event;
|
|
||||||
use Utopia\Config\Config;
|
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Pools\Group;
|
|
||||||
|
|
||||||
class Hamster extends Action
|
class Hamster extends Action
|
||||||
{
|
{
|
||||||
private array $metrics = [
|
|
||||||
'usage_files' => 'files.$all.count.total',
|
|
||||||
'usage_buckets' => 'buckets.$all.count.total',
|
|
||||||
'usage_databases' => 'databases.$all.count.total',
|
|
||||||
'usage_documents' => 'documents.$all.count.total',
|
|
||||||
'usage_collections' => 'collections.$all.count.total',
|
|
||||||
'usage_storage' => 'project.$all.storage.size',
|
|
||||||
'usage_requests' => 'project.$all.network.requests',
|
|
||||||
'usage_bandwidth' => 'project.$all.network.bandwidth',
|
|
||||||
'usage_users' => 'users.$all.count.total',
|
|
||||||
'usage_sessions' => 'sessions.email.requests.create',
|
|
||||||
'usage_executions' => 'executions.$all.compute.total',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected string $directory = '/usr/local';
|
|
||||||
|
|
||||||
protected string $path;
|
|
||||||
|
|
||||||
protected string $date;
|
|
||||||
|
|
||||||
protected Mixpanel $mixpanel;
|
|
||||||
|
|
||||||
public static function getName(): string
|
public static function getName(): string
|
||||||
{
|
{
|
||||||
return 'hamster';
|
return 'hamster';
|
||||||
|
@ -48,266 +20,31 @@ class Hamster extends Action
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', ''));
|
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->desc('Get stats for projects')
|
->desc('Get stats for projects')
|
||||||
->inject('pools')
|
->inject('queueForHamster')
|
||||||
->inject('cache')
|
|
||||||
->inject('dbForConsole')
|
->inject('dbForConsole')
|
||||||
->callback(function (Group $pools, Cache $cache, Database $dbForConsole) {
|
->callback(function (EventHamster $queueForHamster, Database $dbForConsole) {
|
||||||
$this->action($pools, $cache, $dbForConsole);
|
$this->action($queueForHamster, $dbForConsole);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getStatsPerProject(Group $pools, Cache $cache, Database $dbForConsole)
|
public function action(EventHamster $queueForHamster, Database $dbForConsole): void
|
||||||
{
|
{
|
||||||
$this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools, $cache) {
|
|
||||||
/**
|
|
||||||
* Skip user projects with id 'console'
|
|
||||||
*/
|
|
||||||
if ($project->getId() === 'console') {
|
|
||||||
Console::info("Skipping project console");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console::log("Getting stats for {$project->getId()}");
|
|
||||||
|
|
||||||
try {
|
|
||||||
$db = $project->getAttribute('database');
|
|
||||||
$adapter = $pools
|
|
||||||
->get($db)
|
|
||||||
->pop()
|
|
||||||
->getResource();
|
|
||||||
|
|
||||||
$dbForProject = new Database($adapter, $cache);
|
|
||||||
$dbForProject->setDefaultDatabase('appwrite');
|
|
||||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
|
||||||
|
|
||||||
$statsPerProject = [];
|
|
||||||
|
|
||||||
$statsPerProject['time'] = microtime(true);
|
|
||||||
|
|
||||||
/** Get Project ID */
|
|
||||||
$statsPerProject['project_id'] = $project->getId();
|
|
||||||
|
|
||||||
/** Get project created time */
|
|
||||||
$statsPerProject['project_created'] = $project->getAttribute('$createdAt');
|
|
||||||
|
|
||||||
/** Get Project Name */
|
|
||||||
$statsPerProject['project_name'] = $project->getAttribute('name');
|
|
||||||
|
|
||||||
/** Total Project Variables */
|
|
||||||
$statsPerProject['custom_variables'] = $dbForProject->count('variables', [], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Total Migrations */
|
|
||||||
$statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get Custom SMTP */
|
|
||||||
$smtp = $project->getAttribute('smtp', null);
|
|
||||||
if ($smtp) {
|
|
||||||
$statsPerProject['custom_smtp_status'] = $smtp['enabled'] === true ? 'enabled' : 'disabled';
|
|
||||||
|
|
||||||
/** Get Custom Templates Count */
|
|
||||||
$templates = array_keys($project->getAttribute('templates', []));
|
|
||||||
$statsPerProject['custom_email_templates'] = array_filter($templates, function ($template) {
|
|
||||||
return str_contains($template, 'email');
|
|
||||||
});
|
|
||||||
$statsPerProject['custom_sms_templates'] = array_filter($templates, function ($template) {
|
|
||||||
return str_contains($template, 'sms');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get total relationship attributes */
|
|
||||||
$statsPerProject['custom_relationship_attributes'] = $dbForProject->count('attributes', [
|
|
||||||
Query::equal('type', ['relationship'])
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get Total Functions */
|
|
||||||
$statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
foreach (\array_keys(Config::getParam('runtimes')) as $runtime) {
|
|
||||||
$statsPerProject['custom_functions_' . $runtime] = $dbForProject->count('functions', [
|
|
||||||
Query::equal('runtime', [$runtime]),
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get Total Deployments */
|
|
||||||
$statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
|
|
||||||
$statsPerProject['custom_deployments_manual'] = $dbForProject->count('deployments', [
|
|
||||||
Query::equal('type', ['manual'])
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
$statsPerProject['custom_deployments_git'] = $dbForProject->count('deployments', [
|
|
||||||
Query::equal('type', ['vcs'])
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get VCS repos connected */
|
|
||||||
$statsPerProject['custom_vcs_repositories'] = $dbForConsole->count('repositories', [
|
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get Total Teams */
|
|
||||||
$statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get Total Migrations */
|
|
||||||
$statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT);
|
|
||||||
|
|
||||||
/** Get Total Members */
|
|
||||||
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
|
||||||
if ($teamInternalId) {
|
|
||||||
$statsPerProject['custom_organization_members'] = $dbForConsole->count('memberships', [
|
|
||||||
Query::equal('teamInternalId', [$teamInternalId])
|
|
||||||
], APP_LIMIT_COUNT);
|
|
||||||
} else {
|
|
||||||
$statsPerProject['custom_organization_members'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get Email and Name of the project owner */
|
|
||||||
if ($teamInternalId) {
|
|
||||||
$membership = $dbForConsole->findOne('memberships', [
|
|
||||||
Query::equal('teamInternalId', [$teamInternalId]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!$membership || $membership->isEmpty()) {
|
|
||||||
throw new Exception('Membership not found. Skipping project : ' . $project->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$userId = $membership->getAttribute('userId', null);
|
|
||||||
if ($userId) {
|
|
||||||
$user = $dbForConsole->getDocument('users', $userId);
|
|
||||||
$statsPerProject['email'] = $user->getAttribute('email', null);
|
|
||||||
$statsPerProject['name'] = $user->getAttribute('name', null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get Domains */
|
|
||||||
$statsPerProject['custom_domains'] = $dbForConsole->count('rules', [
|
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
|
||||||
Query::limit(APP_LIMIT_COUNT)
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Get Platforms */
|
|
||||||
$platforms = $dbForConsole->find('platforms', [
|
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
|
||||||
Query::limit(APP_LIMIT_COUNT)
|
|
||||||
]);
|
|
||||||
|
|
||||||
$statsPerProject['custom_platforms_web'] = sizeof(array_filter($platforms, function ($platform) {
|
|
||||||
return $platform['type'] === 'web';
|
|
||||||
}));
|
|
||||||
|
|
||||||
$statsPerProject['custom_platforms_android'] = sizeof(array_filter($platforms, function ($platform) {
|
|
||||||
return $platform['type'] === 'android';
|
|
||||||
}));
|
|
||||||
|
|
||||||
$statsPerProject['custom_platforms_apple'] = sizeof(array_filter($platforms, function ($platform) {
|
|
||||||
return str_contains($platform['type'], 'apple');
|
|
||||||
}));
|
|
||||||
|
|
||||||
$statsPerProject['custom_platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) {
|
|
||||||
return str_contains($platform['type'], 'flutter');
|
|
||||||
}));
|
|
||||||
|
|
||||||
$flutterPlatforms = [Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_FLUTTER_LINUX];
|
|
||||||
|
|
||||||
foreach ($flutterPlatforms as $flutterPlatform) {
|
|
||||||
$statsPerProject['custom_platforms_' . $flutterPlatform] = sizeof(array_filter($platforms, function ($platform) use ($flutterPlatform) {
|
|
||||||
return $platform['type'] === $flutterPlatform;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
$statsPerProject['custom_platforms_api_keys'] = $dbForConsole->count('keys', [
|
|
||||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
|
||||||
Query::limit(APP_LIMIT_COUNT)
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Get Usage $statsPerProject */
|
|
||||||
$periods = [
|
|
||||||
'infinity' => [
|
|
||||||
'period' => '1d',
|
|
||||||
'limit' => 90,
|
|
||||||
],
|
|
||||||
'24h' => [
|
|
||||||
'period' => '1h',
|
|
||||||
'limit' => 24,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) {
|
|
||||||
foreach ($this->metrics as $key => $metric) {
|
|
||||||
foreach ($periods as $periodKey => $periodValue) {
|
|
||||||
$limit = $periodValue['limit'];
|
|
||||||
$period = $periodValue['period'];
|
|
||||||
|
|
||||||
$requestDocs = $dbForProject->find('stats', [
|
|
||||||
Query::equal('period', [$period]),
|
|
||||||
Query::equal('metric', [$metric]),
|
|
||||||
Query::limit($limit),
|
|
||||||
Query::orderDesc('time'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$statsPerProject[$key . '_' . $periodKey] = [];
|
|
||||||
foreach ($requestDocs as $requestDoc) {
|
|
||||||
$statsPerProject[$key . '_' . $periodKey][] = [
|
|
||||||
'value' => $requestDoc->getAttribute('value'),
|
|
||||||
'date' => $requestDoc->getAttribute('time'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$statsPerProject[$key . '_' . $periodKey] = array_reverse($statsPerProject[$key . '_' . $periodKey]);
|
|
||||||
// Calculate aggregate of each metric
|
|
||||||
$statsPerProject[$key . '_' . $periodKey] = array_sum(array_column($statsPerProject[$key . '_' . $periodKey], 'value'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isset($statsPerProject['email'])) {
|
|
||||||
/** Send data to mixpanel */
|
|
||||||
$res = $this->mixpanel->createProfile($statsPerProject['email'], '', [
|
|
||||||
'name' => $statsPerProject['name'],
|
|
||||||
'email' => $statsPerProject['email']
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!$res) {
|
|
||||||
Console::error('Failed to create user profile for project: ' . $project->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$event = new Event();
|
|
||||||
$event
|
|
||||||
->setName('Project Daily Usage')
|
|
||||||
->setProps($statsPerProject);
|
|
||||||
$res = $this->mixpanel->createEvent($event);
|
|
||||||
|
|
||||||
if (!$res) {
|
|
||||||
Console::error('Failed to create event for project: ' . $project->getId());
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Console::error('Failed to send stats for project: ' . $project->getId());
|
|
||||||
Console::error($e->getMessage());
|
|
||||||
} finally {
|
|
||||||
$pools
|
|
||||||
->get($db)
|
|
||||||
->reclaim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function action(Group $pools, Cache $cache, Database $dbForConsole): void
|
|
||||||
{
|
|
||||||
|
|
||||||
Console::title('Cloud Hamster V1');
|
Console::title('Cloud Hamster V1');
|
||||||
Console::success(APP_NAME . ' cloud hamster process has started');
|
Console::success(APP_NAME . ' cloud hamster process has started');
|
||||||
|
|
||||||
$sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
$sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
||||||
|
|
||||||
$jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes)
|
$jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes)
|
||||||
|
|
||||||
$now = new \DateTime();
|
$now = new \DateTime();
|
||||||
$now->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
$now->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||||
|
|
||||||
$next = new \DateTime($now->format("Y-m-d $jobInitTime"));
|
$next = new \DateTime($now->format("Y-m-d $jobInitTime"));
|
||||||
$next->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
$next->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||||
$delay = $next->getTimestamp() - $now->getTimestamp();
|
|
||||||
|
|
||||||
|
$delay = $next->getTimestamp() - $now->getTimestamp();
|
||||||
/**
|
/**
|
||||||
* If time passed for the target day.
|
* If time passed for the target day.
|
||||||
*/
|
*/
|
||||||
|
@ -318,29 +55,22 @@ class Hamster extends Action
|
||||||
|
|
||||||
Console::log('[' . $now->format("Y-m-d H:i:s.v") . '] Delaying for ' . $delay . ' setting loop to [' . $next->format("Y-m-d H:i:s.v") . ']');
|
Console::log('[' . $now->format("Y-m-d H:i:s.v") . '] Delaying for ' . $delay . ' setting loop to [' . $next->format("Y-m-d H:i:s.v") . ']');
|
||||||
|
|
||||||
Console::loop(function () use ($pools, $cache, $dbForConsole, $sleep) {
|
Console::loop(function () use ($queueForHamster, $dbForConsole, $sleep) {
|
||||||
$now = date('d-m-Y H:i:s', time());
|
$now = date('d-m-Y H:i:s', time());
|
||||||
Console::info("[{$now}] Getting Cloud Usage Stats every {$sleep} seconds");
|
Console::info("[{$now}] Queuing Cloud Usage Stats every {$sleep} seconds");
|
||||||
$loopStart = microtime(true);
|
$loopStart = microtime(true);
|
||||||
|
|
||||||
/* Initialise new Utopia app */
|
Console::info('Queuing stats for all projects');
|
||||||
$app = new App('UTC');
|
$this->getStatsPerProject($queueForHamster, $dbForConsole, $loopStart);
|
||||||
|
Console::success('Completed queuing stats for all projects');
|
||||||
|
|
||||||
Console::info('Getting stats for all projects');
|
Console::info('Queuing stats for all organizations');
|
||||||
$this->getStatsPerProject($pools, $cache, $dbForConsole);
|
$this->getStatsPerOrganization($queueForHamster, $dbForConsole, $loopStart);
|
||||||
Console::success('Completed getting stats for all projects');
|
Console::success('Completed queuing stats for all organizations');
|
||||||
|
|
||||||
Console::info('Getting stats for all organizations');
|
Console::info('Queuing stats for all users');
|
||||||
$this->getStatsPerOrganization($dbForConsole);
|
$this->getStatsPerUser($queueForHamster, $dbForConsole, $loopStart);
|
||||||
Console::success('Completed getting stats for all organizations');
|
Console::success('Completed queuing stats for all users');
|
||||||
|
|
||||||
Console::info('Getting stats for all users');
|
|
||||||
$this->getStatsPerUser($dbForConsole);
|
|
||||||
Console::success('Completed getting stats for all users');
|
|
||||||
|
|
||||||
$pools
|
|
||||||
->get('console')
|
|
||||||
->reclaim();
|
|
||||||
|
|
||||||
$loopTook = microtime(true) - $loopStart;
|
$loopTook = microtime(true) - $loopStart;
|
||||||
$now = date('d-m-Y H:i:s', time());
|
$now = date('d-m-Y H:i:s', time());
|
||||||
|
@ -348,7 +78,7 @@ class Hamster extends Action
|
||||||
}, $sleep, $delay);
|
}, $sleep, $delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function calculateByGroup(string $collection, Database $dbForConsole, callable $callback)
|
protected function calculateByGroup(string $collection, Database $database, callable $callback)
|
||||||
{
|
{
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$chunk = 0;
|
$chunk = 0;
|
||||||
|
@ -361,7 +91,7 @@ class Hamster extends Action
|
||||||
while ($sum === $limit) {
|
while ($sum === $limit) {
|
||||||
$chunk++;
|
$chunk++;
|
||||||
|
|
||||||
$results = $dbForConsole->find($collection, \array_merge([
|
$results = $database->find($collection, \array_merge([
|
||||||
Query::limit($limit),
|
Query::limit($limit),
|
||||||
Query::offset($count)
|
Query::offset($count)
|
||||||
]));
|
]));
|
||||||
|
@ -371,7 +101,7 @@ class Hamster extends Action
|
||||||
Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents');
|
Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents');
|
||||||
|
|
||||||
foreach ($results as $document) {
|
foreach ($results as $document) {
|
||||||
call_user_func($callback, $dbForConsole, $document);
|
call_user_func($callback, $database, $document);
|
||||||
$count++;
|
$count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,96 +111,45 @@ class Hamster extends Action
|
||||||
Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getStatsPerOrganization(Database $dbForConsole)
|
protected function getStatsPerOrganization(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||||
{
|
{
|
||||||
|
$this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($hamster, $loopStart) {
|
||||||
$this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $document) {
|
|
||||||
try {
|
try {
|
||||||
$statsPerOrganization = [];
|
$organization->setAttribute('$time', $loopStart);
|
||||||
|
$hamster
|
||||||
/** Organization name */
|
->setType(EventHamster::TYPE_ORGANISATION)
|
||||||
$statsPerOrganization['name'] = $document->getAttribute('name');
|
->setOrganization($organization)
|
||||||
|
->trigger();
|
||||||
/** Get Email and of the organization owner */
|
|
||||||
$membership = $dbForConsole->findOne('memberships', [
|
|
||||||
Query::equal('teamInternalId', [$document->getInternalId()]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!$membership || $membership->isEmpty()) {
|
|
||||||
throw new Exception('Membership not found. Skipping organization : ' . $document->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$userId = $membership->getAttribute('userId', null);
|
|
||||||
if ($userId) {
|
|
||||||
$user = $dbForConsole->getDocument('users', $userId);
|
|
||||||
$statsPerOrganization['email'] = $user->getAttribute('email', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Organization Creation Date */
|
|
||||||
$statsPerOrganization['created'] = $document->getAttribute('$createdAt');
|
|
||||||
|
|
||||||
/** Number of team members */
|
|
||||||
$statsPerOrganization['members'] = $document->getAttribute('total');
|
|
||||||
|
|
||||||
/** Number of projects in this organization */
|
|
||||||
$statsPerOrganization['projects'] = $dbForConsole->count('projects', [
|
|
||||||
Query::equal('teamId', [$document->getId()]),
|
|
||||||
Query::limit(APP_LIMIT_COUNT)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!isset($statsPerOrganization['email'])) {
|
|
||||||
throw new Exception('Email not found. Skipping organization : ' . $document->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
$event = new Event();
|
|
||||||
$event
|
|
||||||
->setName('Organization Daily Usage')
|
|
||||||
->setProps($statsPerOrganization);
|
|
||||||
$res = $this->mixpanel->createEvent($event);
|
|
||||||
if (!$res) {
|
|
||||||
throw new Exception('Failed to create event for organization : ' . $document->getId());
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Console::error($e->getMessage());
|
Console::error($e->getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getStatsPerUser(Database $dbForConsole)
|
private function getStatsPerProject(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||||
{
|
{
|
||||||
$this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $document) {
|
$this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($hamster, $loopStart) {
|
||||||
try {
|
try {
|
||||||
$statsPerUser = [];
|
$project->setAttribute('$time', $loopStart);
|
||||||
|
$hamster
|
||||||
/** Organization name */
|
->setType(EventHamster::TYPE_PROJECT)
|
||||||
$statsPerUser['name'] = $document->getAttribute('name');
|
->setProject($project)
|
||||||
|
->trigger();
|
||||||
/** Organization ID (needs to be stored as an email since mixpanel uses the email attribute as a distinctID) */
|
} catch (Exception $e) {
|
||||||
$statsPerUser['email'] = $document->getAttribute('email');
|
Console::error($e->getMessage());
|
||||||
|
}
|
||||||
/** Organization Creation Date */
|
});
|
||||||
$statsPerUser['created'] = $document->getAttribute('$createdAt');
|
|
||||||
|
|
||||||
/** Number of teams this user is a part of */
|
|
||||||
$statsPerUser['memberships'] = $dbForConsole->count('memberships', [
|
|
||||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
|
||||||
Query::limit(APP_LIMIT_COUNT)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!isset($statsPerUser['email'])) {
|
|
||||||
throw new Exception('User has no email: ' . $document->getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send data to mixpanel */
|
protected function getStatsPerUser(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||||
$event = new Event();
|
{
|
||||||
$event
|
$this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($hamster, $loopStart) {
|
||||||
->setName('User Daily Usage')
|
try {
|
||||||
->setProps($statsPerUser);
|
$user->setAttribute('$time', $loopStart);
|
||||||
$res = $this->mixpanel->createEvent($event);
|
$hamster
|
||||||
|
->setType(EventHamster::TYPE_USER)
|
||||||
if (!$res) {
|
->setUser($user)
|
||||||
throw new Exception('Failed to create user profile for user: ' . $document->getId());
|
->trigger();
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Console::error($e->getMessage());
|
Console::error($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,6 +235,11 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
'X-Appwrite-Response-Format' => '1.4.0',
|
'X-Appwrite-Response-Format' => '1.4.0',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Make sure we have a clean slate.
|
||||||
|
// Otherwise, all files in this dir will be pushed,
|
||||||
|
// regardless of whether they were just generated or not.
|
||||||
|
\exec('rm -rf ' . $result);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$sdk->generate($result);
|
$sdk->generate($result);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
|
|
|
@ -155,6 +155,9 @@ class Deletes extends Action
|
||||||
case DELETE_TYPE_SCHEDULES:
|
case DELETE_TYPE_SCHEDULES:
|
||||||
$this->deleteSchedules($dbForConsole, $getProjectDB, $datetime);
|
$this->deleteSchedules($dbForConsole, $getProjectDB, $datetime);
|
||||||
break;
|
break;
|
||||||
|
case DELETE_TYPE_TOPIC:
|
||||||
|
$this->deleteTopic($project, $getProjectDB, $document);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Console::error('No delete operation for type: ' . $type);
|
Console::error('No delete operation for type: ' . $type);
|
||||||
break;
|
break;
|
||||||
|
@ -199,6 +202,25 @@ class Deletes extends Action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Document $project
|
||||||
|
* @param callable $getProjectDB
|
||||||
|
* @param Document $topic
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function deleteTopic(Document $project, callable $getProjectDB, Document $topic)
|
||||||
|
{
|
||||||
|
if ($topic->isEmpty()) {
|
||||||
|
Console::error('Failed to delete subscribers. Topic not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dbForProject = $getProjectDB($project);
|
||||||
|
|
||||||
|
$this->deleteByGroup('subscribers', [
|
||||||
|
Query::equal('topicInternalId', [$topic->getInternalId()])
|
||||||
|
], $dbForProject);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Document $project
|
* @param Document $project
|
||||||
* @param callable $getProjectDB
|
* @param callable $getProjectDB
|
||||||
|
@ -539,6 +561,11 @@ class Deletes extends Action
|
||||||
$this->deleteByGroup('identities', [
|
$this->deleteByGroup('identities', [
|
||||||
Query::equal('userInternalId', [$userInternalId])
|
Query::equal('userInternalId', [$userInternalId])
|
||||||
], $dbForProject);
|
], $dbForProject);
|
||||||
|
|
||||||
|
// Delete targets
|
||||||
|
$this->deleteByGroup('targets', [
|
||||||
|
Query::equal('userInternalId', [$userInternalId])
|
||||||
|
], $dbForProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
437
src/Appwrite/Platform/Workers/Hamster.php
Normal file
437
src/Appwrite/Platform/Workers/Hamster.php
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Platform\Workers;
|
||||||
|
|
||||||
|
use Appwrite\Event\Hamster as EventHamster;
|
||||||
|
use Appwrite\Network\Validator\Origin;
|
||||||
|
use Utopia\Analytics\Adapter\Mixpanel;
|
||||||
|
use Utopia\Analytics\Event as AnalyticsEvent;
|
||||||
|
use Utopia\App;
|
||||||
|
use Utopia\Cache\Cache;
|
||||||
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Config\Config;
|
||||||
|
use Utopia\Platform\Action;
|
||||||
|
use Utopia\Database\Database;
|
||||||
|
use Utopia\Database\Query;
|
||||||
|
use Utopia\Database\Validator\Authorization;
|
||||||
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Queue\Message;
|
||||||
|
use Utopia\Logger\Log;
|
||||||
|
use Utopia\Pools\Group;
|
||||||
|
|
||||||
|
class Hamster extends Action
|
||||||
|
{
|
||||||
|
private array $metrics = [
|
||||||
|
'usage_files' => 'files.$all.count.total',
|
||||||
|
'usage_buckets' => 'buckets.$all.count.total',
|
||||||
|
'usage_databases' => 'databases.$all.count.total',
|
||||||
|
'usage_documents' => 'documents.$all.count.total',
|
||||||
|
'usage_collections' => 'collections.$all.count.total',
|
||||||
|
'usage_storage' => 'project.$all.storage.size',
|
||||||
|
'usage_requests' => 'project.$all.network.requests',
|
||||||
|
'usage_bandwidth' => 'project.$all.network.bandwidth',
|
||||||
|
'usage_users' => 'users.$all.count.total',
|
||||||
|
'usage_sessions' => 'sessions.email.requests.create',
|
||||||
|
'usage_executions' => 'executions.$all.compute.total',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected Mixpanel $mixpanel;
|
||||||
|
|
||||||
|
public static function getName(): string
|
||||||
|
{
|
||||||
|
return 'hamster';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->desc('Hamster worker')
|
||||||
|
->inject('message')
|
||||||
|
->inject('pools')
|
||||||
|
->inject('cache')
|
||||||
|
->inject('dbForConsole')
|
||||||
|
->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole) => $this->action($message, $pools, $cache, $dbForConsole));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Message $message
|
||||||
|
* @param Group $pools
|
||||||
|
* @param Cache $cache
|
||||||
|
* @param Database $dbForConsole
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Utopia\Database\Exception
|
||||||
|
*/
|
||||||
|
public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole): void
|
||||||
|
{
|
||||||
|
$token = App::getEnv('_APP_MIXPANEL_TOKEN', '');
|
||||||
|
if (empty($token)) {
|
||||||
|
throw new \Exception('Missing MixPanel Token');
|
||||||
|
}
|
||||||
|
$this->mixpanel = new Mixpanel($token);
|
||||||
|
|
||||||
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
|
if (empty($payload)) {
|
||||||
|
throw new \Exception('Missing payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $payload['type'] ?? '';
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case EventHamster::TYPE_PROJECT:
|
||||||
|
$this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole);
|
||||||
|
break;
|
||||||
|
case EventHamster::TYPE_ORGANISATION:
|
||||||
|
$this->getStatsForOrganization(new Document($payload['organization']), $dbForConsole);
|
||||||
|
break;
|
||||||
|
case EventHamster::TYPE_USER:
|
||||||
|
$this->getStatsPerUser(new Document($payload['user']), $dbForConsole);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Document $project
|
||||||
|
* @param Group $pools
|
||||||
|
* @param Cache $cache
|
||||||
|
* @param Database $dbForConsole
|
||||||
|
* @throws \Utopia\Database\Exception
|
||||||
|
*/
|
||||||
|
private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Skip user projects with id 'console'
|
||||||
|
*/
|
||||||
|
if ($project->getId() === 'console') {
|
||||||
|
Console::info("Skipping project console");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::log("Getting stats for Project {$project->getId()}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = $project->getAttribute('database');
|
||||||
|
$adapter = $pools
|
||||||
|
->get($db)
|
||||||
|
->pop()
|
||||||
|
->getResource();
|
||||||
|
|
||||||
|
$dbForProject = new Database($adapter, $cache);
|
||||||
|
$dbForProject->setDefaultDatabase('appwrite');
|
||||||
|
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||||
|
|
||||||
|
$statsPerProject = [];
|
||||||
|
|
||||||
|
$statsPerProject['time'] = $project->getAttribute('$time');
|
||||||
|
|
||||||
|
/** Get Project ID */
|
||||||
|
$statsPerProject['project_id'] = $project->getId();
|
||||||
|
|
||||||
|
/** Get project created time */
|
||||||
|
$statsPerProject['project_created'] = $project->getAttribute('$createdAt');
|
||||||
|
|
||||||
|
/** Get Project Name */
|
||||||
|
$statsPerProject['project_name'] = $project->getAttribute('name');
|
||||||
|
|
||||||
|
/** Total Project Variables */
|
||||||
|
$statsPerProject['custom_variables'] = $dbForProject->count('variables', [], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Total Migrations */
|
||||||
|
$statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Get Custom SMTP */
|
||||||
|
$smtp = $project->getAttribute('smtp', null);
|
||||||
|
if ($smtp) {
|
||||||
|
$statsPerProject['custom_smtp_status'] = $smtp['enabled'] === true ? 'enabled' : 'disabled';
|
||||||
|
|
||||||
|
/** Get Custom Templates Count */
|
||||||
|
$templates = array_keys($project->getAttribute('templates', []));
|
||||||
|
$statsPerProject['custom_email_templates'] = array_filter($templates, function ($template) {
|
||||||
|
return str_contains($template, 'email');
|
||||||
|
});
|
||||||
|
$statsPerProject['custom_sms_templates'] = array_filter($templates, function ($template) {
|
||||||
|
return str_contains($template, 'sms');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get total relationship attributes */
|
||||||
|
$statsPerProject['custom_relationship_attributes'] = $dbForProject->count('attributes', [
|
||||||
|
Query::equal('type', ['relationship'])
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Get Total Functions */
|
||||||
|
$statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
foreach (\array_keys(Config::getParam('runtimes')) as $runtime) {
|
||||||
|
$statsPerProject['custom_functions_' . $runtime] = $dbForProject->count('functions', [
|
||||||
|
Query::equal('runtime', [$runtime]),
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Total Deployments */
|
||||||
|
$statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
|
||||||
|
$statsPerProject['custom_deployments_manual'] = $dbForProject->count('deployments', [
|
||||||
|
Query::equal('type', ['manual'])
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
$statsPerProject['custom_deployments_git'] = $dbForProject->count('deployments', [
|
||||||
|
Query::equal('type', ['vcs'])
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Get VCS repos connected */
|
||||||
|
$statsPerProject['custom_vcs_repositories'] = $dbForConsole->count('repositories', [
|
||||||
|
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Get Total Teams */
|
||||||
|
$statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT);
|
||||||
|
|
||||||
|
/** Get Total Members */
|
||||||
|
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
||||||
|
if ($teamInternalId) {
|
||||||
|
$statsPerProject['custom_organization_members'] = $dbForConsole->count('memberships', [
|
||||||
|
Query::equal('teamInternalId', [$teamInternalId])
|
||||||
|
], APP_LIMIT_COUNT);
|
||||||
|
} else {
|
||||||
|
$statsPerProject['custom_organization_members'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Email and Name of the project owner */
|
||||||
|
if ($teamInternalId) {
|
||||||
|
$membership = $dbForConsole->findOne('memberships', [
|
||||||
|
Query::equal('teamInternalId', [$teamInternalId]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$membership || $membership->isEmpty()) {
|
||||||
|
throw new \Exception('Membership not found. Skipping project : ' . $project->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $membership->getAttribute('userId', null);
|
||||||
|
if ($userId) {
|
||||||
|
$user = $dbForConsole->getDocument('users', $userId);
|
||||||
|
$statsPerProject['email'] = $user->getAttribute('email', null);
|
||||||
|
$statsPerProject['name'] = $user->getAttribute('name', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Domains */
|
||||||
|
$statsPerProject['custom_domains'] = $dbForConsole->count('rules', [
|
||||||
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_COUNT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** Get Platforms */
|
||||||
|
$platforms = $dbForConsole->find('platforms', [
|
||||||
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_COUNT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$statsPerProject['custom_platforms_web'] = sizeof(array_filter($platforms, function ($platform) {
|
||||||
|
return $platform['type'] === 'web';
|
||||||
|
}));
|
||||||
|
|
||||||
|
$statsPerProject['custom_platforms_android'] = sizeof(array_filter($platforms, function ($platform) {
|
||||||
|
return $platform['type'] === 'android';
|
||||||
|
}));
|
||||||
|
|
||||||
|
$statsPerProject['custom_platforms_apple'] = sizeof(array_filter($platforms, function ($platform) {
|
||||||
|
return str_contains($platform['type'], 'apple');
|
||||||
|
}));
|
||||||
|
|
||||||
|
$statsPerProject['custom_platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) {
|
||||||
|
return str_contains($platform['type'], 'flutter');
|
||||||
|
}));
|
||||||
|
|
||||||
|
$flutterPlatforms = [Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_FLUTTER_LINUX];
|
||||||
|
|
||||||
|
foreach ($flutterPlatforms as $flutterPlatform) {
|
||||||
|
$statsPerProject['custom_platforms_' . $flutterPlatform] = sizeof(array_filter($platforms, function ($platform) use ($flutterPlatform) {
|
||||||
|
return $platform['type'] === $flutterPlatform;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
$statsPerProject['custom_platforms_api_keys'] = $dbForConsole->count('keys', [
|
||||||
|
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_COUNT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** Get Usage $statsPerProject */
|
||||||
|
$periods = [
|
||||||
|
'infinity' => [
|
||||||
|
'period' => '1d',
|
||||||
|
'limit' => 90,
|
||||||
|
],
|
||||||
|
'24h' => [
|
||||||
|
'period' => '1h',
|
||||||
|
'limit' => 24,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) {
|
||||||
|
foreach ($this->metrics as $key => $metric) {
|
||||||
|
foreach ($periods as $periodKey => $periodValue) {
|
||||||
|
$limit = $periodValue['limit'];
|
||||||
|
$period = $periodValue['period'];
|
||||||
|
|
||||||
|
$requestDocs = $dbForProject->find('stats', [
|
||||||
|
Query::equal('period', [$period]),
|
||||||
|
Query::equal('metric', [$metric]),
|
||||||
|
Query::limit($limit),
|
||||||
|
Query::orderDesc('time'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$statsPerProject[$key . '_' . $periodKey] = [];
|
||||||
|
foreach ($requestDocs as $requestDoc) {
|
||||||
|
$statsPerProject[$key . '_' . $periodKey][] = [
|
||||||
|
'value' => $requestDoc->getAttribute('value'),
|
||||||
|
'date' => $requestDoc->getAttribute('time'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$statsPerProject[$key . '_' . $periodKey] = array_reverse($statsPerProject[$key . '_' . $periodKey]);
|
||||||
|
// Calculate aggregate of each metric
|
||||||
|
$statsPerProject[$key . '_' . $periodKey] = array_sum(array_column($statsPerProject[$key . '_' . $periodKey], 'value'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isset($statsPerProject['email'])) {
|
||||||
|
/** Send data to mixpanel */
|
||||||
|
$res = $this->mixpanel->createProfile($statsPerProject['email'], '', [
|
||||||
|
'name' => $statsPerProject['name'],
|
||||||
|
'email' => $statsPerProject['email']
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$res) {
|
||||||
|
Console::error('Failed to create user profile for project: ' . $project->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$event = new AnalyticsEvent();
|
||||||
|
$event
|
||||||
|
->setName('Project Daily Usage')
|
||||||
|
->setProps($statsPerProject);
|
||||||
|
$res = $this->mixpanel->createEvent($event);
|
||||||
|
|
||||||
|
if (!$res) {
|
||||||
|
Console::error('Failed to create event for project: ' . $project->getId());
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Console::error('Failed to send stats for project: ' . $project->getId());
|
||||||
|
Console::error($e->getMessage());
|
||||||
|
} finally {
|
||||||
|
$pools
|
||||||
|
->get($db)
|
||||||
|
->reclaim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Document $organization
|
||||||
|
* @param Database $dbForConsole
|
||||||
|
* @throws \Utopia\Database\Exception
|
||||||
|
*/
|
||||||
|
private function getStatsForOrganization(Document $organization, Database $dbForConsole): void
|
||||||
|
{
|
||||||
|
Console::log("Getting stats for Organization {$organization->getId()}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$statsPerOrganization = [];
|
||||||
|
|
||||||
|
$statsPerOrganization['time'] = $organization->getAttribute('$time');
|
||||||
|
|
||||||
|
/** Organization name */
|
||||||
|
$statsPerOrganization['name'] = $organization->getAttribute('name');
|
||||||
|
|
||||||
|
|
||||||
|
/** Get Email and of the organization owner */
|
||||||
|
$membership = $dbForConsole->findOne('memberships', [
|
||||||
|
Query::equal('teamInternalId', [$organization->getInternalId()]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$membership || $membership->isEmpty()) {
|
||||||
|
throw new \Exception('Membership not found. Skipping organization : ' . $organization->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $membership->getAttribute('userId', null);
|
||||||
|
if ($userId) {
|
||||||
|
$user = $dbForConsole->getDocument('users', $userId);
|
||||||
|
$statsPerOrganization['email'] = $user->getAttribute('email', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Organization Creation Date */
|
||||||
|
$statsPerOrganization['created'] = $organization->getAttribute('$createdAt');
|
||||||
|
|
||||||
|
/** Number of team members */
|
||||||
|
$statsPerOrganization['members'] = $organization->getAttribute('total');
|
||||||
|
|
||||||
|
/** Number of projects in this organization */
|
||||||
|
$statsPerOrganization['projects'] = $dbForConsole->count('projects', [
|
||||||
|
Query::equal('teamId', [$organization->getId()]),
|
||||||
|
Query::limit(APP_LIMIT_COUNT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isset($statsPerOrganization['email'])) {
|
||||||
|
throw new \Exception('Email not found. Skipping organization : ' . $organization->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
$event = new AnalyticsEvent();
|
||||||
|
$event
|
||||||
|
->setName('Organization Daily Usage')
|
||||||
|
->setProps($statsPerOrganization);
|
||||||
|
$res = $this->mixpanel->createEvent($event);
|
||||||
|
if (!$res) {
|
||||||
|
throw new \Exception('Failed to create event for organization : ' . $organization->getId());
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Console::error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getStatsPerUser(Document $user, Database $dbForConsole)
|
||||||
|
{
|
||||||
|
Console::log("Getting stats for User {$user->getId()}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$statsPerUser = [];
|
||||||
|
|
||||||
|
$statsPerUser['time'] = $user->getAttribute('$time');
|
||||||
|
|
||||||
|
/** Organization name */
|
||||||
|
$statsPerUser['name'] = $user->getAttribute('name');
|
||||||
|
|
||||||
|
/** Organization ID (needs to be stored as an email since mixpanel uses the email attribute as a distinctID) */
|
||||||
|
$statsPerUser['email'] = $user->getAttribute('email');
|
||||||
|
|
||||||
|
/** Organization Creation Date */
|
||||||
|
$statsPerUser['created'] = $user->getAttribute('$createdAt');
|
||||||
|
|
||||||
|
/** Number of teams this user is a part of */
|
||||||
|
$statsPerUser['memberships'] = $dbForConsole->count('memberships', [
|
||||||
|
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||||
|
Query::limit(APP_LIMIT_COUNT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isset($statsPerUser['email'])) {
|
||||||
|
throw new \Exception('User has no email: ' . $user->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send data to mixpanel */
|
||||||
|
$event = new AnalyticsEvent();
|
||||||
|
$event
|
||||||
|
->setName('User Daily Usage')
|
||||||
|
->setProps($statsPerUser);
|
||||||
|
|
||||||
|
$res = $this->mixpanel->createEvent($event);
|
||||||
|
|
||||||
|
if (!$res) {
|
||||||
|
throw new \Exception('Failed to create user profile for user: ' . $user->getId());
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Console::error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,31 +2,42 @@
|
||||||
|
|
||||||
namespace Appwrite\Platform\Workers;
|
namespace Appwrite\Platform\Workers;
|
||||||
|
|
||||||
use Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Utopia\App;
|
use Utopia\App;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\DSN\DSN;
|
use Utopia\DSN\DSN;
|
||||||
use Utopia\Logger\Log;
|
use Utopia\Logger\Log;
|
||||||
use Utopia\Messaging\Messages\Sms;
|
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\Messaging\Adapters\SMS as SMSAdapter;
|
||||||
use Utopia\Messaging\Adapters\SMS\Mock;
|
use Utopia\Messaging\Adapters\SMS\Mock;
|
||||||
use Utopia\Messaging\Adapters\SMS\Msg91;
|
use Utopia\Messaging\Adapters\SMS\Msg91;
|
||||||
use Utopia\Messaging\Adapters\SMS\Telesign;
|
use Utopia\Messaging\Adapters\SMS\Telesign;
|
||||||
use Utopia\Messaging\Adapters\SMS\TextMagic;
|
use Utopia\Messaging\Adapters\SMS\Textmagic;
|
||||||
use Utopia\Messaging\Adapters\SMS\Twilio;
|
use Utopia\Messaging\Adapters\SMS\Twilio;
|
||||||
use Utopia\Messaging\Adapters\SMS\Vonage;
|
use Utopia\Messaging\Adapters\SMS\Vonage;
|
||||||
use Utopia\Platform\Action;
|
use Utopia\Messaging\Adapters\Push as PushAdapter;
|
||||||
use Utopia\Queue\Message;
|
use Utopia\Messaging\Adapters\Push\APNS;
|
||||||
|
use Utopia\Messaging\Adapters\Push\FCM;
|
||||||
|
use Utopia\Messaging\Adapters\Email as EmailAdapter;
|
||||||
|
use Utopia\Messaging\Adapters\Email\Mailgun;
|
||||||
|
use Utopia\Messaging\Adapters\Email\SendGrid;
|
||||||
|
use Utopia\Messaging\Messages\Email;
|
||||||
|
use Utopia\Messaging\Messages\Push;
|
||||||
|
use Utopia\Messaging\Messages\SMS;
|
||||||
|
|
||||||
|
use function Swoole\Coroutine\batch;
|
||||||
|
|
||||||
class Messaging extends Action
|
class Messaging extends Action
|
||||||
{
|
{
|
||||||
private ?DSN $dsn = null;
|
|
||||||
private string $user = '';
|
|
||||||
private string $secret = '';
|
|
||||||
private string $provider = '';
|
|
||||||
|
|
||||||
public static function getName(): string
|
public static function getName(): string
|
||||||
{
|
{
|
||||||
return 'messaging';
|
return "messaging";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,79 +45,342 @@ class Messaging extends Action
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->provider = App::getEnv('_APP_SMS_PROVIDER', '');
|
|
||||||
if (!empty($this->provider)) {
|
|
||||||
$this->dsn = new DSN($this->provider);
|
|
||||||
$this->user = $this->dsn->getUser();
|
|
||||||
$this->secret = $this->dsn->getPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->desc('Messaging worker')
|
->desc('Messaging worker')
|
||||||
->inject('message')
|
->inject('message')
|
||||||
->inject('log')
|
->inject('log')
|
||||||
->callback(fn(Message $message, Log $log) => $this->action($message, $log));
|
->inject('dbForProject')
|
||||||
|
->callback(fn(Message $message, Log $log, Database $dbForProject) => $this->action($message, $log, $dbForProject));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Message $message
|
* @param Message $message
|
||||||
* @param Log $log
|
* @param Log $log
|
||||||
|
* @param Database $dbForProject
|
||||||
* @return void
|
* @return void
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function action(Message $message, Log $log): void
|
public function action(Message $message, Log $log, Database $dbForProject): void
|
||||||
{
|
{
|
||||||
$payload = $message->getPayload() ?? [];
|
$payload = $message->getPayload() ?? [];
|
||||||
|
|
||||||
if (empty($payload)) {
|
if (empty($payload)) {
|
||||||
Console::error('Payload arg not found');
|
throw new \Exception('Skipped SMS processing. Payload is empty.');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($payload['recipient'])) {
|
if (!\is_null($payload['message']) && !\is_null($payload['recipients'])) {
|
||||||
Console::error('Recipient arg not found');
|
if ($payload['providerType'] === MESSAGE_TYPE_SMS) {
|
||||||
return;
|
$this->processInternalSMSMessage($log, new Document($payload['message']), $payload['recipients']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = $dbForProject->getDocument('messages', $payload['messageId']);
|
||||||
|
|
||||||
|
$this->processMessage($dbForProject, $message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($payload['message'])) {
|
private function processMessage(Database $dbForProject, Document $message): void
|
||||||
Console::error('Message arg not found');
|
{
|
||||||
return;
|
$topicsId = $message->getAttribute('topics', []);
|
||||||
|
$targetsId = $message->getAttribute('targets', []);
|
||||||
|
$usersId = $message->getAttribute('users', []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Document[] $recipients
|
||||||
|
*/
|
||||||
|
$recipients = [];
|
||||||
|
|
||||||
|
if (\count($topicsId) > 0) {
|
||||||
|
$topics = $dbForProject->find('topics', [Query::equal('$id', $topicsId)]);
|
||||||
|
foreach ($topics as $topic) {
|
||||||
|
$targets = \array_filter($topic->getAttribute('targets'), fn (Document $target) => $target->getAttribute('providerType') === $message->getAttribute('providerType'));
|
||||||
|
$recipients = \array_merge($recipients, $targets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$log->addTag('type', $this->dsn->getHost());
|
if (\count($usersId) > 0) {
|
||||||
|
$users = $dbForProject->find('users', [Query::equal('$id', $usersId)]);
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$targets = \array_filter($user->getAttribute('targets'), fn (Document $target) => $target->getAttribute('providerType') === $message->getAttribute('providerType'));
|
||||||
|
$recipients = \array_merge($recipients, $targets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$sms = match ($this->dsn->getHost()) {
|
if (\count($targetsId) > 0) {
|
||||||
'mock' => new Mock($this->user, $this->secret), // used for tests
|
$targets = $dbForProject->find('targets', [Query::equal('$id', $targetsId)]);
|
||||||
'twilio' => new Twilio($this->user, $this->secret),
|
$recipients = \array_merge($recipients, $targets);
|
||||||
'text-magic' => new TextMagic($this->user, $this->secret),
|
}
|
||||||
'telesign' => new Telesign($this->user, $this->secret),
|
|
||||||
'msg91' => new Msg91($this->user, $this->secret),
|
$primaryProvider = $dbForProject->findOne('providers', [
|
||||||
'vonage' => new Vonage($this->user, $this->secret),
|
Query::equal('enabled', [true]),
|
||||||
default => null
|
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 (!$providerId && $primaryProvider instanceof Document && !$primaryProvider->isEmpty()) {
|
||||||
|
$providerId = $primaryProvider->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($providerId) {
|
||||||
|
if (!isset($identifiersByProviderId[$providerId])) {
|
||||||
|
$identifiersByProviderId[$providerId] = [];
|
||||||
|
}
|
||||||
|
$identifiersByProviderId[$providerId][] = $recipient->getAttribute('identifier');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[] $results
|
||||||
|
*/
|
||||||
|
$results = batch(\array_map(function ($providerId) use ($identifiersByProviderId, $providers, $primaryProvider, $message, $dbForProject) {
|
||||||
|
return function () use ($providerId, $identifiersByProviderId, $providers, $primaryProvider, $message, $dbForProject) {
|
||||||
|
$provider = new Document();
|
||||||
|
|
||||||
|
if ($primaryProvider->getId() === $providerId) {
|
||||||
|
$provider = $primaryProvider;
|
||||||
|
} else {
|
||||||
|
$provider = $dbForProject->getDocument('providers', $providerId, [Query::equal('enabled', [true])]);
|
||||||
|
|
||||||
|
if ($provider->isEmpty()) {
|
||||||
|
$provider = $primaryProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$providers[] = $provider;
|
||||||
|
$identifiers = $identifiersByProviderId[$providerId];
|
||||||
|
|
||||||
|
$adapter = match ($provider->getAttribute('type')) {
|
||||||
|
MESSAGE_TYPE_SMS => $this->sms($provider),
|
||||||
|
MESSAGE_TYPE_PUSH => $this->push($provider),
|
||||||
|
MESSAGE_TYPE_EMAIL => $this->email($provider),
|
||||||
|
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
|
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
||||||
Console::error('Skipped sms processing. No Phone provider has been set.');
|
$batches = \array_chunk($identifiers, $maxBatchSize);
|
||||||
return;
|
$batchIndex = 0;
|
||||||
|
|
||||||
|
$results = batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex) {
|
||||||
|
return function () use ($batch, $message, $provider, $adapter, $batchIndex) {
|
||||||
|
$deliveredTotal = 0;
|
||||||
|
$deliveryErrors = [];
|
||||||
|
$messageData = clone $message;
|
||||||
|
$messageData->setAttribute('to', $batch);
|
||||||
|
|
||||||
|
$data = match ($provider->getAttribute('type')) {
|
||||||
|
MESSAGE_TYPE_SMS => $this->buildSMSMessage($messageData, $provider),
|
||||||
|
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
|
||||||
|
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($messageData, $provider),
|
||||||
|
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
$adapter->send($data);
|
||||||
|
$deliveredTotal += \count($batch);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage();
|
||||||
|
} finally {
|
||||||
|
$batchIndex++;
|
||||||
|
return [
|
||||||
|
'deliveredTotal' => $deliveredTotal,
|
||||||
|
'deliveryErrors' => $deliveryErrors,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}, $batches));
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
};
|
||||||
|
}, \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) {
|
||||||
|
$message->setAttribute('status', 'failed');
|
||||||
|
} 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(Log $log, Document $message, array $recipients): void
|
||||||
|
{
|
||||||
|
if (empty(App::getEnv('_APP_SMS_PROVIDER')) || empty(App::getEnv('_APP_SMS_FROM'))) {
|
||||||
|
throw new \Exception('Skipped SMS processing. No Phone configuration has been set.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$smsDSN = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
|
||||||
|
$host = $smsDSN->getHost();
|
||||||
|
$password = $smsDSN->getPassword();
|
||||||
|
$user = $smsDSN->getUser();
|
||||||
|
|
||||||
|
$log->addTag('type', $host);
|
||||||
|
|
||||||
$from = App::getEnv('_APP_SMS_FROM');
|
$from = App::getEnv('_APP_SMS_FROM');
|
||||||
|
|
||||||
if (empty($from)) {
|
$provider = new Document([
|
||||||
Console::error('Skipped sms processing. No phone number has been set.');
|
'$id' => ID::unique(),
|
||||||
return;
|
'provider' => $host,
|
||||||
}
|
'type' => MESSAGE_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
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
$message = new SMS(
|
$adapter = $this->sms($provider);
|
||||||
to: [$payload['recipient']],
|
|
||||||
content: $payload['message'],
|
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
|
||||||
from: $from,
|
$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 {
|
try {
|
||||||
$sms->send($message);
|
$adapter->send($data);
|
||||||
} catch (\Exception $error) {
|
} catch (\Exception $e) {
|
||||||
throw new Exception('Error sending message: ' . $error->getMessage(), 500);
|
Console::error('Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage()); // TODO: Find a way to log into Sentry
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}, $batches));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shutdown(): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sms(Document $provider): ?SMSAdapter
|
||||||
|
{
|
||||||
|
$credentials = $provider->getAttribute('credentials');
|
||||||
|
return match ($provider->getAttribute('provider')) {
|
||||||
|
'mock' => new Mock('username', 'password'),
|
||||||
|
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
|
||||||
|
'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']),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function push(Document $provider): ?PushAdapter
|
||||||
|
{
|
||||||
|
$credentials = $provider->getAttribute('credentials');
|
||||||
|
return match ($provider->getAttribute('provider')) {
|
||||||
|
'apns' => new APNS(
|
||||||
|
$credentials['authKey'],
|
||||||
|
$credentials['authKeyId'],
|
||||||
|
$credentials['teamId'],
|
||||||
|
$credentials['bundleId'],
|
||||||
|
$credentials['endpoint']
|
||||||
|
),
|
||||||
|
'fcm' => new FCM($credentials['serverKey']),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function email(Document $provider): ?EmailAdapter
|
||||||
|
{
|
||||||
|
$credentials = $provider->getAttribute('credentials');
|
||||||
|
return match ($provider->getAttribute('provider')) {
|
||||||
|
'mailgun' => new Mailgun($credentials['apiKey'], $credentials['domain'], $credentials['isEuRegion']),
|
||||||
|
'sendgrid' => new SendGrid($credentials['apiKey']),
|
||||||
|
default => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildEmailMessage(Document $message, Document $provider): Email
|
||||||
|
{
|
||||||
|
$from = $provider['options']['from'];
|
||||||
|
$to = $message['to'];
|
||||||
|
$subject = $message['data']['subject'];
|
||||||
|
$content = $message['data']['content'];
|
||||||
|
$html = $message['data']['html'];
|
||||||
|
|
||||||
|
return new Email($to, $subject, $content, $from, null, $html);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildSMSMessage(Document $message, Document $provider): SMS
|
||||||
|
{
|
||||||
|
$to = $message['to'];
|
||||||
|
$content = $message['data']['content'];
|
||||||
|
$from = $provider['options']['from'];
|
||||||
|
|
||||||
|
return new SMS($to, $content, $from);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPushMessage(Document $message): Push
|
||||||
|
{
|
||||||
|
$to = $message['to'];
|
||||||
|
$title = $message['data']['title'];
|
||||||
|
$body = $message['data']['body'];
|
||||||
|
$data = $message['data']['data'];
|
||||||
|
$action = $message['data']['action'];
|
||||||
|
$sound = $message['data']['sound'];
|
||||||
|
$icon = $message['data']['icon'];
|
||||||
|
$color = $message['data']['color'];
|
||||||
|
$tag = $message['data']['tag'];
|
||||||
|
$badge = $message['data']['badge'];
|
||||||
|
|
||||||
|
return new Push($to, $title, $body, $data, $action, $sound, $icon, $color, $tag, $badge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/Appwrite/Utopia/Database/Validator/Queries/Messages.php
Normal file
28
src/Appwrite/Utopia/Database/Validator/Queries/Messages.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||||
|
|
||||||
|
class Messages extends Base
|
||||||
|
{
|
||||||
|
public const ALLOWED_ATTRIBUTES = [
|
||||||
|
'topics',
|
||||||
|
'users',
|
||||||
|
'targets',
|
||||||
|
'providerId',
|
||||||
|
'deliveredAt',
|
||||||
|
'deliveredTo',
|
||||||
|
'deliveryErrors',
|
||||||
|
'status',
|
||||||
|
'description',
|
||||||
|
'data'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('messages', self::ALLOWED_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
22
src/Appwrite/Utopia/Database/Validator/Queries/Providers.php
Normal file
22
src/Appwrite/Utopia/Database/Validator/Queries/Providers.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||||
|
|
||||||
|
class Providers extends Base
|
||||||
|
{
|
||||||
|
public const ALLOWED_ATTRIBUTES = [
|
||||||
|
'name',
|
||||||
|
'provider',
|
||||||
|
'type',
|
||||||
|
'enabled',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('providers', self::ALLOWED_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||||
|
|
||||||
|
class Subscribers extends Base
|
||||||
|
{
|
||||||
|
public const ALLOWED_ATTRIBUTES = [
|
||||||
|
'targetId',
|
||||||
|
'topicId',
|
||||||
|
'userId',
|
||||||
|
'providerType'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('subscribers', self::ALLOWED_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
21
src/Appwrite/Utopia/Database/Validator/Queries/Targets.php
Normal file
21
src/Appwrite/Utopia/Database/Validator/Queries/Targets.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||||
|
|
||||||
|
class Targets extends Base
|
||||||
|
{
|
||||||
|
public const ALLOWED_ATTRIBUTES = [
|
||||||
|
'userId',
|
||||||
|
'providerId',
|
||||||
|
'identifier',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('targets', self::ALLOWED_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
20
src/Appwrite/Utopia/Database/Validator/Queries/Topics.php
Normal file
20
src/Appwrite/Utopia/Database/Validator/Queries/Topics.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||||
|
|
||||||
|
class Topics extends Base
|
||||||
|
{
|
||||||
|
public const ALLOWED_ATTRIBUTES = [
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('topics', self::ALLOWED_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||||
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
||||||
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
||||||
|
use Appwrite\Utopia\Response\Model\AuthProvider;
|
||||||
use Appwrite\Utopia\Response\Model\BaseList;
|
use Appwrite\Utopia\Response\Model\BaseList;
|
||||||
use Appwrite\Utopia\Response\Model\Branch;
|
use Appwrite\Utopia\Response\Model\Branch;
|
||||||
use Appwrite\Utopia\Response\Model\Collection;
|
use Appwrite\Utopia\Response\Model\Collection;
|
||||||
|
@ -80,8 +81,12 @@ use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||||
use Appwrite\Utopia\Response\Model\Installation;
|
use Appwrite\Utopia\Response\Model\Installation;
|
||||||
use Appwrite\Utopia\Response\Model\LocaleCode;
|
use Appwrite\Utopia\Response\Model\LocaleCode;
|
||||||
use Appwrite\Utopia\Response\Model\Provider;
|
use Appwrite\Utopia\Response\Model\Provider;
|
||||||
|
use Appwrite\Utopia\Response\Model\Message;
|
||||||
|
use Appwrite\Utopia\Response\Model\Subscriber;
|
||||||
|
use Appwrite\Utopia\Response\Model\Topic;
|
||||||
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
||||||
use Appwrite\Utopia\Response\Model\Runtime;
|
use Appwrite\Utopia\Response\Model\Runtime;
|
||||||
|
use Appwrite\Utopia\Response\Model\Target;
|
||||||
use Appwrite\Utopia\Response\Model\TemplateSMS;
|
use Appwrite\Utopia\Response\Model\TemplateSMS;
|
||||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||||
|
@ -191,6 +196,18 @@ class Response extends SwooleResponse
|
||||||
public const MODEL_PHONE = 'phone';
|
public const MODEL_PHONE = 'phone';
|
||||||
public const MODEL_PHONE_LIST = 'phoneList';
|
public const MODEL_PHONE_LIST = 'phoneList';
|
||||||
|
|
||||||
|
// Messaging
|
||||||
|
public const MODEL_PROVIDER = 'provider';
|
||||||
|
public const MODEL_PROVIDER_LIST = 'providerList';
|
||||||
|
public const MODEL_MESSAGE = 'message';
|
||||||
|
public const MODEL_MESSAGE_LIST = 'messageList';
|
||||||
|
public const MODEL_TOPIC = 'topic';
|
||||||
|
public const MODEL_TOPIC_LIST = 'topicList';
|
||||||
|
public const MODEL_SUBSCRIBER = 'subscriber';
|
||||||
|
public const MODEL_SUBSCRIBER_LIST = 'subscriberList';
|
||||||
|
public const MODEL_TARGET = 'target';
|
||||||
|
public const MODEL_TARGET_LIST = 'targetList';
|
||||||
|
|
||||||
// Teams
|
// Teams
|
||||||
public const MODEL_TEAM = 'team';
|
public const MODEL_TEAM = 'team';
|
||||||
public const MODEL_TEAM_LIST = 'teamList';
|
public const MODEL_TEAM_LIST = 'teamList';
|
||||||
|
@ -238,8 +255,8 @@ class Response extends SwooleResponse
|
||||||
public const MODEL_WEBHOOK_LIST = 'webhookList';
|
public const MODEL_WEBHOOK_LIST = 'webhookList';
|
||||||
public const MODEL_KEY = 'key';
|
public const MODEL_KEY = 'key';
|
||||||
public const MODEL_KEY_LIST = 'keyList';
|
public const MODEL_KEY_LIST = 'keyList';
|
||||||
public const MODEL_PROVIDER = 'provider';
|
public const MODEL_AUTH_PROVIDER = 'authProvider';
|
||||||
public const MODEL_PROVIDER_LIST = 'providerList';
|
public const MODEL_AUTH_PROVIDER_LIST = 'authProviderList';
|
||||||
public const MODEL_PLATFORM = 'platform';
|
public const MODEL_PLATFORM = 'platform';
|
||||||
public const MODEL_PLATFORM_LIST = 'platformList';
|
public const MODEL_PLATFORM_LIST = 'platformList';
|
||||||
public const MODEL_VARIABLE = 'variable';
|
public const MODEL_VARIABLE = 'variable';
|
||||||
|
@ -316,7 +333,7 @@ class Response extends SwooleResponse
|
||||||
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
|
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
|
||||||
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
|
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
|
||||||
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
|
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
|
||||||
->setModel(new BaseList('Providers List', self::MODEL_PROVIDER_LIST, 'platforms', self::MODEL_PROVIDER, true, false))
|
->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false))
|
||||||
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
|
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
|
||||||
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
|
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
|
||||||
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
|
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
|
||||||
|
@ -328,6 +345,11 @@ class Response extends SwooleResponse
|
||||||
->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS))
|
->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS))
|
||||||
->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE))
|
->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE))
|
||||||
->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE))
|
->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE))
|
||||||
|
->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER))
|
||||||
|
->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE))
|
||||||
|
->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC))
|
||||||
|
->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER))
|
||||||
|
->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET))
|
||||||
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
|
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
|
||||||
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
|
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
|
||||||
// Entities
|
// Entities
|
||||||
|
@ -380,7 +402,7 @@ class Response extends SwooleResponse
|
||||||
->setModel(new Project())
|
->setModel(new Project())
|
||||||
->setModel(new Webhook())
|
->setModel(new Webhook())
|
||||||
->setModel(new Key())
|
->setModel(new Key())
|
||||||
->setModel(new Provider())
|
->setModel(new AuthProvider())
|
||||||
->setModel(new Platform())
|
->setModel(new Platform())
|
||||||
->setModel(new Variable())
|
->setModel(new Variable())
|
||||||
->setModel(new Country())
|
->setModel(new Country())
|
||||||
|
@ -408,6 +430,11 @@ class Response extends SwooleResponse
|
||||||
->setModel(new TemplateSMS())
|
->setModel(new TemplateSMS())
|
||||||
->setModel(new TemplateEmail())
|
->setModel(new TemplateEmail())
|
||||||
->setModel(new ConsoleVariables())
|
->setModel(new ConsoleVariables())
|
||||||
|
->setModel(new Provider())
|
||||||
|
->setModel(new Message())
|
||||||
|
->setModel(new Topic())
|
||||||
|
->setModel(new Subscriber())
|
||||||
|
->setModel(new Target())
|
||||||
->setModel(new Migration())
|
->setModel(new Migration())
|
||||||
->setModel(new MigrationReport())
|
->setModel(new MigrationReport())
|
||||||
->setModel(new MigrationFirebaseProject())
|
->setModel(new MigrationFirebaseProject())
|
||||||
|
|
|
@ -60,8 +60,8 @@ class V13 extends Filter
|
||||||
|
|
||||||
protected function parseProject($content)
|
protected function parseProject($content)
|
||||||
{
|
{
|
||||||
$content['providers'] = $content['authProviders'];
|
$content['providers'] = $content['oAuthProviders'];
|
||||||
unset($content['authProviders']);
|
unset($content['oAuthProviders']);
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,9 +88,9 @@ class V16 extends Filter
|
||||||
|
|
||||||
protected function parseProject(array $content)
|
protected function parseProject(array $content)
|
||||||
{
|
{
|
||||||
foreach ($content['providers'] ?? [] as $i => $provider) {
|
foreach ($content['oAuthProviders'] ?? [] as $i => $provider) {
|
||||||
$content['providers'][$i]['name'] = \ucfirst($provider['key']);
|
$content['oAuthProviders'][$i]['name'] = \ucfirst($provider['key']);
|
||||||
unset($content['providers'][$i]['key']);
|
unset($content['oAuthProviders'][$i]['key']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$content['domains'] = [];
|
$content['domains'] = [];
|
||||||
|
|
69
src/Appwrite/Utopia/Response/Model/AuthProvider.php
Normal file
69
src/Appwrite/Utopia/Response/Model/AuthProvider.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
class AuthProvider extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected bool $public = false;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('key', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Auth Provider.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'github',
|
||||||
|
])
|
||||||
|
->addRule('name', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Auth Provider name.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'GitHub',
|
||||||
|
])
|
||||||
|
->addRule('appId', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'OAuth 2.0 application ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('secret', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'OAuth 2.0 application secret. Might be JSON string if provider requires extra configuration.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'Bpw_g9c2TGXxfgLshDbSaL8tsCcqgczQ',
|
||||||
|
])
|
||||||
|
->addRule('enabled', [
|
||||||
|
'type' => self::TYPE_BOOLEAN,
|
||||||
|
'description' => 'Auth Provider is active and can be used to create session.',
|
||||||
|
'example' => '',
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'AuthProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Response::MODEL_AUTH_PROVIDER;
|
||||||
|
}
|
||||||
|
}
|
131
src/Appwrite/Utopia/Response/Model/Message.php
Normal file
131
src/Appwrite/Utopia/Response/Model/Message.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
use Utopia\Database\DateTime;
|
||||||
|
use Utopia\Database\Document as DatabaseDocument;
|
||||||
|
|
||||||
|
class Message extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('$id', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Message ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '5e5ea5c16897e',
|
||||||
|
])
|
||||||
|
->addRule('$createdAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Message creation time in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('$updatedAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Message update date in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('providerType', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Message provider type.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => MESSAGE_TYPE_EMAIL,
|
||||||
|
])
|
||||||
|
->addRule('topics', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Topic IDs set as recipients.',
|
||||||
|
'default' => '',
|
||||||
|
'array' => true,
|
||||||
|
'example' => ['5e5ea5c16897e'],
|
||||||
|
])
|
||||||
|
->addRule('users', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'User IDs set as recipients.',
|
||||||
|
'default' => '',
|
||||||
|
'array' => true,
|
||||||
|
'example' => ['5e5ea5c16897e'],
|
||||||
|
])
|
||||||
|
->addRule('targets', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Target IDs set as recipients.',
|
||||||
|
'default' => '',
|
||||||
|
'array' => true,
|
||||||
|
'example' => ['5e5ea5c16897e'],
|
||||||
|
])
|
||||||
|
->addRule('scheduledAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'The scheduled time for message.',
|
||||||
|
'required' => false,
|
||||||
|
'default' => DateTime::now(),
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('deliveredAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'The time when the message was delivered.',
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('deliveryErrors', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Delivery errors if any.',
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'array' => true,
|
||||||
|
'example' => ['Failed to send message to target 5e5ea5c16897e: Credentials not valid.'],
|
||||||
|
])
|
||||||
|
->addRule('deliveredTotal', [
|
||||||
|
'type' => self::TYPE_INTEGER,
|
||||||
|
'description' => 'Number of recipients the message was delivered to.',
|
||||||
|
'default' => 0,
|
||||||
|
'example' => 1,
|
||||||
|
])
|
||||||
|
->addRule('data', [
|
||||||
|
'type' => self::TYPE_JSON,
|
||||||
|
'description' => 'Data of the message.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => [
|
||||||
|
'subject' => 'Welcome to Appwrite',
|
||||||
|
'content' => 'Hi there, welcome to Appwrite family.',
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->addRule('status', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Status of delivery.',
|
||||||
|
'default' => 'processing',
|
||||||
|
'example' => 'Message status can be one of the following: processing, sent, cancelled, failed.',
|
||||||
|
])
|
||||||
|
->addRule('description', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Message description.',
|
||||||
|
'required' => false,
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'Welcome Email.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Message';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Response::MODEL_MESSAGE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,9 +138,9 @@ class Project extends Model
|
||||||
'default' => false,
|
'default' => false,
|
||||||
'example' => true,
|
'example' => true,
|
||||||
])
|
])
|
||||||
->addRule('providers', [
|
->addRule('oAuthProviders', [
|
||||||
'type' => Response::MODEL_PROVIDER,
|
'type' => Response::MODEL_AUTH_PROVIDER,
|
||||||
'description' => 'List of Providers.',
|
'description' => 'List of Auth Providers.',
|
||||||
'default' => [],
|
'default' => [],
|
||||||
'example' => [new \stdClass()],
|
'example' => [new \stdClass()],
|
||||||
'array' => true,
|
'array' => true,
|
||||||
|
@ -328,9 +328,9 @@ class Project extends Model
|
||||||
$document->setAttribute('auth' . ucfirst($key), $value);
|
$document->setAttribute('auth' . ucfirst($key), $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Providers
|
// OAuth Providers
|
||||||
$providers = Config::getParam('providers', []);
|
$providers = Config::getParam('oAuthProviders', []);
|
||||||
$providerValues = $document->getAttribute('authProviders', []);
|
$providerValues = $document->getAttribute('oAuthProviders', []);
|
||||||
$projectProviders = [];
|
$projectProviders = [];
|
||||||
|
|
||||||
foreach ($providers as $key => $provider) {
|
foreach ($providers as $key => $provider) {
|
||||||
|
@ -348,7 +348,7 @@ class Project extends Model
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->setAttribute("providers", $projectProviders);
|
$document->setAttribute('oAuthProviders', $projectProviders);
|
||||||
|
|
||||||
return $document;
|
return $document;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,44 +7,68 @@ use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
class Provider extends Model
|
class Provider extends Model
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected bool $public = false;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->addRule('key', [
|
->addRule('$id', [
|
||||||
'type' => self::TYPE_STRING,
|
'type' => self::TYPE_STRING,
|
||||||
'description' => 'Provider.',
|
'description' => 'Provider ID.',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'example' => 'github',
|
'example' => '5e5ea5c16897e',
|
||||||
|
])
|
||||||
|
->addRule('$createdAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Provider creation time in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('$updatedAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Provider update date in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
])
|
])
|
||||||
->addRule('name', [
|
->addRule('name', [
|
||||||
'type' => self::TYPE_STRING,
|
'type' => self::TYPE_STRING,
|
||||||
'description' => 'Provider name.',
|
'description' => 'The name for the provider instance.',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'example' => 'GitHub',
|
'example' => 'Mailgun',
|
||||||
])
|
])
|
||||||
->addRule('appId', [
|
->addRule('provider', [
|
||||||
'type' => self::TYPE_STRING,
|
'type' => self::TYPE_STRING,
|
||||||
'description' => 'OAuth 2.0 application ID.',
|
'description' => 'The name of the provider service.',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'example' => '259125845563242502',
|
'example' => 'mailgun',
|
||||||
])
|
|
||||||
->addRule('secret', [
|
|
||||||
'type' => self::TYPE_STRING,
|
|
||||||
'description' => 'OAuth 2.0 application secret. Might be JSON string if provider requires extra configuration.',
|
|
||||||
'default' => '',
|
|
||||||
'example' => 'Bpw_g9c2TGXxfgLshDbSaL8tsCcqgczQ',
|
|
||||||
])
|
])
|
||||||
->addRule('enabled', [
|
->addRule('enabled', [
|
||||||
'type' => self::TYPE_BOOLEAN,
|
'type' => self::TYPE_BOOLEAN,
|
||||||
'description' => 'Provider is active and can be used to create session.',
|
'description' => 'Is provider enabled?',
|
||||||
'example' => '',
|
'default' => true,
|
||||||
|
'example' => true,
|
||||||
])
|
])
|
||||||
;
|
->addRule('type', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Type of provider.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => MESSAGE_TYPE_SMS,
|
||||||
|
])
|
||||||
|
->addRule('credentials', [
|
||||||
|
'type' => self::TYPE_JSON,
|
||||||
|
'description' => 'Provider credentials.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => [
|
||||||
|
'key' => '123456789'
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->addRule('options', [
|
||||||
|
'type' => self::TYPE_JSON,
|
||||||
|
'description' => 'Provider options.',
|
||||||
|
'default' => [],
|
||||||
|
'required' => false,
|
||||||
|
'example' => [
|
||||||
|
'from' => 'sender-email@mydomain'
|
||||||
|
],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
97
src/Appwrite/Utopia/Response/Model/Subscriber.php
Normal file
97
src/Appwrite/Utopia/Response/Model/Subscriber.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
class Subscriber extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('$id', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Subscriber ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('$createdAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Subscriber creation time in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('$updatedAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Subscriber update date in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('targetId', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Target ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('target', [
|
||||||
|
'type' => Response::MODEL_TARGET,
|
||||||
|
'description' => 'Target.',
|
||||||
|
'default' => [],
|
||||||
|
'example' => [
|
||||||
|
'$id' => '259125845563242502',
|
||||||
|
'$createdAt' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
'$updatedAt' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
'providerType' => 'email',
|
||||||
|
'providerId' => '259125845563242502',
|
||||||
|
'name' => 'ageon-app-email',
|
||||||
|
'identifier' => 'random-mail@email.org',
|
||||||
|
'userId' => '5e5ea5c16897e',
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->addRule('userId', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Topic 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.',
|
||||||
|
'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' => MESSAGE_TYPE_EMAIL,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Subscriber';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Response::MODEL_SUBSCRIBER;
|
||||||
|
}
|
||||||
|
}
|
83
src/Appwrite/Utopia/Response/Model/Target.php
Normal file
83
src/Appwrite/Utopia/Response/Model/Target.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
class Target extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('$id', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Target ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('$createdAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Target creation time in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('$updatedAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Target update date in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('name', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Target Name.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'Aegon apple token',
|
||||||
|
])
|
||||||
|
->addRule('userId', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'User ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('providerId', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Provider ID.',
|
||||||
|
'required' => false,
|
||||||
|
'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' => MESSAGE_TYPE_EMAIL,
|
||||||
|
])
|
||||||
|
->addRule('identifier', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The target identifier.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'token',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Target';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Response::MODEL_TARGET;
|
||||||
|
}
|
||||||
|
}
|
71
src/Appwrite/Utopia/Response/Model/Topic.php
Normal file
71
src/Appwrite/Utopia/Response/Model/Topic.php
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
use Appwrite\Utopia\Response;
|
||||||
|
use Appwrite\Utopia\Response\Model;
|
||||||
|
|
||||||
|
class Topic extends Model
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->addRule('$id', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Topic ID.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => '259125845563242502',
|
||||||
|
])
|
||||||
|
->addRule('$createdAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Topic creation time in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('$updatedAt', [
|
||||||
|
'type' => self::TYPE_DATETIME,
|
||||||
|
'description' => 'Topic update date in ISO 8601 format.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||||
|
])
|
||||||
|
->addRule('name', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'The name of the topic.',
|
||||||
|
'default' => '',
|
||||||
|
'example' => 'events',
|
||||||
|
])
|
||||||
|
->addRule('total', [
|
||||||
|
'type' => self::TYPE_INTEGER,
|
||||||
|
'description' => 'Total count of subscribers subscribed to topic.',
|
||||||
|
'default' => 0,
|
||||||
|
'example' => 100,
|
||||||
|
])
|
||||||
|
->addRule('description', [
|
||||||
|
'type' => self::TYPE_STRING,
|
||||||
|
'description' => 'Description of the topic.',
|
||||||
|
'default' => '',
|
||||||
|
'required' => false,
|
||||||
|
'example' => 'All events related messages will be sent to this topic.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Topic';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType(): string
|
||||||
|
{
|
||||||
|
return Response::MODEL_TOPIC;
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,6 +120,13 @@ class User extends Model
|
||||||
'default' => new \stdClass(),
|
'default' => new \stdClass(),
|
||||||
'example' => ['theme' => 'pink', 'timezone' => 'UTC'],
|
'example' => ['theme' => 'pink', 'timezone' => 'UTC'],
|
||||||
])
|
])
|
||||||
|
->addRule('targets', [
|
||||||
|
'type' => Response::MODEL_TARGET,
|
||||||
|
'description' => 'A user-owned message receiver. A single user may have multiple e.g. emails, phones, and a browser. Each target is registered with a single provider.',
|
||||||
|
'default' => [],
|
||||||
|
'array' => true,
|
||||||
|
'example' => [],
|
||||||
|
])
|
||||||
->addRule('accessedAt', [
|
->addRule('accessedAt', [
|
||||||
'type' => self::TYPE_DATETIME,
|
'type' => self::TYPE_DATETIME,
|
||||||
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_USER_ACCCESS / 60 / 60 . ' hours.',
|
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_USER_ACCCESS / 60 / 60 . ' hours.',
|
||||||
|
|
|
@ -168,6 +168,7 @@ class Client
|
||||||
$headers = array_merge($this->headers, $headers);
|
$headers = array_merge($this->headers, $headers);
|
||||||
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
|
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
|
||||||
$responseHeaders = [];
|
$responseHeaders = [];
|
||||||
|
$cookies = [];
|
||||||
|
|
||||||
$query = match ($headers['content-type']) {
|
$query = match ($headers['content-type']) {
|
||||||
'application/json' => json_encode($params),
|
'application/json' => json_encode($params),
|
||||||
|
@ -189,7 +190,7 @@ class Client
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders, &$cookies) {
|
||||||
$len = strlen($header);
|
$len = strlen($header);
|
||||||
$header = explode(':', $header, 2);
|
$header = explode(':', $header, 2);
|
||||||
|
|
||||||
|
@ -197,6 +198,12 @@ class Client
|
||||||
return $len;
|
return $len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strtolower(trim($header[0])) == 'set-cookie') {
|
||||||
|
$parsed = $this->parseCookie((string)trim($header[1]));
|
||||||
|
$name = array_key_first($parsed);
|
||||||
|
$cookies[$name] = $parsed[$name];
|
||||||
|
}
|
||||||
|
|
||||||
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
|
||||||
|
|
||||||
return $len;
|
return $len;
|
||||||
|
@ -241,6 +248,7 @@ class Client
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'headers' => $responseHeaders,
|
'headers' => $responseHeaders,
|
||||||
|
'cookies' => $cookies,
|
||||||
'body' => $responseBody
|
'body' => $responseBody
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,17 @@ trait ProjectCustom
|
||||||
'avatars.read',
|
'avatars.read',
|
||||||
'health.read',
|
'health.read',
|
||||||
'rules.read',
|
'rules.read',
|
||||||
'rules.write'
|
'rules.write',
|
||||||
|
'targets.read',
|
||||||
|
'targets.write',
|
||||||
|
'providers.read',
|
||||||
|
'providers.write',
|
||||||
|
'messages.read',
|
||||||
|
'messages.write',
|
||||||
|
'topics.write',
|
||||||
|
'topics.read',
|
||||||
|
'subscribers.write',
|
||||||
|
'subscribers.read',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ abstract class Scope extends TestCase
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_console'];
|
$session = $session['cookies']['a_session_console'];
|
||||||
|
|
||||||
self::$root = [
|
self::$root = [
|
||||||
'$id' => ID::custom($root['body']['$id']),
|
'$id' => ID::custom($root['body']['$id']),
|
||||||
|
@ -150,7 +150,7 @@ abstract class Scope extends TestCase
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$token = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$token = $session['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
self::$user[$this->getProject()['$id']] = [
|
self::$user[$this->getProject()['$id']] = [
|
||||||
'$id' => ID::custom($user['body']['$id']),
|
'$id' => ID::custom($user['body']['$id']),
|
||||||
|
|
|
@ -126,7 +126,7 @@ trait AccountBase
|
||||||
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
$this->assertNotFalse(\DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $response['body']['expire']));
|
||||||
|
|
||||||
$sessionId = $response['body']['$id'];
|
$sessionId = $response['body']['$id'];
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
// apiKey is only available in custom client test
|
// apiKey is only available in custom client test
|
||||||
$apiKey = $this->getProject()['apiKey'];
|
$apiKey = $this->getProject()['apiKey'];
|
||||||
|
@ -993,7 +993,7 @@ trait AccountBase
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$sessionNewId = $response['body']['$id'];
|
$sessionNewId = $response['body']['$id'];
|
||||||
$sessionNew = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$sessionNew = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$this->assertEquals($response['headers']['status-code'], 201);
|
$this->assertEquals($response['headers']['status-code'], 201);
|
||||||
|
|
||||||
|
@ -1059,7 +1059,7 @@ trait AccountBase
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$sessionNew = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$sessionNew = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$this->assertEquals($response['headers']['status-code'], 201);
|
$this->assertEquals($response['headers']['status-code'], 201);
|
||||||
|
|
||||||
|
@ -1141,7 +1141,7 @@ trait AccountBase
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data['session'] = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$data['session'] = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
@ -1417,7 +1417,7 @@ trait AccountBase
|
||||||
$this->assertNotEmpty($response['body']['userId']);
|
$this->assertNotEmpty($response['body']['userId']);
|
||||||
|
|
||||||
$sessionId = $response['body']['$id'];
|
$sessionId = $response['body']['$id'];
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
namespace Tests\E2E\Services\Account;
|
namespace Tests\E2E\Services\Account;
|
||||||
|
|
||||||
use Appwrite\Extend\Exception;
|
|
||||||
use Appwrite\SMS\Adapter\Mock;
|
|
||||||
use Appwrite\Tests\Retry;
|
use Appwrite\Tests\Retry;
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\ProjectCustom;
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
use Tests\E2E\Scopes\SideClient;
|
use Tests\E2E\Scopes\SideClient;
|
||||||
|
use Utopia\App;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
|
use Utopia\DSN\DSN;
|
||||||
|
|
||||||
use function sleep;
|
use function sleep;
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals($response['headers']['status-code'], 201);
|
$this->assertEquals($response['headers']['status-code'], 201);
|
||||||
|
|
||||||
$sessionId = $response['body']['$id'];
|
$sessionId = $response['body']['$id'];
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
@ -206,7 +206,7 @@ class AccountCustomClientTest extends Scope
|
||||||
|
|
||||||
$this->assertEquals($response['headers']['status-code'], 201);
|
$this->assertEquals($response['headers']['status-code'], 201);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
@ -288,7 +288,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals($response['headers']['status-code'], 201);
|
$this->assertEquals($response['headers']['status-code'], 201);
|
||||||
|
|
||||||
$sessionId = $response['body']['$id'];
|
$sessionId = $response['body']['$id'];
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
@ -368,7 +368,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertNotEmpty($response['body']);
|
$this->assertNotEmpty($response['body']);
|
||||||
$this->assertNotEmpty($response['body']['$id']);
|
$this->assertNotEmpty($response['body']['$id']);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
\usleep(1000 * 30); // wait for 30ms to let the shutdown update accessedAt
|
\usleep(1000 * 30); // wait for 30ms to let the shutdown update accessedAt
|
||||||
|
|
||||||
|
@ -571,7 +571,7 @@ class AccountCustomClientTest extends Scope
|
||||||
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
|
'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertEquals('success', $response['body']['result']);
|
$this->assertEquals('success', $response['body']['result']);
|
||||||
|
@ -764,6 +764,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
|
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
|
||||||
|
|
||||||
$userId = $response['body']['userId'];
|
$userId = $response['body']['userId'];
|
||||||
|
$messageId = $response['body']['$id'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for FAILURE
|
* Test for FAILURE
|
||||||
|
@ -849,7 +850,7 @@ class AccountCustomClientTest extends Scope
|
||||||
$this->assertNotEmpty($response['body']['$id']);
|
$this->assertNotEmpty($response['body']['$id']);
|
||||||
$this->assertNotEmpty($response['body']['userId']);
|
$this->assertNotEmpty($response['body']['userId']);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
|
|
@ -2791,7 +2791,7 @@ trait DatabasesBase
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
$session2 = $this->client->parseCookie((string)$session2['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$document3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], [
|
$document3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], [
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
@ -2979,7 +2979,7 @@ trait DatabasesBase
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
$session2 = $this->client->parseCookie((string)$session2['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$document3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], [
|
$document3GetWithDocumentRead = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document3['body']['$id'], [
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
|
|
@ -32,7 +32,7 @@ trait DatabasesPermissionsScope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $session['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
$user = [
|
$user = [
|
||||||
'$id' => $user['body']['$id'],
|
'$id' => $user['body']['$id'],
|
||||||
|
|
|
@ -6,7 +6,9 @@ use Tests\E2E\Client;
|
||||||
use Tests\E2E\Scopes\ProjectCustom;
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\SideClient;
|
use Tests\E2E\Scopes\SideClient;
|
||||||
|
use Utopia\App;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
|
use Utopia\DSN\DSN;
|
||||||
|
|
||||||
class AccountTest extends Scope
|
class AccountTest extends Scope
|
||||||
{
|
{
|
||||||
|
@ -63,7 +65,7 @@ class AccountTest extends Scope
|
||||||
$this->assertIsArray($session['body']['data']);
|
$this->assertIsArray($session['body']['data']);
|
||||||
$this->assertIsArray($session['body']['data']['accountCreateEmailSession']);
|
$this->assertIsArray($session['body']['data']['accountCreateEmailSession']);
|
||||||
|
|
||||||
$cookie = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$cookie = $session['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
$this->assertNotEmpty($cookie);
|
$this->assertNotEmpty($cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +125,7 @@ class AccountTest extends Scope
|
||||||
public function testCreatePhoneVerification(): array
|
public function testCreatePhoneVerification(): array
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
||||||
$query = $this->getQuery(self::$CREATE_PHONE_VERIFICATION);
|
$query = $this->getQuery(self::$CREATE_PHONE_VERIFICATION);
|
||||||
$graphQLPayload = [
|
$graphQLPayload = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
|
|
|
@ -73,9 +73,7 @@ class AuthTest extends Scope
|
||||||
'x-appwrite-project' => $projectId,
|
'x-appwrite-project' => $projectId,
|
||||||
], $graphQLPayload);
|
], $graphQLPayload);
|
||||||
|
|
||||||
$this->token1 = $this->client->parseCookie(
|
$this->token1 = $session1['cookies']['a_session_' . $projectId];
|
||||||
(string)$session1['headers']['set-cookie']
|
|
||||||
)['a_session_' . $projectId];
|
|
||||||
|
|
||||||
// Create session 2
|
// Create session 2
|
||||||
$graphQLPayload['variables']['email'] = $email2;
|
$graphQLPayload['variables']['email'] = $email2;
|
||||||
|
@ -85,9 +83,7 @@ class AuthTest extends Scope
|
||||||
'x-appwrite-project' => $projectId,
|
'x-appwrite-project' => $projectId,
|
||||||
], $graphQLPayload);
|
], $graphQLPayload);
|
||||||
|
|
||||||
$this->token2 = $this->client->parseCookie(
|
$this->token2 = $session2['cookies']['a_session_' . $projectId];
|
||||||
(string)$session2['headers']['set-cookie']
|
|
||||||
)['a_session_' . $projectId];
|
|
||||||
|
|
||||||
// Create database
|
// Create database
|
||||||
$query = $this->getQuery(self::$CREATE_DATABASE);
|
$query = $this->getQuery(self::$CREATE_DATABASE);
|
||||||
|
|
|
@ -109,6 +109,11 @@ trait Base
|
||||||
public static string $DELETE_USER_SESSIONS = 'delete_user_sessions';
|
public static string $DELETE_USER_SESSIONS = 'delete_user_sessions';
|
||||||
public static string $DELETE_USER_SESSION = 'delete_user_session';
|
public static string $DELETE_USER_SESSION = 'delete_user_session';
|
||||||
public static string $DELETE_USER = 'delete_user';
|
public static string $DELETE_USER = 'delete_user';
|
||||||
|
public static string $CREATE_USER_TARGET = 'create_user_target';
|
||||||
|
public static string $LIST_USER_TARGETS = 'list_user_targets';
|
||||||
|
public static string $GET_USER_TARGET = 'get_user_target';
|
||||||
|
public static string $UPDATE_USER_TARGET = 'update_user_target';
|
||||||
|
public static string $DELETE_USER_TARGET = 'delete_user_target';
|
||||||
|
|
||||||
// Teams
|
// Teams
|
||||||
public static string $GET_TEAM = 'get_team';
|
public static string $GET_TEAM = 'get_team';
|
||||||
|
@ -199,6 +204,53 @@ trait Base
|
||||||
public static string $GET_QRCODE = 'get_qrcode';
|
public static string $GET_QRCODE = 'get_qrcode';
|
||||||
public static string $GET_USER_INITIALS = 'get_user_initials';
|
public static string $GET_USER_INITIALS = 'get_user_initials';
|
||||||
|
|
||||||
|
// Providers
|
||||||
|
public static string $CREATE_MAILGUN_PROVIDER = 'create_mailgun_provider';
|
||||||
|
public static string $CREATE_SENDGRID_PROVIDER = 'create_sendgrid_provider';
|
||||||
|
public static string $CREATE_TWILIO_PROVIDER = 'create_twilio_provider';
|
||||||
|
public static string $CREATE_TELESIGN_PROVIDER = 'create_telesign_provider';
|
||||||
|
public static string $CREATE_TEXTMAGIC_PROVIDER = 'create_textmagic_provider';
|
||||||
|
public static string $CREATE_MSG91_PROVIDER = 'create_msg91_provider';
|
||||||
|
public static string $CREATE_VONAGE_PROVIDER = 'create_vonage_provider';
|
||||||
|
public static string $CREATE_FCM_PROVIDER = 'create_fcm_provider';
|
||||||
|
public static string $CREATE_APNS_PROVIDER = 'create_apns_provider';
|
||||||
|
public static string $LIST_PROVIDERS = 'list_providers';
|
||||||
|
public static string $GET_PROVIDER = 'get_provider';
|
||||||
|
public static string $UPDATE_MAILGUN_PROVIDER = 'update_mailgun_provider';
|
||||||
|
public static string $UPDATE_SENDGRID_PROVIDER = 'update_sendgrid_provider';
|
||||||
|
public static string $UPDATE_TWILIO_PROVIDER = 'update_twilio_provider';
|
||||||
|
public static string $UPDATE_TELESIGN_PROVIDER = 'update_telesign_provider';
|
||||||
|
public static string $UPDATE_TEXTMAGIC_PROVIDER = 'update_textmagic_provider';
|
||||||
|
public static string $UPDATE_MSG91_PROVIDER = 'update_msg91_provider';
|
||||||
|
public static string $UPDATE_VONAGE_PROVIDER = 'update_vonage_provider';
|
||||||
|
public static string $UPDATE_FCM_PROVIDER = 'update_fcm_provider';
|
||||||
|
public static string $UPDATE_APNS_PROVIDER = 'update_apns_provider';
|
||||||
|
public static string $DELETE_PROVIDER = 'delete_provider';
|
||||||
|
|
||||||
|
// Topics
|
||||||
|
public static string $CREATE_TOPIC = 'create_topic';
|
||||||
|
public static string $LIST_TOPICS = 'list_topics';
|
||||||
|
public static string $GET_TOPIC = 'get_topic';
|
||||||
|
public static string $UPDATE_TOPIC = 'update_topic';
|
||||||
|
public static string $DELETE_TOPIC = 'delete_topic';
|
||||||
|
|
||||||
|
// Subscriptions
|
||||||
|
public static string $CREATE_SUBSCRIBER = 'create_subscriber';
|
||||||
|
public static string $LIST_SUBSCRIBERS = 'list_subscribers';
|
||||||
|
public static string $GET_SUBSCRIBER = 'get_subscriber';
|
||||||
|
public static string $DELETE_SUBSCRIBER = 'delete_subscriber';
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
public static string $CREATE_EMAIL = 'create_email';
|
||||||
|
public static string $CREATE_SMS = 'create_sms';
|
||||||
|
public static string $CREATE_PUSH_NOTIFICATION = 'create_push_notification';
|
||||||
|
public static string $LIST_MESSAGES = 'list_messages';
|
||||||
|
public static string $GET_MESSAGE = 'get_message';
|
||||||
|
|
||||||
|
public static string $UPDATE_EMAIL = 'update_email';
|
||||||
|
public static string $UPDATE_SMS = 'update_sms';
|
||||||
|
public static string $UPDATE_PUSH_NOTIFICATION = 'update_push_notification';
|
||||||
|
|
||||||
// Complex queries
|
// Complex queries
|
||||||
public static string $COMPLEX_QUERY = 'complex_query';
|
public static string $COMPLEX_QUERY = 'complex_query';
|
||||||
|
|
||||||
|
@ -881,6 +933,55 @@ trait Base
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
case self::$CREATE_USER_TARGET:
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$LIST_USER_TARGETS:
|
||||||
|
return 'query listUserTargets($userId: String!) {
|
||||||
|
usersListTargets(userId: $userId) {
|
||||||
|
total
|
||||||
|
targets {
|
||||||
|
_id
|
||||||
|
userId
|
||||||
|
providerType
|
||||||
|
providerId
|
||||||
|
identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$GET_USER_TARGET:
|
||||||
|
return 'query getUserTarget($userId: String!, $targetId: String!) {
|
||||||
|
usersGetTarget(userId: $userId, targetId: $targetId) {
|
||||||
|
_id
|
||||||
|
userId
|
||||||
|
providerType
|
||||||
|
providerId
|
||||||
|
identifier
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_USER_TARGET:
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$DELETE_USER_TARGET:
|
||||||
|
return 'mutation deleteUserTarget($userId: String!, $targetId: String!){
|
||||||
|
usersDeleteTarget(userId: $userId, targetId: $targetId) {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}';
|
||||||
case self::$GET_LOCALE:
|
case self::$GET_LOCALE:
|
||||||
return 'query getLocale {
|
return 'query getLocale {
|
||||||
localeGet {
|
localeGet {
|
||||||
|
@ -1688,6 +1789,439 @@ trait Base
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
case self::$CREATE_MAILGUN_PROVIDER:
|
||||||
|
return 'mutation createMailgunProvider($providerId: String!, $name: String!, $domain: String!, $apiKey: String!, $from: String!, $isEuRegion: Boolean!) {
|
||||||
|
messagingCreateMailgunProvider(providerId: $providerId, name: $name, domain: $domain, apiKey: $apiKey, from: $from, isEuRegion: $isEuRegion) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_SENDGRID_PROVIDER:
|
||||||
|
return 'mutation createSendgridProvider($providerId: String!, $name: String!, $from: String!, $apiKey: String!) {
|
||||||
|
messagingCreateSendgridProvider(providerId: $providerId, name: $name, from: $from, apiKey: $apiKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_TWILIO_PROVIDER:
|
||||||
|
return 'mutation createTwilioProvider($providerId: String!, $name: String!, $from: String!, $accountSid: String!, $authToken: String!) {
|
||||||
|
messagingCreateTwilioProvider(providerId: $providerId, name: $name, from: $from, accountSid: $accountSid, authToken: $authToken) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_TELESIGN_PROVIDER:
|
||||||
|
return 'mutation createTelesignProvider($providerId: String!, $name: String!, $from: String!, $username: String!, $password: String!) {
|
||||||
|
messagingCreateTelesignProvider(providerId: $providerId, name: $name, from: $from, username: $username, password: $password) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_TEXTMAGIC_PROVIDER:
|
||||||
|
return 'mutation createTextmagicProvider($providerId: String!, $name: String!, $from: String!, $username: String!, $apiKey: String!) {
|
||||||
|
messagingCreateTextmagicProvider(providerId: $providerId, name: $name, from: $from, username: $username, apiKey: $apiKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_MSG91_PROVIDER:
|
||||||
|
return 'mutation createMsg91Provider($providerId: String!, $name: String!, $from: String!, $senderId: String!, $authKey: String!, $enabled: Boolean) {
|
||||||
|
messagingCreateMsg91Provider(providerId: $providerId, name: $name, from: $from, senderId: $senderId, authKey: $authKey, enabled: $enabled) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_VONAGE_PROVIDER:
|
||||||
|
return 'mutation createVonageProvider($providerId: String!, $name: String!, $from: String!, $apiKey: String!, $apiSecret: String!) {
|
||||||
|
messagingCreateVonageProvider(providerId: $providerId, name: $name, from: $from, apiKey: $apiKey, apiSecret: $apiSecret) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_FCM_PROVIDER:
|
||||||
|
return 'mutation createFcmProvider($providerId: String!, $name: String!, $serverKey: String!) {
|
||||||
|
messagingCreateFcmProvider(providerId: $providerId, name: $name, serverKey: $serverKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_APNS_PROVIDER:
|
||||||
|
return 'mutation createApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!, $endpoint: String!) {
|
||||||
|
messagingCreateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId, endpoint: $endpoint) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$LIST_PROVIDERS:
|
||||||
|
return 'query listProviders {
|
||||||
|
messagingListProviders {
|
||||||
|
total
|
||||||
|
providers {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$GET_PROVIDER:
|
||||||
|
return 'query getProvider($providerId: String!) {
|
||||||
|
messagingGetProvider(providerId: $providerId) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_MAILGUN_PROVIDER:
|
||||||
|
return 'mutation updateMailgunProvider($providerId: String!, $name: String!, $domain: String!, $apiKey: String!, $isEuRegion: Boolean, $enabled: Boolean) {
|
||||||
|
messagingUpdateMailgunProvider(providerId: $providerId, name: $name, domain: $domain, apiKey: $apiKey, isEuRegion: $isEuRegion, enabled: $enabled) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_SENDGRID_PROVIDER:
|
||||||
|
return 'mutation messagingUpdateSendgridProvider($providerId: String!, $name: String!, $apiKey: String!) {
|
||||||
|
messagingUpdateSendgridProvider(providerId: $providerId, name: $name, apiKey: $apiKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_TWILIO_PROVIDER:
|
||||||
|
return 'mutation updateTwilioProvider($providerId: String!, $name: String!, $accountSid: String!, $authToken: String!) {
|
||||||
|
messagingUpdateTwilioProvider(providerId: $providerId, name: $name, accountSid: $accountSid, authToken: $authToken) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_TELESIGN_PROVIDER:
|
||||||
|
return 'mutation updateTelesignProvider($providerId: String!, $name: String!, $username: String!, $password: String!) {
|
||||||
|
messagingUpdateTelesignProvider(providerId: $providerId, name: $name, username: $username, password: $password) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_TEXTMAGIC_PROVIDER:
|
||||||
|
return 'mutation updateTextmagicProvider($providerId: String!, $name: String!, $username: String!, $apiKey: String!) {
|
||||||
|
messagingUpdateTextmagicProvider(providerId: $providerId, name: $name, username: $username, apiKey: $apiKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_MSG91_PROVIDER:
|
||||||
|
return 'mutation updateMsg91Provider($providerId: String!, $name: String!, $senderId: String!, $authKey: String!) {
|
||||||
|
messagingUpdateMsg91Provider(providerId: $providerId, name: $name, senderId: $senderId, authKey: $authKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_VONAGE_PROVIDER:
|
||||||
|
return 'mutation updateVonageProvider($providerId: String!, $name: String!, $apiKey: String!, $apiSecret: String!) {
|
||||||
|
messagingUpdateVonageProvider(providerId: $providerId, name: $name, apiKey: $apiKey, apiSecret: $apiSecret) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_FCM_PROVIDER:
|
||||||
|
return 'mutation updateFcmProvider($providerId: String!, $name: String!, $serverKey: String!) {
|
||||||
|
messagingUpdateFcmProvider(providerId: $providerId, name: $name, serverKey: $serverKey) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_APNS_PROVIDER:
|
||||||
|
return 'mutation updateApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!, $endpoint: String!) {
|
||||||
|
messagingUpdateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId, endpoint: $endpoint) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
provider
|
||||||
|
type
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$DELETE_PROVIDER:
|
||||||
|
return 'mutation deleteProvider($providerId: String!) {
|
||||||
|
messagingDeleteProvider(providerId: $providerId) {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_TOPIC:
|
||||||
|
return 'mutation createTopic($topicId: String!, $name: String!, $description: String!) {
|
||||||
|
messagingCreateTopic(topicId: $topicId, name: $name, description: $description) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$LIST_TOPICS:
|
||||||
|
return 'query listTopics {
|
||||||
|
messagingListTopics {
|
||||||
|
total
|
||||||
|
topics {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$GET_TOPIC:
|
||||||
|
return 'query getTopic($topicId: String!) {
|
||||||
|
messagingGetTopic(topicId: $topicId) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_TOPIC:
|
||||||
|
return 'mutation updateTopic($topicId: String!, $name: String!, $description: String!) {
|
||||||
|
messagingUpdateTopic(topicId: $topicId, name: $name, description: $description) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$DELETE_TOPIC:
|
||||||
|
return 'mutation deleteTopic($topicId: String!) {
|
||||||
|
messagingDeleteTopic(topicId: $topicId) {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_SUBSCRIBER:
|
||||||
|
return 'mutation createSubscriber($subscriberId: String!, $targetId: String!, $topicId: String!) {
|
||||||
|
messagingCreateSubscriber(subscriberId: $subscriberId, targetId: $targetId, topicId: $topicId) {
|
||||||
|
_id
|
||||||
|
targetId
|
||||||
|
topicId
|
||||||
|
userName
|
||||||
|
target {
|
||||||
|
_id
|
||||||
|
userId
|
||||||
|
name
|
||||||
|
providerType
|
||||||
|
identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$LIST_SUBSCRIBERS:
|
||||||
|
return 'query listSubscribers($topicId: String!) {
|
||||||
|
messagingListSubscribers(topicId: $topicId) {
|
||||||
|
total
|
||||||
|
subscribers {
|
||||||
|
_id
|
||||||
|
targetId
|
||||||
|
topicId
|
||||||
|
userName
|
||||||
|
target {
|
||||||
|
_id
|
||||||
|
userId
|
||||||
|
name
|
||||||
|
providerType
|
||||||
|
identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$GET_SUBSCRIBER:
|
||||||
|
return 'query getSubscriber($topicId: String!, $subscriberId: String!) {
|
||||||
|
messagingGetSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
|
||||||
|
_id
|
||||||
|
targetId
|
||||||
|
topicId
|
||||||
|
userName
|
||||||
|
target {
|
||||||
|
_id
|
||||||
|
userId
|
||||||
|
name
|
||||||
|
providerType
|
||||||
|
identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$DELETE_SUBSCRIBER:
|
||||||
|
return 'mutation deleteSubscriber($topicId: String!, $subscriberId: String!) {
|
||||||
|
messagingDeleteSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_EMAIL:
|
||||||
|
return 'mutation createEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String!, $content: String!, $status: String, $description: String, $html: Boolean, $scheduledAt: String) {
|
||||||
|
messagingCreateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_SMS:
|
||||||
|
return 'mutation createSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String!, $status: String, $description: String, $scheduledAt: String) {
|
||||||
|
messagingCreateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$CREATE_PUSH_NOTIFICATION:
|
||||||
|
return 'mutation createPushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String!, $body: String!, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
|
||||||
|
messagingCreatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$LIST_MESSAGES:
|
||||||
|
return 'query listMessages {
|
||||||
|
messagingListMessages {
|
||||||
|
total
|
||||||
|
messages {
|
||||||
|
_id
|
||||||
|
providerType
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$GET_MESSAGE:
|
||||||
|
return 'query getMessage($messageId: String!) {
|
||||||
|
messagingGetMessage(messageId: $messageId) {
|
||||||
|
_id
|
||||||
|
providerType
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_EMAIL:
|
||||||
|
return 'mutation updateEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String, $content: String, $status: String, $description: String, $html: Boolean, $scheduledAt: String) {
|
||||||
|
messagingUpdateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_SMS:
|
||||||
|
return 'mutation updateSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String, $status: String, $description: String, $scheduledAt: String) {
|
||||||
|
messagingUpdateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
case self::$UPDATE_PUSH_NOTIFICATION:
|
||||||
|
return 'mutation updatePushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String, $body: String, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
|
||||||
|
messagingUpdatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
|
||||||
|
_id
|
||||||
|
topics
|
||||||
|
users
|
||||||
|
targets
|
||||||
|
scheduledAt
|
||||||
|
deliveredAt
|
||||||
|
deliveryErrors
|
||||||
|
deliveredTotal
|
||||||
|
status
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}';
|
||||||
case self::$COMPLEX_QUERY:
|
case self::$COMPLEX_QUERY:
|
||||||
return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) {
|
return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) {
|
||||||
databasesCreate(databaseId: $databaseId, name: $databaseName) {
|
databasesCreate(databaseId: $databaseId, name: $databaseName) {
|
||||||
|
@ -1940,7 +2474,7 @@ trait Base
|
||||||
protected string $stdout = '';
|
protected string $stdout = '';
|
||||||
protected string $stderr = '';
|
protected string $stderr = '';
|
||||||
|
|
||||||
protected function packageCode($folder)
|
protected function packageCode($folder): void
|
||||||
{
|
{
|
||||||
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
|
||||||
}
|
}
|
||||||
|
|
1153
tests/e2e/Services/GraphQL/MessagingTest.php
Normal file
1153
tests/e2e/Services/GraphQL/MessagingTest.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -45,6 +45,56 @@ class UsersTest extends Scope
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateUser
|
||||||
|
*/
|
||||||
|
public function testCreateUserTarget(array $user)
|
||||||
|
{
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
||||||
|
$query = $this->getQuery(self::$CREATE_MAILGUN_PROVIDER);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'providerId' => ID::unique(),
|
||||||
|
'name' => 'Mailgun1',
|
||||||
|
'apiKey' => 'api-key',
|
||||||
|
'domain' => 'domain',
|
||||||
|
'from' => 'from@domain.com',
|
||||||
|
'isEuRegion' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
$providerId = $provider['body']['data']['messagingCreateMailgunProvider']['_id'];
|
||||||
|
|
||||||
|
$this->assertEquals(200, $provider['headers']['status-code']);
|
||||||
|
|
||||||
|
$query = $this->getQuery(self::$CREATE_USER_TARGET);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'targetId' => ID::unique(),
|
||||||
|
'userId' => $user['_id'],
|
||||||
|
'providerType' => 'email',
|
||||||
|
'providerId' => $providerId,
|
||||||
|
'identifier' => 'random-email@mail.org',
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $target['headers']['status-code']);
|
||||||
|
$this->assertEquals('random-email@mail.org', $target['body']['data']['usersCreateTarget']['identifier']);
|
||||||
|
|
||||||
|
return $target['body']['data']['usersCreateTarget'];
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetUsers()
|
public function testGetUsers()
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
|
@ -176,6 +226,54 @@ class UsersTest extends Scope
|
||||||
$this->assertIsArray($user['body']['data']['usersListLogs']);
|
$this->assertIsArray($user['body']['data']['usersListLogs']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateUserTarget
|
||||||
|
*/
|
||||||
|
public function testListUserTargets(array $target)
|
||||||
|
{
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
$query = $this->getQuery(self::$LIST_USER_TARGETS);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'userId' => $target['userId'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$targets = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $targets['headers']['status-code']);
|
||||||
|
$this->assertIsArray($targets['body']['data']['usersListTargets']);
|
||||||
|
$this->assertCount(2, $targets['body']['data']['usersListTargets']['targets']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateUserTarget
|
||||||
|
*/
|
||||||
|
public function testGetUserTarget(array $target)
|
||||||
|
{
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
$query = $this->getQuery(self::$GET_USER_TARGET);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'userId' => $target['userId'],
|
||||||
|
'targetId' => $target['_id'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $target['headers']['status-code']);
|
||||||
|
$this->assertEquals('random-email@mail.org', $target['body']['data']['usersGetTarget']['identifier']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testUpdateUserStatus()
|
public function testUpdateUserStatus()
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
|
@ -360,6 +458,31 @@ class UsersTest extends Scope
|
||||||
$this->assertEquals('{"key":"value"}', $user['body']['data']['usersUpdatePrefs']['data']);
|
$this->assertEquals('{"key":"value"}', $user['body']['data']['usersUpdatePrefs']['data']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateUserTarget
|
||||||
|
*/
|
||||||
|
public function testUpdateUserTarget(array $target)
|
||||||
|
{
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
$query = $this->getQuery(self::$UPDATE_USER_TARGET);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'userId' => $target['userId'],
|
||||||
|
'targetId' => $target['_id'],
|
||||||
|
'identifier' => 'random-email1@mail.org',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $target['headers']['status-code']);
|
||||||
|
$this->assertEquals('random-email1@mail.org', $target['body']['data']['usersUpdateTarget']['identifier']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testDeleteUserSessions()
|
public function testDeleteUserSessions()
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
|
@ -407,6 +530,29 @@ class UsersTest extends Scope
|
||||||
$this->getUser();
|
$this->getUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testCreateUserTarget
|
||||||
|
*/
|
||||||
|
public function testDeleteUserTarget(array $target)
|
||||||
|
{
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
$query = $this->getQuery(self::$DELETE_USER_TARGET);
|
||||||
|
$graphQLPayload = [
|
||||||
|
'query' => $query,
|
||||||
|
'variables' => [
|
||||||
|
'userId' => $target['userId'],
|
||||||
|
'targetId' => $target['_id'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), $graphQLPayload);
|
||||||
|
|
||||||
|
$this->assertEquals(204, $target['headers']['status-code']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testDeleteUser()
|
public function testDeleteUser()
|
||||||
{
|
{
|
||||||
$projectId = $this->getProject()['$id'];
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
1040
tests/e2e/Services/Messaging/MessagingBase.php
Normal file
1040
tests/e2e/Services/Messaging/MessagingBase.php
Normal file
File diff suppressed because it is too large
Load diff
413
tests/e2e/Services/Messaging/MessagingConsoleClientTest.php
Normal file
413
tests/e2e/Services/Messaging/MessagingConsoleClientTest.php
Normal file
|
@ -0,0 +1,413 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Messaging;
|
||||||
|
|
||||||
|
use Tests\E2E\Client;
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\SideConsole;
|
||||||
|
use Utopia\Database\Helpers\ID;
|
||||||
|
|
||||||
|
class MessagingConsoleClientTest extends Scope
|
||||||
|
{
|
||||||
|
use MessagingBase;
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideConsole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testListProviders
|
||||||
|
*/
|
||||||
|
public function testGetProviderLogs(array $providers): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $providers[0]['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/sendgrid/', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'providerId' => ID::unique(),
|
||||||
|
'name' => 'Sengrid1',
|
||||||
|
'apiKey' => 'my-apikey',
|
||||||
|
'from' => 'sender-email@my-domain.com',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $provider['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/providers/sendgrid/' . $provider['body']['$id'], \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'sendgrid' => [
|
||||||
|
'name' => 'Sendgrid2',
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
$this->assertCount(2, $logs['body']['logs']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)', 'offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['equal("$id", "asdf")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['orderAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['cursorAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testListTopic
|
||||||
|
*/
|
||||||
|
public function testGetTopicLogs(string $topicId): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topicId . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'topicId' => ID::unique(),
|
||||||
|
'name' => 'my-app',
|
||||||
|
'description' => 'web app'
|
||||||
|
]);
|
||||||
|
$this->assertEquals(201, $topic['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/topics/' . $topic['body']['$id'], \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'description' => 'updated-description'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertCount(2, $logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)', 'offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['equal("$id", "asdf")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['orderAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['cursorAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testSendEmail
|
||||||
|
*/
|
||||||
|
public function testGetMessageLogs(array $email): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', [
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$email = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'messageId' => ID::unique(),
|
||||||
|
'status' => 'draft',
|
||||||
|
'topics' => [ID::unique()],
|
||||||
|
'subject' => 'Khali beats Undertaker',
|
||||||
|
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $email['headers']['status-code']);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'subject' => 'Khali beats John Cena!',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
$this->assertCount(2, $logs['body']['logs']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(1)', 'offset(1)'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||||
|
$this->assertIsArray($logs['body']['logs']);
|
||||||
|
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||||
|
$this->assertIsNumeric($logs['body']['total']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE
|
||||||
|
*/
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['limit(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['offset(-1)']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['equal("$id", "asdf")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['orderAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'queries' => ['cursorAsc("$id")']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($response['headers']['status-code'], 400);
|
||||||
|
}
|
||||||
|
}
|
14
tests/e2e/Services/Messaging/MessagingCustomClientTest.php
Normal file
14
tests/e2e/Services/Messaging/MessagingCustomClientTest.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Messaging;
|
||||||
|
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\SideClient;
|
||||||
|
|
||||||
|
class MessagingCustomClientTest extends Scope
|
||||||
|
{
|
||||||
|
use MessagingBase;
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideClient;
|
||||||
|
}
|
14
tests/e2e/Services/Messaging/MessagingCustomServerTest.php
Normal file
14
tests/e2e/Services/Messaging/MessagingCustomServerTest.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\E2E\Services\Messaging;
|
||||||
|
|
||||||
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
use Tests\E2E\Scopes\Scope;
|
||||||
|
use Tests\E2E\Scopes\SideServer;
|
||||||
|
|
||||||
|
class MessagingCustomServerTest extends Scope
|
||||||
|
{
|
||||||
|
use MessagingBase;
|
||||||
|
use ProjectCustom;
|
||||||
|
use SideServer;
|
||||||
|
}
|
|
@ -794,7 +794,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
public function testUpdateProjectOAuth($data): array
|
public function testUpdateProjectOAuth($data): array
|
||||||
{
|
{
|
||||||
$id = $data['projectId'] ?? '';
|
$id = $data['projectId'] ?? '';
|
||||||
$providers = require('app/config/providers.php');
|
$providers = require('app/config/oAuthProviders.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for SUCCESS
|
* Test for SUCCESS
|
||||||
|
@ -825,7 +825,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
|
|
||||||
foreach ($providers as $key => $provider) {
|
foreach ($providers as $key => $provider) {
|
||||||
$asserted = false;
|
$asserted = false;
|
||||||
foreach ($response['body']['providers'] as $responseProvider) {
|
foreach ($response['body']['oAuthProviders'] as $responseProvider) {
|
||||||
if ($responseProvider['key'] === $key) {
|
if ($responseProvider['key'] === $key) {
|
||||||
$this->assertEquals('AppId-' . ucfirst($key), $responseProvider['appId']);
|
$this->assertEquals('AppId-' . ucfirst($key), $responseProvider['appId']);
|
||||||
$this->assertEquals('Secret-' . ucfirst($key), $responseProvider['secret']);
|
$this->assertEquals('Secret-' . ucfirst($key), $responseProvider['secret']);
|
||||||
|
@ -867,7 +867,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($providers as $key => $provider) {
|
foreach ($providers as $key => $provider) {
|
||||||
$asserted = false;
|
$asserted = false;
|
||||||
foreach ($response['body']['providers'] as $responseProvider) {
|
foreach ($response['body']['oAuthProviders'] as $responseProvider) {
|
||||||
if ($responseProvider['key'] === $key) {
|
if ($responseProvider['key'] === $key) {
|
||||||
// On first provider, test enabled=false
|
// On first provider, test enabled=false
|
||||||
$this->assertEquals($i !== 0, $responseProvider['enabled']);
|
$this->assertEquals($i !== 0, $responseProvider['enabled']);
|
||||||
|
@ -931,7 +931,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
'password' => $originalPassword,
|
'password' => $originalPassword,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $id];
|
$session = $response['cookies']['a_session_' . $id];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for SUCCESS
|
* Test for SUCCESS
|
||||||
|
@ -1313,7 +1313,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
$this->assertEquals(201, $session['headers']['status-code']);
|
$this->assertEquals(201, $session['headers']['status-code']);
|
||||||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $id];
|
$session = $session['cookies']['a_session_' . $id];
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([
|
$response = $this->client->call(Client::METHOD_PATCH, '/account/password', array_merge([
|
||||||
'origin' => 'http://localhost',
|
'origin' => 'http://localhost',
|
||||||
|
|
|
@ -468,7 +468,7 @@ class RealtimeCustomClientTest extends Scope
|
||||||
'password' => 'new-password',
|
'password' => 'new-password',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$sessionNew = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $projectId];
|
$sessionNew = $response['cookies']['a_session_' . $projectId];
|
||||||
$sessionNewId = $response['body']['$id'];
|
$sessionNewId = $response['body']['$id'];
|
||||||
|
|
||||||
return array("session" => $sessionNew, "sessionId" => $sessionNewId);
|
return array("session" => $sessionNew, "sessionId" => $sessionNewId);
|
||||||
|
|
|
@ -32,7 +32,7 @@ trait StoragePermissionsScope
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $session['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
|
|
||||||
|
|
||||||
$user = [
|
$user = [
|
||||||
|
|
|
@ -403,7 +403,7 @@ trait TeamsBaseClient
|
||||||
$this->assertCount(2, $response['body']['roles']);
|
$this->assertCount(2, $response['body']['roles']);
|
||||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined']));
|
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined']));
|
||||||
$this->assertEquals(true, $response['body']['confirm']);
|
$this->assertEquals(true, $response['body']['confirm']);
|
||||||
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']];
|
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
|
||||||
$data['session'] = $session;
|
$data['session'] = $session;
|
||||||
|
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue