Merge pull request #8240 from appwrite/revert-8237-revert-8193-feat-remove-random-shared-tables
Revert "Merge pull request #8237 from appwrite/revert-8193-feat-remov…
This commit is contained in:
commit
7e3070a4b2
13 changed files with 42 additions and 603 deletions
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
|
@ -145,3 +145,6 @@ jobs:
|
||||||
|
|
||||||
- name: Run ${{matrix.service}} Tests
|
- name: Run ${{matrix.service}} Tests
|
||||||
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||||
|
|
||||||
|
- name: Run ${{matrix.service}} Shared Tables Tests
|
||||||
|
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||||
|
|
|
@ -109,7 +109,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
|
||||||
if (isset($databases[$dsn->getHost()])) {
|
if (isset($databases[$dsn->getHost()])) {
|
||||||
$database = $databases[$dsn->getHost()];
|
$database = $databases[$dsn->getHost()];
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
@ -133,7 +133,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
|
||||||
|
|
||||||
$databases[$dsn->getHost()] = $database;
|
$databases[$dsn->getHost()] = $database;
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
|
|
@ -111,35 +111,8 @@ App::post('/v1/projects')
|
||||||
|
|
||||||
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
|
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
|
||||||
|
|
||||||
$backups['database_db_fra1_v14x_02'] = ['from' => '03:00', 'to' => '05:00'];
|
|
||||||
$backups['database_db_fra1_v14x_03'] = ['from' => '00:00', 'to' => '02:00'];
|
|
||||||
$backups['database_db_fra1_v14x_04'] = ['from' => '00:00', 'to' => '02:00'];
|
|
||||||
$backups['database_db_fra1_v14x_05'] = ['from' => '00:00', 'to' => '02:00'];
|
|
||||||
$backups['database_db_fra1_v14x_06'] = ['from' => '00:00', 'to' => '02:00'];
|
|
||||||
$backups['database_db_fra1_v14x_07'] = ['from' => '00:00', 'to' => '02:00'];
|
|
||||||
|
|
||||||
$databases = Config::getParam('pools-database', []);
|
$databases = Config::getParam('pools-database', []);
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove databases from the list that are currently undergoing an backup
|
|
||||||
*/
|
|
||||||
if (count($databases) > 1) {
|
|
||||||
$now = new \DateTime();
|
|
||||||
|
|
||||||
foreach ($databases as $index => $database) {
|
|
||||||
if (empty($backups[$database])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$backup = $backups[$database];
|
|
||||||
$from = \DateTime::createFromFormat('H:i', $backup['from']);
|
|
||||||
$to = \DateTime::createFromFormat('H:i', $backup['to']);
|
|
||||||
if ($now >= $from && $now <= $to) {
|
|
||||||
unset($databases[$index]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
||||||
$index = \array_search($databaseOverride, $databases);
|
$index = \array_search($databaseOverride, $databases);
|
||||||
if ($index !== false) {
|
if ($index !== false) {
|
||||||
|
@ -152,37 +125,12 @@ App::post('/v1/projects')
|
||||||
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
|
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 1 in 5 projects use shared tables. Temporary until all projects are using shared tables.
|
// TODO: Temporary until all projects are using shared tables.
|
||||||
if (
|
if ($dsn === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
(
|
|
||||||
!\mt_rand(0, 4)
|
|
||||||
&& System::getEnv('_APP_DATABASE_SHARED_TABLES', 'enabled') === 'enabled'
|
|
||||||
&& System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
|
|
||||||
) ||
|
|
||||||
(
|
|
||||||
$dsn === DATABASE_SHARED_TABLES
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
$schema = 'appwrite';
|
$schema = 'appwrite';
|
||||||
$database = 'appwrite';
|
$database = 'appwrite';
|
||||||
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
|
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
|
||||||
$dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
|
$dsn = $schema . '://' . System::getEnv('_APP_DATABASE_SHARED_TABLES', '') . '?database=' . $database;
|
||||||
|
|
||||||
if (!empty($namespace)) {
|
|
||||||
$dsn .= '&namespace=' . $namespace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Allow overriding in development mode. Temporary until all projects are using shared tables.
|
|
||||||
if (
|
|
||||||
App::isDevelopment()
|
|
||||||
&& System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
|
|
||||||
&& $request->getHeader('x-appwrited-share-tables', false)
|
|
||||||
) {
|
|
||||||
$schema = 'appwrite';
|
|
||||||
$database = 'appwrite';
|
|
||||||
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
|
|
||||||
$dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
|
|
||||||
|
|
||||||
if (!empty($namespace)) {
|
if (!empty($namespace)) {
|
||||||
$dsn .= '&namespace=' . $namespace;
|
$dsn .= '&namespace=' . $namespace;
|
||||||
|
@ -236,7 +184,7 @@ App::post('/v1/projects')
|
||||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
||||||
$dbForProject = new Database($adapter, $cache);
|
$dbForProject = new Database($adapter, $cache);
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$dbForProject
|
$dbForProject
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
|
|
@ -584,7 +584,7 @@ App::init()
|
||||||
->addHeader('Server', 'Appwrite')
|
->addHeader('Server', 'Appwrite')
|
||||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
|
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
|
||||||
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
||||||
->addHeader('Access-Control-Allow-Origin', $refDomain)
|
->addHeader('Access-Control-Allow-Origin', $refDomain)
|
||||||
->addHeader('Access-Control-Allow-Credentials', 'true');
|
->addHeader('Access-Control-Allow-Credentials', 'true');
|
||||||
|
@ -635,7 +635,7 @@ App::options()
|
||||||
$response
|
$response
|
||||||
->addHeader('Server', 'Appwrite')
|
->addHeader('Server', 'Appwrite')
|
||||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
|
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
|
||||||
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
||||||
->addHeader('Access-Control-Allow-Origin', $origin)
|
->addHeader('Access-Control-Allow-Origin', $origin)
|
||||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||||
|
|
|
@ -143,9 +143,6 @@ const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||||
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||||
|
|
||||||
// Databases
|
|
||||||
const DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0';
|
|
||||||
|
|
||||||
// Database Reconnect
|
// Database Reconnect
|
||||||
const DATABASE_RECONNECT_SLEEP = 2;
|
const DATABASE_RECONNECT_SLEEP = 2;
|
||||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||||
|
@ -1338,7 +1335,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
@ -1391,7 +1388,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
|
||||||
->setMetadata('project', $project->getId())
|
->setMetadata('project', $project->getId())
|
||||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
|
|
@ -92,7 +92,7 @@ if (!function_exists("getProjectDB")) {
|
||||||
|
|
||||||
$database = new Database($adapter, getCache());
|
$database = new Database($adapter, getCache());
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
|
|
@ -93,7 +93,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
|
||||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
@ -126,7 +126,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
|
||||||
if (isset($databases[$dsn->getHost()])) {
|
if (isset($databases[$dsn->getHost()])) {
|
||||||
$database = $databases[$dsn->getHost()];
|
$database = $databases[$dsn->getHost()];
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
@ -150,7 +150,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
|
||||||
|
|
||||||
$databases[$dsn->getHost()] = $database;
|
$databases[$dsn->getHost()] = $database;
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant($project->getInternalId())
|
->setTenant($project->getInternalId())
|
||||||
|
|
|
@ -189,6 +189,7 @@ services:
|
||||||
- _APP_CONSOLE_COUNTRIES_DENYLIST
|
- _APP_CONSOLE_COUNTRIES_DENYLIST
|
||||||
- _APP_EXPERIMENT_LOGGING_PROVIDER
|
- _APP_EXPERIMENT_LOGGING_PROVIDER
|
||||||
- _APP_EXPERIMENT_LOGGING_CONFIG
|
- _APP_EXPERIMENT_LOGGING_CONFIG
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-realtime:
|
appwrite-realtime:
|
||||||
entrypoint: realtime
|
entrypoint: realtime
|
||||||
|
@ -238,6 +239,7 @@ services:
|
||||||
- _APP_USAGE_STATS
|
- _APP_USAGE_STATS
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-audits:
|
appwrite-worker-audits:
|
||||||
entrypoint: worker-audits
|
entrypoint: worker-audits
|
||||||
|
@ -267,6 +269,7 @@ services:
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-webhooks:
|
appwrite-worker-webhooks:
|
||||||
entrypoint: worker-webhooks
|
entrypoint: worker-webhooks
|
||||||
|
@ -299,6 +302,7 @@ services:
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
|
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-deletes:
|
appwrite-worker-deletes:
|
||||||
entrypoint: worker-deletes
|
entrypoint: worker-deletes
|
||||||
|
@ -356,6 +360,7 @@ services:
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_EXECUTOR_SECRET
|
- _APP_EXECUTOR_SECRET
|
||||||
- _APP_EXECUTOR_HOST
|
- _APP_EXECUTOR_HOST
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-databases:
|
appwrite-worker-databases:
|
||||||
entrypoint: worker-databases
|
entrypoint: worker-databases
|
||||||
|
@ -387,6 +392,7 @@ services:
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_WORKERS_NUM
|
- _APP_WORKERS_NUM
|
||||||
- _APP_QUEUE_NAME
|
- _APP_QUEUE_NAME
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-builds:
|
appwrite-worker-builds:
|
||||||
entrypoint: worker-builds
|
entrypoint: worker-builds
|
||||||
|
@ -452,6 +458,7 @@ services:
|
||||||
- _APP_STORAGE_WASABI_SECRET
|
- _APP_STORAGE_WASABI_SECRET
|
||||||
- _APP_STORAGE_WASABI_REGION
|
- _APP_STORAGE_WASABI_REGION
|
||||||
- _APP_STORAGE_WASABI_BUCKET
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-certificates:
|
appwrite-worker-certificates:
|
||||||
entrypoint: worker-certificates
|
entrypoint: worker-certificates
|
||||||
|
@ -487,6 +494,7 @@ services:
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-functions:
|
appwrite-worker-functions:
|
||||||
entrypoint: worker-functions
|
entrypoint: worker-functions
|
||||||
|
@ -526,6 +534,7 @@ services:
|
||||||
- _APP_DOCKER_HUB_PASSWORD
|
- _APP_DOCKER_HUB_PASSWORD
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-mails:
|
appwrite-worker-mails:
|
||||||
entrypoint: worker-mails
|
entrypoint: worker-mails
|
||||||
|
@ -560,6 +569,7 @@ services:
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_DOMAIN
|
- _APP_DOMAIN
|
||||||
- _APP_OPTIONS_FORCE_HTTPS
|
- _APP_OPTIONS_FORCE_HTTPS
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-messaging:
|
appwrite-worker-messaging:
|
||||||
entrypoint: worker-messaging
|
entrypoint: worker-messaging
|
||||||
|
@ -614,6 +624,7 @@ services:
|
||||||
- _APP_STORAGE_WASABI_SECRET
|
- _APP_STORAGE_WASABI_SECRET
|
||||||
- _APP_STORAGE_WASABI_REGION
|
- _APP_STORAGE_WASABI_REGION
|
||||||
- _APP_STORAGE_WASABI_BUCKET
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-migrations:
|
appwrite-worker-migrations:
|
||||||
entrypoint: worker-migrations
|
entrypoint: worker-migrations
|
||||||
|
@ -649,6 +660,7 @@ services:
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-task-maintenance:
|
appwrite-task-maintenance:
|
||||||
entrypoint: maintenance
|
entrypoint: maintenance
|
||||||
|
@ -686,6 +698,7 @@ services:
|
||||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||||
- _APP_MAINTENANCE_DELAY
|
- _APP_MAINTENANCE_DELAY
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-usage:
|
appwrite-worker-usage:
|
||||||
entrypoint: worker-usage
|
entrypoint: worker-usage
|
||||||
|
@ -717,6 +730,7 @@ services:
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-worker-usage-dump:
|
appwrite-worker-usage-dump:
|
||||||
entrypoint: worker-usage-dump
|
entrypoint: worker-usage-dump
|
||||||
|
@ -748,6 +762,7 @@ services:
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-task-scheduler-functions:
|
appwrite-task-scheduler-functions:
|
||||||
entrypoint: schedule-functions
|
entrypoint: schedule-functions
|
||||||
|
@ -775,6 +790,7 @@ services:
|
||||||
- _APP_DB_SCHEMA
|
- _APP_DB_SCHEMA
|
||||||
- _APP_DB_USER
|
- _APP_DB_USER
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-task-scheduler-messages:
|
appwrite-task-scheduler-messages:
|
||||||
entrypoint: schedule-messages
|
entrypoint: schedule-messages
|
||||||
|
@ -802,6 +818,7 @@ services:
|
||||||
- _APP_DB_SCHEMA
|
- _APP_DB_SCHEMA
|
||||||
- _APP_DB_USER
|
- _APP_DB_USER
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS
|
||||||
|
- _APP_DATABASE_SHARED_TABLES
|
||||||
|
|
||||||
appwrite-assistant:
|
appwrite-assistant:
|
||||||
container_name: appwrite-assistant
|
container_name: appwrite-assistant
|
||||||
|
@ -900,20 +917,7 @@ services:
|
||||||
- MYSQL_USER=${_APP_DB_USER}
|
- MYSQL_USER=${_APP_DB_USER}
|
||||||
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
||||||
- MARIADB_AUTO_UPGRADE=1
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
command: "mysqld --innodb-flush-method=fsync" # add ' --query_cache_size=0' for DB tests
|
command: "mysqld --innodb-flush-method=fsync"
|
||||||
# command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu
|
|
||||||
|
|
||||||
# smtp:
|
|
||||||
# image: appwrite/smtp:1.2.0
|
|
||||||
# container_name: appwrite-smtp
|
|
||||||
# restart: unless-stopped
|
|
||||||
# networks:
|
|
||||||
# - appwrite
|
|
||||||
# environment:
|
|
||||||
# - LOCAL_DOMAINS=@
|
|
||||||
# - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com
|
|
||||||
# - SMARTHOST_HOST=smtp
|
|
||||||
# - SMARTHOST_PORT=587
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.2.4-alpine
|
image: redis:7.2.4-alpine
|
||||||
|
@ -931,14 +935,6 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-redis:/data:rw
|
- appwrite-redis:/data:rw
|
||||||
|
|
||||||
# clamav:
|
|
||||||
# image: appwrite/clamav:1.2.0
|
|
||||||
# container_name: appwrite-clamav
|
|
||||||
# networks:
|
|
||||||
# - appwrite
|
|
||||||
# volumes:
|
|
||||||
# - appwrite-uploads:/storage/uploads
|
|
||||||
|
|
||||||
# Dev Tools Start ------------------------------------------------------------------------------------------
|
# Dev Tools Start ------------------------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
|
||||||
|
|
|
@ -498,14 +498,14 @@ class Deletes extends Action
|
||||||
$collections = $dbForProject->listCollections($limit);
|
$collections = $dbForProject->listCollections($limit);
|
||||||
|
|
||||||
foreach ($collections as $collection) {
|
foreach ($collections as $collection) {
|
||||||
if ($dsn->getHost() !== DATABASE_SHARED_TABLES || !\in_array($collection->getId(), $projectCollectionIds)) {
|
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '') || !\in_array($collection->getId(), $projectCollectionIds)) {
|
||||||
$dbForProject->deleteCollection($collection->getId());
|
$dbForProject->deleteCollection($collection->getId());
|
||||||
} else {
|
} else {
|
||||||
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
|
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$collectionsIds = \array_map(fn ($collection) => $collection->getId(), $collections);
|
$collectionsIds = \array_map(fn ($collection) => $collection->getId(), $collections);
|
||||||
|
|
||||||
if (empty(\array_diff($collectionsIds, $projectCollectionIds))) {
|
if (empty(\array_diff($collectionsIds, $projectCollectionIds))) {
|
||||||
|
@ -554,7 +554,7 @@ class Deletes extends Action
|
||||||
], $dbForConsole);
|
], $dbForConsole);
|
||||||
|
|
||||||
// Delete metadata table
|
// Delete metadata table
|
||||||
if ($dsn->getHost() !== DATABASE_SHARED_TABLES) {
|
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||||
$dbForProject->deleteCollection('_metadata');
|
$dbForProject->deleteCollection('_metadata');
|
||||||
} else {
|
} else {
|
||||||
$this->deleteByGroup('_metadata', [], $dbForProject);
|
$this->deleteByGroup('_metadata', [], $dbForProject);
|
||||||
|
|
|
@ -31,7 +31,7 @@ class HTTPTest extends Scope
|
||||||
$this->assertEquals(204, $response['headers']['status-code']);
|
$this->assertEquals(204, $response['headers']['status-code']);
|
||||||
$this->assertEquals('Appwrite', $response['headers']['server']);
|
$this->assertEquals('Appwrite', $response['headers']['server']);
|
||||||
$this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']);
|
$this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']);
|
||||||
$this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
|
$this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
|
||||||
$this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']);
|
$this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']);
|
||||||
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
|
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
|
||||||
$this->assertEquals('true', $response['headers']['access-control-allow-credentials']);
|
$this->assertEquals('true', $response['headers']['access-control-allow-credentials']);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Tests\E2E\Services\Functions;
|
namespace Tests\E2E\Services\Functions;
|
||||||
|
|
||||||
|
use Appwrite\Tests\Retry;
|
||||||
use CURLFile;
|
use CURLFile;
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
use Tests\E2E\Scopes\ProjectCustom;
|
use Tests\E2E\Scopes\ProjectCustom;
|
||||||
|
@ -42,6 +43,7 @@ class FunctionsCustomClientTest extends Scope
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Retry(count: 2)]
|
||||||
public function testCreateExecution(): array
|
public function testCreateExecution(): array
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -988,7 +988,7 @@ class FunctionsCustomServerTest extends Scope
|
||||||
$this->assertEquals($executions['body']['executions'][0]['logs'], '');
|
$this->assertEquals($executions['body']['executions'][0]['logs'], '');
|
||||||
$this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']);
|
$this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']);
|
||||||
|
|
||||||
sleep(70); //wait for scheduled execution to be created and time out
|
sleep(75); // Wait for scheduled execution to be created and time out
|
||||||
|
|
||||||
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
|
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
|
@ -999,12 +999,6 @@ class FunctionsCustomServerTest extends Scope
|
||||||
$this->assertCount(2, $executions['body']['executions']);
|
$this->assertCount(2, $executions['body']['executions']);
|
||||||
$this->assertIsArray($executions['body']['executions']);
|
$this->assertIsArray($executions['body']['executions']);
|
||||||
$this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule');
|
$this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule');
|
||||||
$this->assertEquals($executions['body']['executions'][1]['status'], 'failed');
|
|
||||||
$this->assertEquals($executions['body']['executions'][1]['responseStatusCode'], 500);
|
|
||||||
$this->assertLessThan(20, $executions['body']['executions'][1]['duration']);
|
|
||||||
$this->assertEquals($executions['body']['executions'][1]['responseBody'], '');
|
|
||||||
$this->assertEquals($executions['body']['executions'][1]['logs'], '');
|
|
||||||
$this->assertStringContainsString('timed out', $executions['body']['executions'][1]['errors']);
|
|
||||||
|
|
||||||
// Cleanup : Delete function
|
// Cleanup : Delete function
|
||||||
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
|
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
|
||||||
|
|
|
@ -9,7 +9,6 @@ use Tests\E2E\General\UsageTest;
|
||||||
use Tests\E2E\Scopes\ProjectConsole;
|
use Tests\E2E\Scopes\ProjectConsole;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\SideClient;
|
use Tests\E2E\Scopes\SideClient;
|
||||||
use Utopia\Database\Database;
|
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
|
@ -3494,504 +3493,4 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTenantIsolation(): void
|
|
||||||
{
|
|
||||||
// Create a team and a project
|
|
||||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'teamId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Team',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$teamId = $team['body']['$id'];
|
|
||||||
|
|
||||||
// Project-level isolation
|
|
||||||
$project1 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
'x-appwrite-shared-tables' => false
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'projectId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Project',
|
|
||||||
'teamId' => $teamId,
|
|
||||||
'region' => 'default'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Application level isolation (shared tables)
|
|
||||||
$project2 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
'x-appwrite-shared-tables' => true
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'projectId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Project',
|
|
||||||
'teamId' => $teamId,
|
|
||||||
'region' => 'default'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Project-level isolation
|
|
||||||
$project3 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
'x-appwrite-shared-tables' => false
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'projectId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Project',
|
|
||||||
'teamId' => $teamId,
|
|
||||||
'region' => 'default'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Application level isolation (shared tables)
|
|
||||||
$project4 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
'x-appwrite-shared-tables' => true
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'projectId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Project',
|
|
||||||
'teamId' => $teamId,
|
|
||||||
'region' => 'default'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create and API key in each project
|
|
||||||
$key1 = $this->client->call(Client::METHOD_POST, '/projects/' . $project1['body']['$id'] . '/keys', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'name' => 'Key Test',
|
|
||||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$key2 = $this->client->call(Client::METHOD_POST, '/projects/' . $project2['body']['$id'] . '/keys', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'name' => 'Key Test',
|
|
||||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$key3 = $this->client->call(Client::METHOD_POST, '/projects/' . $project3['body']['$id'] . '/keys', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'name' => 'Key Test',
|
|
||||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$key4 = $this->client->call(Client::METHOD_POST, '/projects/' . $project4['body']['$id'] . '/keys', array_merge([
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
|
||||||
], $this->getHeaders()), [
|
|
||||||
'name' => 'Key Test',
|
|
||||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create a database in each project
|
|
||||||
$database1 = $this->client->call(Client::METHOD_POST, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Database',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$database2 = $this->client->call(Client::METHOD_POST, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Database',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$database3 = $this->client->call(Client::METHOD_POST, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Database',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$database4 = $this->client->call(Client::METHOD_POST, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Database',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create a collection in each project
|
|
||||||
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database1['body']['$id'],
|
|
||||||
'collectionId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Collection',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database2['body']['$id'],
|
|
||||||
'collectionId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Collection',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$collection3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database3['body']['$id'],
|
|
||||||
'collectionId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Collection',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$collection4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database4['body']['$id'],
|
|
||||||
'collectionId' => ID::unique(),
|
|
||||||
'name' => 'Amazing Collection',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create an attribute in each project
|
|
||||||
$attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes/string', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database1['body']['$id'],
|
|
||||||
'collectionId' => $collection1['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'size' => 255,
|
|
||||||
'required' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
$attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes/string', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database2['body']['$id'],
|
|
||||||
'collectionId' => $collection2['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'size' => 255,
|
|
||||||
'required' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
$attribute3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes/string', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database3['body']['$id'],
|
|
||||||
'collectionId' => $collection3['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'size' => 255,
|
|
||||||
'required' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
$attribute4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes/string', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database4['body']['$id'],
|
|
||||||
'collectionId' => $collection4['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'size' => 255,
|
|
||||||
'required' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Wait for attributes
|
|
||||||
\sleep(2);
|
|
||||||
|
|
||||||
// Create an index in each project
|
|
||||||
$index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database1['body']['$id'],
|
|
||||||
'collectionId' => $collection1['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'type' => Database::INDEX_KEY,
|
|
||||||
'attributes' => [$attribute1['body']['key']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database2['body']['$id'],
|
|
||||||
'collectionId' => $collection2['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'type' => Database::INDEX_KEY,
|
|
||||||
'attributes' => [$attribute2['body']['key']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$index3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database3['body']['$id'],
|
|
||||||
'collectionId' => $collection3['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'type' => Database::INDEX_KEY,
|
|
||||||
'attributes' => [$attribute3['body']['key']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$index4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database4['body']['$id'],
|
|
||||||
'collectionId' => $collection4['body']['$id'],
|
|
||||||
'key' => ID::unique(),
|
|
||||||
'type' => Database::INDEX_KEY,
|
|
||||||
'attributes' => [$attribute4['body']['key']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Wait for indexes
|
|
||||||
\sleep(2);
|
|
||||||
|
|
||||||
// Assert that each project has only 1 database, 1 collection, 1 attribute and 1 index
|
|
||||||
$databasesProject1 = $this->client->call(Client::METHOD_GET, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $databasesProject1['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($databasesProject1['body']['databases']));
|
|
||||||
|
|
||||||
$databasesProject2 = $this->client->call(Client::METHOD_GET, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $databasesProject2['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($databasesProject2['body']['databases']));
|
|
||||||
|
|
||||||
$databasesProject3 = $this->client->call(Client::METHOD_GET, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $databasesProject3['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($databasesProject3['body']['databases']));
|
|
||||||
|
|
||||||
$databasesProject4 = $this->client->call(Client::METHOD_GET, '/databases', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $databasesProject4['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($databasesProject4['body']['databases']));
|
|
||||||
|
|
||||||
$collectionsProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $collectionsProject1['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($collectionsProject1['body']['collections']));
|
|
||||||
|
|
||||||
$collectionsProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $collectionsProject2['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($collectionsProject2['body']['collections']));
|
|
||||||
|
|
||||||
$collectionsProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $collectionsProject3['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($collectionsProject3['body']['collections']));
|
|
||||||
|
|
||||||
$collectionsProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $collectionsProject4['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($collectionsProject4['body']['collections']));
|
|
||||||
|
|
||||||
$attributesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $attributesProject1['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($attributesProject1['body']['attributes']));
|
|
||||||
$this->assertEquals('available', $attributesProject1['body']['attributes'][0]['status']);
|
|
||||||
|
|
||||||
$attributesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $attributesProject2['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($attributesProject2['body']['attributes']));
|
|
||||||
$this->assertEquals('available', $attributesProject2['body']['attributes'][0]['status']);
|
|
||||||
|
|
||||||
$attributesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $attributesProject3['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($attributesProject3['body']['attributes']));
|
|
||||||
$this->assertEquals('available', $attributesProject3['body']['attributes'][0]['status']);
|
|
||||||
|
|
||||||
$attributesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $attributesProject4['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($attributesProject4['body']['attributes']));
|
|
||||||
$this->assertEquals('available', $attributesProject4['body']['attributes'][0]['status']);
|
|
||||||
|
|
||||||
$indexesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $indexesProject1['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($indexesProject1['body']['indexes']));
|
|
||||||
|
|
||||||
$indexesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $indexesProject2['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($indexesProject2['body']['indexes']));
|
|
||||||
|
|
||||||
$indexesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $indexesProject3['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($indexesProject3['body']['indexes']));
|
|
||||||
|
|
||||||
$indexesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(1, $indexesProject4['body']['total']);
|
|
||||||
$this->assertEquals(1, \count($indexesProject4['body']['indexes']));
|
|
||||||
|
|
||||||
// Attempt to read cross-type resources
|
|
||||||
$collectionProject2WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'], [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(404, $collectionProject2WithProject1Key['headers']['status-code']);
|
|
||||||
|
|
||||||
$collectionProject1WithProject2Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(404, $collectionProject1WithProject2Key['headers']['status-code']);
|
|
||||||
|
|
||||||
// Attempt to read cross-tenant resources
|
|
||||||
$collectionProject3WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'], [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project1['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key1['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(404, $collectionProject3WithProject1Key['headers']['status-code']);
|
|
||||||
|
|
||||||
$collectionProject1WithProject3Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project3['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key3['body']['secret']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(404, $collectionProject1WithProject3Key['headers']['status-code']);
|
|
||||||
|
|
||||||
// Assert that shared project resources can have the same ID as they're unique on tenant + ID not just ID
|
|
||||||
$collection5 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'databaseId' => $database2['body']['$id'],
|
|
||||||
'collectionId' => $collection4['body']['$id'],
|
|
||||||
'name' => 'Amazing Collection',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(201, $collection5['headers']['status-code']);
|
|
||||||
|
|
||||||
// Assert that users across projects on shared tables can have the same email as they're unique on tenant + email not just email
|
|
||||||
$user1 = $this->client->call(Client::METHOD_POST, '/users', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project2['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key2['body']['secret']
|
|
||||||
], [
|
|
||||||
'userId' => 'user',
|
|
||||||
'email' => 'test@appwrite.io',
|
|
||||||
'password' => 'password',
|
|
||||||
'name' => 'Test User',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(201, $user1['headers']['status-code']);
|
|
||||||
|
|
||||||
$user2 = $this->client->call(Client::METHOD_POST, '/users', [
|
|
||||||
'content-type' => 'application/json',
|
|
||||||
'x-appwrite-project' => $project4['body']['$id'],
|
|
||||||
'x-appwrite-key' => $key4['body']['secret']
|
|
||||||
], [
|
|
||||||
'userId' => 'user',
|
|
||||||
'email' => 'test@appwrite.io',
|
|
||||||
'password' => 'password',
|
|
||||||
'name' => 'Test User',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals(201, $user2['headers']['status-code']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue