mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'master' into feat-storage-buckets
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
// Reference Material
|
||||
// https://developer.yammer.com/docs/oauth-2
|
||||
|
||||
class Yammer extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpoint = 'https://www.yammer.com/oauth2/';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'yammer';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return $this->endpoint . 'oauth2/authorize?'.
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(string $code): string
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
|
||||
$accessToken = $this->request(
|
||||
'POST',
|
||||
$this->endpoint . 'access_token?',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'code' => $code,
|
||||
'grant_type' => 'authorization_code'
|
||||
])
|
||||
);
|
||||
|
||||
$accessToken = \json_decode($accessToken, true);
|
||||
|
||||
if (isset($accessToken['access_token']['token'])) {
|
||||
return $accessToken['access_token']['token'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['email'])) {
|
||||
return $user['email'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['full_name'])) {
|
||||
return $user['full_name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$headers = ['Authorization: Bearer '. \urlencode($accessToken)];
|
||||
$user = $this->request('GET', 'https://www.yammer.com/api/v1/users/current.json', $headers);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,26 @@ class Document extends ArrayObject
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Document Attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
foreach ($this as $attribute => $value) {
|
||||
if(array_key_exists($attribute, ['$id' => true, '$permissions' => true, '$collection' => true, '$execute' => []])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[$attribute] = $value;
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Attribute.
|
||||
*
|
||||
@@ -215,7 +235,7 @@ class Document extends ArrayObject
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArrayCopy(array $whitelist = [], array $blacklist = [])
|
||||
public function getArrayCopy(array $whitelist = [], array $blacklist = []): array
|
||||
{
|
||||
$array = parent::getArrayCopy();
|
||||
|
||||
|
||||
@@ -2,34 +2,50 @@
|
||||
|
||||
namespace Appwrite\Migration;
|
||||
|
||||
use Appwrite\Database\Document;
|
||||
use Appwrite\Database\Database;
|
||||
use Appwrite\Database\Document as OldDocument;
|
||||
use Appwrite\Database\Database as OldDatabase;
|
||||
use PDO;
|
||||
use Redis;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Exception;
|
||||
|
||||
abstract class Migration
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $options;
|
||||
|
||||
/**
|
||||
* @var PDO
|
||||
*/
|
||||
protected $db;
|
||||
protected PDO $db;
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
protected Redis $cache;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $limit = 50;
|
||||
protected int $limit = 500;
|
||||
|
||||
/**
|
||||
* @var Document
|
||||
* @var OldDocument
|
||||
*/
|
||||
protected $project;
|
||||
protected OldDocument $project;
|
||||
|
||||
/**
|
||||
* @var Database
|
||||
* @var OldDatabase
|
||||
*/
|
||||
protected $projectDB;
|
||||
protected OldDatabase $oldProjectDB;
|
||||
|
||||
/**
|
||||
* @var OldDatabase
|
||||
*/
|
||||
protected OldDatabase $oldConsoleDB;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -50,32 +66,45 @@ abstract class Migration
|
||||
'0.10.4' => 'V09',
|
||||
'0.11.0' => 'V10',
|
||||
'0.12.0' => 'V10',
|
||||
'0.13.0' => 'V10',
|
||||
'0.12.0' => 'V11',
|
||||
'0.13.0' => 'V11',
|
||||
];
|
||||
|
||||
/**
|
||||
* Migration constructor.
|
||||
*
|
||||
* @param PDO $pdo
|
||||
* @param PDO $db
|
||||
* @param Redis|null $cache
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(PDO $db)
|
||||
public function __construct(PDO $db, Redis $cache = null, array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->db = $db;
|
||||
if (!is_null($cache)) {
|
||||
$this->cache = $cache;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set project for migration.
|
||||
*
|
||||
* @param Document $project
|
||||
* @param Database $projectDB
|
||||
* @param OldDocument $project
|
||||
* @param OldDatabase $projectDB
|
||||
* @param OldDatabase $oldConsoleDB
|
||||
*
|
||||
* @return Migration
|
||||
* @return self
|
||||
*/
|
||||
public function setProject(Document $project, Database $projectDB): Migration
|
||||
public function setProject(OldDocument $project, OldDatabase $projectDB, OldDatabase $oldConsoleDB): self
|
||||
{
|
||||
$this->project = $project;
|
||||
$this->projectDB = $projectDB;
|
||||
$this->projectDB->setNamespace('app_' . $project->getId());
|
||||
|
||||
$this->oldProjectDB = $projectDB;
|
||||
$this->oldProjectDB->setNamespace('app_' . $project->getId());
|
||||
|
||||
$this->oldConsoleDB = $oldConsoleDB;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -118,7 +147,7 @@ abstract class Migration
|
||||
}
|
||||
|
||||
try {
|
||||
$new = $this->projectDB->overwriteDocument($document->getArrayCopy());
|
||||
$new = $this->projectDB->overwriteDocument($new->getArrayCopy());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to update document: ' . $th->getMessage());
|
||||
return;
|
||||
@@ -135,7 +164,14 @@ abstract class Migration
|
||||
}
|
||||
}
|
||||
|
||||
public function check_diff_multi($array1, $array2)
|
||||
/**
|
||||
* Checks 2 arrays for differences.
|
||||
*
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return array
|
||||
*/
|
||||
public function check_diff_multi(array $array1, array $array2): array
|
||||
{
|
||||
$result = array();
|
||||
|
||||
|
||||
@@ -0,0 +1,821 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Migration\Version;
|
||||
|
||||
use Appwrite\Database\Database as OldDatabase;
|
||||
use Appwrite\Database\Document as OldDocument;
|
||||
use Appwrite\Migration\Migration;
|
||||
use Exception;
|
||||
use PDO;
|
||||
use Redis;
|
||||
use Swoole\Runtime;
|
||||
use Throwable;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Limit;
|
||||
use Utopia\Database\Exception\Authorization as ExceptionAuthorization;
|
||||
use Utopia\Database\Exception\Structure;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
global $register;
|
||||
|
||||
class V11 extends Migration
|
||||
{
|
||||
protected Database $dbProject;
|
||||
protected Database $dbConsole;
|
||||
|
||||
protected array $oldCollections;
|
||||
protected array $newCollections;
|
||||
|
||||
public function __construct(PDO $db, Redis $cache = null, array $options = [])
|
||||
{
|
||||
parent::__construct($db, $cache, $options);
|
||||
$this->options = array_map(fn ($option) => $option === 'yes' ? true : false, $this->options);
|
||||
|
||||
if (!is_null($cache)) {
|
||||
$this->cache->flushAll();
|
||||
$cacheAdapter = new Cache(new RedisCache($this->cache));
|
||||
$this->dbProject = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution
|
||||
$this->dbConsole = new Database(new MariaDB($this->db), $cacheAdapter);
|
||||
|
||||
$this->dbProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$this->dbConsole->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$this->dbConsole->setNamespace('_project_console');
|
||||
}
|
||||
|
||||
$this->newCollections = Config::getParam('collections', []);
|
||||
$this->oldCollections = Config::getParam('collectionsold', []);
|
||||
}
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
Authorization::disable();
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
||||
$oldProject = $this->project;
|
||||
|
||||
$this->dbProject->setNamespace('_project_' . $oldProject->getId());
|
||||
$this->dbConsole->setNamespace('_project_console');
|
||||
|
||||
Console::info('');
|
||||
Console::info('------------------------------------');
|
||||
Console::info('Migrating project ' . $oldProject->getAttribute('name'));
|
||||
Console::info('------------------------------------');
|
||||
|
||||
/**
|
||||
* Create internal/external structure for projects and skip the console project.
|
||||
*/
|
||||
if ($oldProject->getId() !== 'console') {
|
||||
try {
|
||||
$project = $this->dbConsole->getDocument(collection: 'projects', id: $oldProject->getId());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getTraceAsString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Project Document.
|
||||
*/
|
||||
if ($project->isEmpty()) {
|
||||
$newProject = $this->fixDocument($oldProject);
|
||||
$newProject->setAttribute('version', '0.12.0');
|
||||
$project = $this->dbConsole->createDocument('projects', $newProject);
|
||||
Console::log('Created project document: ' . $oldProject->getAttribute('name') . ' (' . $oldProject->getId() . ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create internal tables
|
||||
*/
|
||||
try {
|
||||
Console::log('Created internal tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
|
||||
$this->dbProject->createMetadata();
|
||||
} catch (\Throwable $th) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Audit tables
|
||||
*/
|
||||
if ($this->dbProject->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
$audit = new Audit($this->dbProject);
|
||||
$audit->setup();
|
||||
Console::log('Created audit tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Abuse tables
|
||||
*/
|
||||
if ($this->dbProject->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
|
||||
$adapter = new TimeLimit("", 0, 1, $this->dbProject);
|
||||
$adapter->setup();
|
||||
Console::log('Created abuse tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create internal collections for Project
|
||||
*/
|
||||
foreach ($this->newCollections as $key => $collection) {
|
||||
if (!$this->dbProject->getCollection($key)->isEmpty()) continue; // Skip if project collection already exists
|
||||
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'array' => $attribute['array'],
|
||||
'filters' => $attribute['filters'],
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($collection['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->dbProject->createCollection($key, $attributes, $indexes);
|
||||
}
|
||||
if ($this->options['migrateCollections']) {
|
||||
$this->migrateExternalCollections();
|
||||
}
|
||||
} else {
|
||||
Console::log('Skipped console project migration.');
|
||||
}
|
||||
|
||||
$sum = $this->limit;
|
||||
$offset = 0;
|
||||
$total = 0;
|
||||
|
||||
/**
|
||||
* Migrate internal documents
|
||||
*/
|
||||
while ($sum >= $this->limit) {
|
||||
$all = $this->oldProjectDB->getCollection([
|
||||
'limit' => $this->limit,
|
||||
'offset' => $offset,
|
||||
'orderType' => 'DESC',
|
||||
'filters' => [
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_RULES,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_TASKS,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_PROJECTS,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_CONNECTIONS,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_RESERVED,
|
||||
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_TOKENS,
|
||||
]
|
||||
]);
|
||||
|
||||
$sum = \count($all);
|
||||
|
||||
Console::log('Migrating Internal Documents: ' . $offset . ' / ' . $this->oldProjectDB->getSum());
|
||||
|
||||
foreach ($all as $document) {
|
||||
if (
|
||||
!array_key_exists($document->getCollection(), $this->oldCollections)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new = $this->fixDocument($document);
|
||||
|
||||
if (is_null($new) || empty($new->getId())) {
|
||||
Console::warning('Skipped Document due to missing ID.');
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->dbProject->getDocument($new->getCollection(), $new->getId())->isEmpty()) {
|
||||
$this->dbProject->createDocument($new->getCollection(), $new);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Failed to migrate document ({$new->getId()}) from collection ({$new->getCollection()}): " . $th->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$offset += $this->limit;
|
||||
$total += $sum;
|
||||
}
|
||||
Console::log('Migrated ' . $total . ' Internal Documents.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate external collections for Project
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Throwable
|
||||
* @throws Limit
|
||||
* @throws ExceptionAuthorization
|
||||
* @throws Structure
|
||||
*/
|
||||
protected function migrateExternalCollections(): void
|
||||
{
|
||||
$sum = $this->limit;
|
||||
$offset = 0;
|
||||
|
||||
while ($sum >= $this->limit) {
|
||||
$databaseCollections = $this->oldProjectDB->getCollection([
|
||||
'limit' => $this->limit,
|
||||
'offset' => $offset,
|
||||
'orderType' => 'DESC',
|
||||
'filters' => [
|
||||
'$collection=' . OldDatabase::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
]
|
||||
]);
|
||||
|
||||
$sum = \count($databaseCollections);
|
||||
Console::log('Migrating Collections: ' . $offset . ' / ' . $this->oldProjectDB->getSum());
|
||||
|
||||
foreach ($databaseCollections as $oldCollection) {
|
||||
$id = $oldCollection->getId();
|
||||
$permissions = $oldCollection->getPermissions();
|
||||
$name = $oldCollection->getAttribute('name');
|
||||
$newCollection = $this->dbProject->getCollection('collection_' . $id);
|
||||
|
||||
if ($newCollection->isEmpty()) {
|
||||
$this->dbProject->createCollection('collection_' . $id);
|
||||
/**
|
||||
* Migrate permissions
|
||||
*/
|
||||
$read = $this->migrateWildcardPermissions($permissions['read'] ?? []);
|
||||
$write = $this->migrateWildcardPermissions($permissions['write'] ?? []);
|
||||
|
||||
/**
|
||||
* Suffix collection name with a subsequent number to make it unique if possible.
|
||||
*/
|
||||
$suffix = 1;
|
||||
while ($this->dbProject->findOne('collections', [
|
||||
new Query('name', Query::TYPE_EQUAL, [$name])
|
||||
])) {
|
||||
$name .= ' - ' . $suffix++;
|
||||
}
|
||||
|
||||
$this->dbProject->createDocument('collections', new Document([
|
||||
'$id' => $id,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'permission' => 'document',
|
||||
'dateCreated' => time(),
|
||||
'dateUpdated' => time(),
|
||||
'name' => substr($name, 0, 256),
|
||||
'enabled' => true,
|
||||
'search' => implode(' ', [$id, $name]),
|
||||
]));
|
||||
} else {
|
||||
Console::warning('Skipped Collection ' . $newCollection->getId() . ' from ' . $newCollection->getCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate collection rules to attributes
|
||||
*/
|
||||
$attributes = $this->getCollectionAttributes($oldCollection);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
try {
|
||||
$this->dbProject->createAttribute(
|
||||
collection: 'collection_' . $attribute['$collection'],
|
||||
id: $attribute['$id'],
|
||||
type: $attribute['type'],
|
||||
size: $attribute['size'],
|
||||
required: $attribute['required'],
|
||||
default: $attribute['default'],
|
||||
signed: $attribute['signed'],
|
||||
array: $attribute['array'],
|
||||
format: $attribute['format'] ?? null,
|
||||
formatOptions: $attribute['formatOptions'] ?? [],
|
||||
filters: $attribute['filters']
|
||||
);
|
||||
$this->dbProject->createDocument('attributes', new Document([
|
||||
'$id' => $attribute['$collection'] . '_' . $attribute['$id'],
|
||||
'key' => $attribute['$id'],
|
||||
'collectionId' => $attribute['$collection'],
|
||||
'type' => $attribute['type'],
|
||||
'status' => 'available',
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
'signed' => $attribute['signed'],
|
||||
'default' => $attribute['default'],
|
||||
'array' => $attribute['array'],
|
||||
'format' => $attribute['format'] ?? null,
|
||||
'formatOptions' => $attribute['formatOptions'] ?? null,
|
||||
'filters' => $attribute['filters']
|
||||
]));
|
||||
|
||||
Console::log('Created "' . $attribute['$id'] . '" attribute in collection: ' . $name);
|
||||
} catch (\Throwable $th) {
|
||||
Console::log($th->getMessage() . ' - ("' . $attribute['$id'] . '" attribute in collection ' . $name . ')');
|
||||
}
|
||||
}
|
||||
if ($this->options['migrateDocuments']) {
|
||||
$this->migrateExternalDocuments(collection: $id);
|
||||
}
|
||||
}
|
||||
$offset += $this->limit;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Migrate all external documents
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Throwable
|
||||
* @throws ExceptionAuthorization
|
||||
* @throws Structure
|
||||
*/
|
||||
protected function migrateExternalDocuments(string $collection): void
|
||||
{
|
||||
$sum = $this->limit;
|
||||
$offset = 0;
|
||||
|
||||
while ($sum >= $this->limit) {
|
||||
$allDocs = $this->oldProjectDB->getCollection([
|
||||
'limit' => $this->limit,
|
||||
'offset' => $offset,
|
||||
'orderType' => 'DESC',
|
||||
'filters' => [
|
||||
'$collection=' . $collection
|
||||
]
|
||||
]);
|
||||
|
||||
$sum = \count($allDocs);
|
||||
|
||||
Console::log('Migrating External Documents for Collection ' . $collection . ': ' . $offset . ' / ' . $this->oldProjectDB->getSum());
|
||||
|
||||
foreach ($allDocs as $document) {
|
||||
if (!$this->dbProject->getDocument('collection_' . $collection, $document->getId())->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
go(function ($document) {
|
||||
foreach ($document as $key => $attr) {
|
||||
/**
|
||||
* Convert nested Document to JSON strings.
|
||||
*/
|
||||
if ($document->getAttribute($key) instanceof OldDocument) {
|
||||
$document[$key] = json_encode($this->fixDocument($attr)->getArrayCopy());
|
||||
}
|
||||
/**
|
||||
* Convert numeric Attributes to float.
|
||||
*/
|
||||
if (!is_string($attr) && is_numeric($attr)) {
|
||||
$document[$key] = floatval($attr);
|
||||
}
|
||||
|
||||
if (\is_array($attr)) {
|
||||
foreach ($attr as $index => $child) {
|
||||
/**
|
||||
* Convert array of nested Document to array JSON strings.
|
||||
*/
|
||||
if ($document->getAttribute($key)[$index] instanceof OldDocument) {
|
||||
$document[$key][$index] = json_encode($this->fixDocument($child)->getArrayCopy());
|
||||
}
|
||||
/**
|
||||
* Convert array of numeric Attributes to array float.
|
||||
*/
|
||||
if (!is_string($child) && is_numeric($child)) {
|
||||
$document[$key][$index] = floatval($child); // Convert any numeric to float
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, $document);
|
||||
$document = new Document($document->getArrayCopy());
|
||||
$document = $this->migratePermissions($document);
|
||||
|
||||
try {
|
||||
$this->dbProject->createDocument('collection_' . $collection, $document);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Failed to migrate document ({$document->getId()}): " . $th->getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$offset += $this->limit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates single docuemnt.
|
||||
*
|
||||
* @param OldDocument $oldDocument
|
||||
* @return Document|null
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function fixDocument(OldDocument $oldDocument): Document|null
|
||||
{
|
||||
$document = new Document($oldDocument->getArrayCopy());
|
||||
$document = $this->migratePermissions($document);
|
||||
|
||||
/**
|
||||
* Check attributes and set their default values.
|
||||
*/
|
||||
if (array_key_exists($document->getCollection(), $this->oldCollections)) {
|
||||
foreach ($this->newCollections[$document->getCollection()]['attributes'] ?? [] as $attr) {
|
||||
if (
|
||||
(!$attr['array'] ||
|
||||
($attr['array'] && array_key_exists('filter', $attr)
|
||||
&& in_array('json', $attr['filter'])))
|
||||
&& empty($document->getAttribute($attr['$id'], null))
|
||||
) {
|
||||
$document->setAttribute($attr['$id'], $attr['default'] ?? null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($document->getAttribute('$collection')) {
|
||||
case OldDatabase::SYSTEM_COLLECTION_PROJECTS:
|
||||
$newProviders = [];
|
||||
$newAuths = [];
|
||||
$providers = Config::getParam('providers', []);
|
||||
$auths = Config::getParam('auth', []);
|
||||
|
||||
/**
|
||||
* Remove Tasks
|
||||
*/
|
||||
$document->removeAttribute('tasks');
|
||||
|
||||
/*
|
||||
* Add enabled OAuth2 providers to default data rules
|
||||
*/
|
||||
foreach ($providers as $index => $provider) {
|
||||
$appId = $document->getAttribute('usersOauth2' . \ucfirst($index) . 'Appid');
|
||||
$appSecret = $document->getAttribute('usersOauth2' . \ucfirst($index) . 'Secret');
|
||||
|
||||
if (!is_null($appId) || !is_null($appId)) {
|
||||
$newProviders[$appId] = $appSecret;
|
||||
}
|
||||
|
||||
$document
|
||||
->removeAttribute('usersOauth2' . \ucfirst($index) . 'Appid')
|
||||
->removeAttribute('usersOauth2' . \ucfirst($index) . 'Secret');
|
||||
}
|
||||
|
||||
$document->setAttribute('providers', $newProviders);
|
||||
|
||||
/*
|
||||
* Migrate User providers settings
|
||||
*/
|
||||
$oldAuths = [
|
||||
'email-password' => 'usersAuthEmailPassword',
|
||||
'magic-url' => 'usersAuthMagicURL',
|
||||
'anonymous' => 'usersAuthAnonymous',
|
||||
'invites' => 'usersAuthInvites',
|
||||
'jwt' => 'usersAuthJWT',
|
||||
'phone' => 'usersAuthPhone'
|
||||
];
|
||||
|
||||
foreach ($oldAuths as $index => $auth) {
|
||||
$enabled = $document->getAttribute($auth, true);
|
||||
$newAuths['auth' . \ucfirst($auths[$index]['key'])] = $enabled;
|
||||
$document->removeAttribute($auth);
|
||||
}
|
||||
|
||||
if (!empty($document->getAttribute('usersAuthLimit'))) {
|
||||
$newAuths['limit'] = $document->getAttribute('usersAuthLimit');
|
||||
}
|
||||
|
||||
$document->removeAttribute('usersAuthLimit');
|
||||
|
||||
$document->setAttribute('auths', $newProviders);
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_PLATFORMS:
|
||||
$projectId = $this->getProjectIdFromReadPermissions($document);
|
||||
|
||||
if (is_null($projectId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Project ID
|
||||
*/
|
||||
if ($document->getAttribute('projectId') === null) {
|
||||
$document->setAttribute('projectId', $projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set empty key and store if null
|
||||
*/
|
||||
if ($document->getAttribute('key') === null) {
|
||||
$document->setAttribute('key', '');
|
||||
}
|
||||
if ($document->getAttribute('store') === null) {
|
||||
$document->setAttribute('store', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Permissions
|
||||
*/
|
||||
$document->setAttribute('$read', ['role:all']);
|
||||
$document->setAttribute('$write', ['role:all']);
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_CERTIFICATES:
|
||||
/**
|
||||
* Replace certificateId attribute.
|
||||
*/
|
||||
if ($document->getAttribute('certificateId') !== null) {
|
||||
$document->setAttribute('$id', $document->getAttribute('certificateId'));
|
||||
$document->removeAttribute('certificateId');
|
||||
}
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_DOMAINS:
|
||||
$projectId = $this->getProjectIdFromReadPermissions($document);
|
||||
|
||||
if (is_null($projectId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Project ID
|
||||
*/
|
||||
if ($document->getAttribute('projectId') === null) {
|
||||
$document->setAttribute('projectId', $projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set empty verification if null
|
||||
*/
|
||||
if ($document->getAttribute('verification') === null) {
|
||||
$document->setAttribute('verification', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Permissions
|
||||
*/
|
||||
$document->setAttribute('$read', ['role:all']);
|
||||
$document->setAttribute('$write', ['role:all']);
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_KEYS:
|
||||
$projectId = $this->getProjectIdFromReadPermissions($document);
|
||||
|
||||
if (is_null($projectId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Project ID
|
||||
*/
|
||||
if ($document->getAttribute('projectId') === null) {
|
||||
$document->setAttribute('projectId', $projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scopes if empty
|
||||
*/
|
||||
if (empty($document->getAttribute('scopes', []))) {
|
||||
$document->setAttribute('scopes', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Permissions
|
||||
*/
|
||||
$document->setAttribute('$read', ['role:all']);
|
||||
$document->setAttribute('$write', ['role:all']);
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_FUNCTIONS:
|
||||
$document->setAttribute('events', $document->getAttribute('events', []));
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_WEBHOOKS:
|
||||
$projectId = $this->getProjectIdFromReadPermissions($document);
|
||||
|
||||
if (is_null($projectId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Project ID
|
||||
*/
|
||||
if ($document->getAttribute('projectId') === null) {
|
||||
$document->setAttribute('projectId', $projectId);
|
||||
}
|
||||
|
||||
$document->setAttribute('events', $document->getAttribute('events', []));
|
||||
|
||||
/**
|
||||
* Reset Permissions
|
||||
*/
|
||||
$document->setAttribute('$read', ['role:all']);
|
||||
$document->setAttribute('$write', ['role:all']);
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_USERS:
|
||||
/**
|
||||
* Set deleted attribute to false
|
||||
*/
|
||||
if ($document->getAttribute('deleted') === null) {
|
||||
$document->setAttribute('deleted', false);
|
||||
}
|
||||
/**
|
||||
* Remove deprecated user status 0 and replace with boolean.
|
||||
*/
|
||||
if ($document->getAttribute('status') === 2) {
|
||||
$document->setAttribute('status', false);
|
||||
} else {
|
||||
$document->setAttribute('status', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default values for arrays if not set.
|
||||
*/
|
||||
if (empty($document->getAttribute('prefs', []))) {
|
||||
$document->setAttribute('prefs', new \stdClass());
|
||||
}
|
||||
if (empty($document->getAttribute('sessions', []))) {
|
||||
$document->setAttribute('sessions', []);
|
||||
}
|
||||
if (empty($document->getAttribute('tokens', []))) {
|
||||
$document->setAttribute('tokens', []);
|
||||
}
|
||||
if (empty($document->getAttribute('memberships', []))) {
|
||||
$document->setAttribute('memberships', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace user:{self} with user:USER_ID
|
||||
*/
|
||||
$write = $document->getWrite();
|
||||
$document->setAttribute('$write', str_replace('user:{self}', "user:{$document->getId()}", $write));
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_TEAMS:
|
||||
|
||||
/**
|
||||
* Replace team:{self} with team:TEAM_ID
|
||||
*/
|
||||
$read = $document->getWrite();
|
||||
$write = $document->getWrite();
|
||||
|
||||
$document->setAttribute('$read', str_replace('team:{self}', "team:{$document->getId()}", $read));
|
||||
$document->setAttribute('$write', str_replace('team:{self}', "team:{$document->getId()}", $write));
|
||||
|
||||
break;
|
||||
case OldDatabase::SYSTEM_COLLECTION_FILES:
|
||||
/**
|
||||
* Migrating breakind changes on Files.
|
||||
*/
|
||||
if (!empty($document->getAttribute('fileOpenSSLVersion', null))) {
|
||||
$document
|
||||
->setAttribute('openSSLVersion', $document->getAttribute('fileOpenSSLVersion'))
|
||||
->removeAttribute('fileOpenSSLVersion');
|
||||
}
|
||||
if (!empty($document->getAttribute('fileOpenSSLCipher', null))) {
|
||||
$document
|
||||
->setAttribute('openSSLCipher', $document->getAttribute('fileOpenSSLCipher'))
|
||||
->removeAttribute('fileOpenSSLCipher');
|
||||
}
|
||||
if (!empty($document->getAttribute('fileOpenSSLTag', null))) {
|
||||
$document
|
||||
->setAttribute('openSSLTag', $document->getAttribute('fileOpenSSLTag'))
|
||||
->removeAttribute('fileOpenSSLTag');
|
||||
}
|
||||
if (!empty($document->getAttribute('fileOpenSSLIV', null))) {
|
||||
$document
|
||||
->setAttribute('openSSLIV', $document->getAttribute('fileOpenSSLIV'))
|
||||
->removeAttribute('fileOpenSSLIV');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deprecated attributes.
|
||||
*/
|
||||
$document->removeAttribute('folderId');
|
||||
$document->removeAttribute('token');
|
||||
break;
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates $permissions to independent $read and $write.
|
||||
* @param Document $document
|
||||
* @return Document
|
||||
*/
|
||||
protected function migratePermissions(Document $document): Document
|
||||
{
|
||||
if ($document->isSet('$permissions')) {
|
||||
$permissions = $document->getAttribute('$permissions', []);
|
||||
$read = $this->migrateWildcardPermissions($permissions['read'] ?? []);
|
||||
$write = $this->migrateWildcardPermissions($permissions['write'] ?? []);
|
||||
$document->setAttribute('$read', $read);
|
||||
$document->setAttribute('$write', $write);
|
||||
$document->removeAttribute('$permissions');
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a permissions array and replaces wildcard * with role:all.
|
||||
* @param array $permissions
|
||||
* @return array
|
||||
*/
|
||||
protected function migrateWildcardPermissions(array $permissions): array
|
||||
{
|
||||
return array_map(function ($permission) {
|
||||
if ($permission === '*') return 'role:all';
|
||||
return $permission;
|
||||
}, $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new collection attributes from old collection rules.
|
||||
* @param OldDocument $collection
|
||||
* @return array
|
||||
*/
|
||||
protected function getCollectionAttributes(OldDocument $collection): array
|
||||
{
|
||||
$attributes = [];
|
||||
foreach ($collection->getAttribute('rules', []) as $key => $value) {
|
||||
$collectionId = $collection->getId();
|
||||
$id = $value['key'];
|
||||
$array = $value['array'] ?? false;
|
||||
$required = $value['required'] ?? false;
|
||||
$default = $value['default'] ?? null;
|
||||
$default = match ($value['type']) {
|
||||
OldDatabase::SYSTEM_VAR_TYPE_NUMERIC => floatval($default),
|
||||
default => $default
|
||||
};
|
||||
$type = match ($value['type']) {
|
||||
OldDatabase::SYSTEM_VAR_TYPE_TEXT => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_EMAIL => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_DOCUMENT => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_IP => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_URL => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_WILDCARD => Database::VAR_STRING,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_NUMERIC => Database::VAR_FLOAT,
|
||||
OldDatabase::SYSTEM_VAR_TYPE_BOOLEAN => Database::VAR_BOOLEAN,
|
||||
default => Database::VAR_STRING
|
||||
};
|
||||
|
||||
$size = $type === Database::VAR_STRING ? 65_535 : 0; // Max size of text in MariaDB
|
||||
|
||||
if ($required) {
|
||||
$default = null;
|
||||
}
|
||||
|
||||
$attributes[$key] = [
|
||||
'$collection' => $collectionId,
|
||||
'$id' => $id,
|
||||
'type' => $type,
|
||||
'size' => $size,
|
||||
'required' => $required,
|
||||
'default' => $default,
|
||||
'array' => $array,
|
||||
'signed' => true,
|
||||
'filters' => []
|
||||
];
|
||||
|
||||
if ($type === Database::VAR_FLOAT) {
|
||||
$attributes[$key]['format'] = APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
|
||||
$attributes[$key]['formatOptions'] = [];
|
||||
$attributes[$key]['formatOptions']['min'] = -PHP_FLOAT_MAX;
|
||||
$attributes[$key]['formatOptions']['max'] = PHP_FLOAT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document $document
|
||||
* @return string|null
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getProjectIdFromReadPermissions(Document $document): string|null
|
||||
{
|
||||
$readPermissions = $document->getRead();
|
||||
$teamId = str_replace('team:', '', reset($readPermissions));
|
||||
$project = $this->oldConsoleDB->getCollectionFirst([
|
||||
'filters' => [
|
||||
'$collection=' . OldDatabase::SYSTEM_COLLECTION_PROJECTS,
|
||||
'teamId=' . $teamId
|
||||
]
|
||||
]);
|
||||
|
||||
if (!$project) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $project->getId();
|
||||
}
|
||||
}
|
||||
+121
-31
@@ -2,61 +2,156 @@
|
||||
|
||||
namespace Appwrite\Resque;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
|
||||
use Exception;
|
||||
abstract class Worker
|
||||
{
|
||||
/**
|
||||
* Callbacks that will be executed when an error occurs
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static protected array $errorCallbacks = [];
|
||||
|
||||
/**
|
||||
* Associative array holding all information passed into the worker
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public array $args = [];
|
||||
|
||||
abstract public function init(): void;
|
||||
/**
|
||||
* Function for identifying the worker needs to be set to unique name
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
}
|
||||
|
||||
abstract public function run(): void;
|
||||
/**
|
||||
* Function executed before running first task.
|
||||
* Can include any preparations, such as connecting to external services or loading files
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function init() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
}
|
||||
|
||||
abstract public function shutdown(): void;
|
||||
/**
|
||||
* Function executed when new task requests is received.
|
||||
* You can access $args here, it will contain event information
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function run() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
}
|
||||
|
||||
/**
|
||||
* Function executed just before shutting down the worker.
|
||||
* You can do cleanup here, such as disconnecting from services or removing temp files
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function shutdown() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
}
|
||||
|
||||
const MAX_ATTEMPTS = 10;
|
||||
const SLEEP_TIME = 2;
|
||||
|
||||
const DATABASE_INTERNAL = 'internal';
|
||||
const DATABASE_EXTERNAL = 'external';
|
||||
const DATABASE_PROJECT = 'project';
|
||||
const DATABASE_CONSOLE = 'console';
|
||||
|
||||
/**
|
||||
* A wrapper around 'init' function with non-worker-specific code
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->init();
|
||||
try {
|
||||
$this->init();
|
||||
} catch(\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "init", $this->getName());
|
||||
}
|
||||
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around 'run' function with non-worker-specific code
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function perform(): void
|
||||
{
|
||||
$this->run();
|
||||
try {
|
||||
$this->run();
|
||||
} catch(\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "run", $this->getName(), $this->args);
|
||||
}
|
||||
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around 'shutdown' function with non-worker-specific code
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->shutdown();
|
||||
try {
|
||||
$this->shutdown();
|
||||
} catch(\Throwable $error) {
|
||||
foreach (self::$errorCallbacks as $errorCallback) {
|
||||
$errorCallback($error, "shutdown", $this->getName());
|
||||
}
|
||||
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register callback. Will be executed when error occurs.
|
||||
* @param callable $callback
|
||||
* @param Throwable $error
|
||||
* @return self
|
||||
*/
|
||||
public static function error(callable $callback): void
|
||||
{
|
||||
\array_push(self::$errorCallbacks, $callback);
|
||||
}
|
||||
/**
|
||||
* Get internal project database
|
||||
* @param string $projectId
|
||||
* @return Database
|
||||
*/
|
||||
protected function getInternalDB(string $projectId): Database
|
||||
protected function getProjectDB(string $projectId): Database
|
||||
{
|
||||
return $this->getDB(self::DATABASE_INTERNAL, $projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get external project database
|
||||
* @param string $projectId
|
||||
* @return Database
|
||||
*/
|
||||
protected function getExternalDB(string $projectId): Database
|
||||
{
|
||||
return $this->getDB(self::DATABASE_EXTERNAL, $projectId);
|
||||
return $this->getDB(self::DATABASE_PROJECT, $projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,20 +177,14 @@ abstract class Worker
|
||||
$sleep = self::SLEEP_TIME; // overwritten when necessary
|
||||
|
||||
switch ($type) {
|
||||
case self::DATABASE_INTERNAL:
|
||||
case self::DATABASE_PROJECT:
|
||||
if (!$projectId) {
|
||||
throw new \Exception('ProjectID not provided - cannot get database');
|
||||
}
|
||||
$namespace = "project_{$projectId}_internal";
|
||||
break;
|
||||
case self::DATABASE_EXTERNAL:
|
||||
if (!$projectId) {
|
||||
throw new \Exception('ProjectID not provided - cannot get database');
|
||||
}
|
||||
$namespace = "project_{$projectId}_external";
|
||||
$namespace = "_project_{$projectId}";
|
||||
break;
|
||||
case self::DATABASE_CONSOLE:
|
||||
$namespace = "project_console_internal";
|
||||
$namespace = "_project_console";
|
||||
$sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created
|
||||
break;
|
||||
default:
|
||||
@@ -110,9 +199,10 @@ abstract class Worker
|
||||
$attempts++;
|
||||
$cache = new Cache(new RedisCache($register->get('cache')));
|
||||
$database = new Database(new MariaDB($register->get('db')), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace($namespace); // Main DB
|
||||
if (!$database->exists()) {
|
||||
throw new \Exception("Table does not exist: {$database->getNamespace()}");
|
||||
if (!empty($projectId) && !$database->getDocument('projects', $projectId)->isEmpty()) {
|
||||
throw new \Exception("Project does not exist: {$projectId}");
|
||||
}
|
||||
break; // leave loop if successful
|
||||
} catch(\Exception $e) {
|
||||
|
||||
@@ -29,7 +29,7 @@ class OpenAPI3 extends Format
|
||||
*/
|
||||
public function parse(): array
|
||||
{
|
||||
/*
|
||||
/**
|
||||
* Specifications (v3.0.0):
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
|
||||
*/
|
||||
@@ -275,6 +275,7 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['x-example'] = false;
|
||||
break;
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
case 'Appwrite\Database\Validator\CustomId':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
break;
|
||||
@@ -393,13 +394,18 @@ class OpenAPI3 extends Format
|
||||
|
||||
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') {
|
||||
continue;
|
||||
|
||||
@@ -198,7 +198,7 @@ class Swagger2 extends Format
|
||||
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
|
||||
'description' => $modelDescription,
|
||||
'schema' => [
|
||||
'oneOf' => \array_map(function($m) {
|
||||
'x-oneOf' => \array_map(function($m) {
|
||||
return ['$ref' => '#/definitions/'.$m->getType()];
|
||||
}, $model)
|
||||
],
|
||||
@@ -263,6 +263,7 @@ class Swagger2 extends Format
|
||||
$node['x-example'] = false;
|
||||
break;
|
||||
case 'Utopia\Database\Validator\UID':
|
||||
case 'Appwrite\Database\Validator\CustomId':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
|
||||
break;
|
||||
@@ -392,7 +393,10 @@ class Swagger2 extends Format
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
foreach ($model->getRules() as $rule) {
|
||||
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
|
||||
if (
|
||||
in_array($model->getType(), $usedModels)
|
||||
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
|
||||
) {
|
||||
$usedModels[] = $rule['type'];
|
||||
}
|
||||
}
|
||||
@@ -460,13 +464,13 @@ class Swagger2 extends Format
|
||||
if(\is_array($rule['type'])) {
|
||||
if($rule['array']) {
|
||||
$items = [
|
||||
'anyOf' => \array_map(function($type) {
|
||||
'x-anyOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/definitions/'.$type];
|
||||
}, $rule['type'])
|
||||
];
|
||||
} else {
|
||||
$items = [
|
||||
'oneOf' => \array_map(function($type) {
|
||||
'x-oneOf' => \array_map(function($type) {
|
||||
return ['$ref' => '#/definitions/'.$type];
|
||||
}, $rule['type'])
|
||||
];
|
||||
@@ -514,7 +518,7 @@ class Swagger2 extends Format
|
||||
}
|
||||
}
|
||||
if (!in_array($name, $required)) {
|
||||
$output['definitions'][$model->getType()]['properties'][$name]['nullable'] = true;
|
||||
$output['definitions'][$model->getType()]['properties'][$name]['x-nullable'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Appwrite\Utopia;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Utopia\Route;
|
||||
use Utopia\Swoole\Request as UtopiaRequest;
|
||||
|
||||
class Request extends UtopiaRequest
|
||||
{
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
private static $filter = null;
|
||||
|
||||
/**
|
||||
* @var Route
|
||||
*/
|
||||
private static $route = null;
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
*/
|
||||
public function __construct(SwooleRequest $request)
|
||||
{
|
||||
parent::__construct($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Params
|
||||
*
|
||||
* Get all params of current method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array
|
||||
{
|
||||
$requestParameters = [];
|
||||
|
||||
switch($this->getMethod()) {
|
||||
case self::METHOD_GET:
|
||||
$requestParameters = (!empty($this->swoole->get)) ? $this->swoole->get : [];
|
||||
break;
|
||||
case self::METHOD_POST:
|
||||
case self::METHOD_PUT:
|
||||
case self::METHOD_PATCH:
|
||||
case self::METHOD_DELETE:
|
||||
$requestParameters = $this->generateInput();
|
||||
break;
|
||||
default:
|
||||
$requestParameters = (!empty($this->swoole->get)) ? $this->swoole->get : [];
|
||||
}
|
||||
|
||||
if (self::hasFilter() && self::hasRoute()) {
|
||||
$endpointIdentifier = self::getRoute()->getLabel('sdk.namespace', 'unknown') . '.' . self::getRoute()->getLabel('sdk.method', 'unknown');
|
||||
$requestParameters = self::getFilter()->parse($requestParameters, $endpointIdentifier);
|
||||
}
|
||||
|
||||
return $requestParameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to set a response filter
|
||||
*
|
||||
* @param $filter the response filter to set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setFilter(?Filter $filter)
|
||||
{
|
||||
self::$filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently set filter
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
public static function getFilter(): ?Filter
|
||||
{
|
||||
return self::$filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a filter has been set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasFilter(): bool
|
||||
{
|
||||
return self::$filter != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to set a request route
|
||||
*
|
||||
* @param Route $route the request route to set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setRoute(?Route $route)
|
||||
{
|
||||
self::$route = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently get route
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public static function getRoute(): ?Route
|
||||
{
|
||||
return self::$route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a route has been set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasRoute(): bool
|
||||
{
|
||||
return self::$route != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request;
|
||||
|
||||
abstract class Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse params to another format.
|
||||
*
|
||||
* @param array $content
|
||||
* @param string $model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function parse(array $content, string $model): array;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request\Filters;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
|
||||
class V12 extends Filter
|
||||
{
|
||||
// Convert 0.11 params format to 0.12 format
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
switch ($model) {
|
||||
// No IDs -> Custom IDs
|
||||
case "account.create":
|
||||
case "account.createMagicURLSession":
|
||||
case "users.create":
|
||||
$content = $this->addId($content, 'userId');
|
||||
break;
|
||||
case "functions.create":
|
||||
$content = $this->addId($content, 'functionId');
|
||||
break;
|
||||
case "teams.create":
|
||||
$content = $this->addId($content, 'teamId');
|
||||
break;
|
||||
|
||||
// Status integer -> boolean
|
||||
case "users.updateStatus":
|
||||
$content = $this->convertStatus($content);
|
||||
break;
|
||||
|
||||
// Deprecating order type
|
||||
case "functions.listExecutions":
|
||||
$content = $this->removeOrderType($content);
|
||||
break;
|
||||
|
||||
// The rest (more complex) formats
|
||||
case "database.createDocument":
|
||||
$content = $this->addId($content, 'documentId');
|
||||
$content = $this->removeParentProperties($content);
|
||||
break;
|
||||
case "database.listDocuments":
|
||||
$content = $this->removeOrderCast($content);
|
||||
$content = $this->convertOrder($content);
|
||||
$content = $this->convertQueries($content);
|
||||
break;
|
||||
case "database.createCollection":
|
||||
$content = $this->addId($content, 'collectionId');
|
||||
$content = $this->removeRules($content);
|
||||
$content = $this->addCollectionPermissionLevel($content);
|
||||
break;
|
||||
case "database.updateCollection":
|
||||
$content = $this->removeRules($content);
|
||||
$content = $this->addCollectionPermissionLevel($content);
|
||||
break;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
// New parameters
|
||||
|
||||
protected function addId(array $content, string $key): array
|
||||
{
|
||||
$content[$key] = 'unique()';
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function addCollectionPermissionLevel(array $content): array
|
||||
{
|
||||
$content['permission'] = 'document';
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Deprecated parameters
|
||||
|
||||
protected function removeRules(array $content): array
|
||||
{
|
||||
unset($content['rules']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function removeOrderType(array $content): array
|
||||
{
|
||||
unset($content['orderType']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function removeOrderCast(array $content): array
|
||||
{
|
||||
unset($content['orderCast']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function removeParentProperties(array $content): array
|
||||
{
|
||||
if (isset($content['parentDocument'])) unset($content['parentDocument']);
|
||||
if (isset($content['parentProperty'])) unset($content['parentProperty']);
|
||||
if (isset($content['parentPropertyType'])) unset($content['parentPropertyType']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Modified parameters
|
||||
|
||||
protected function convertStatus(array $content): array
|
||||
{
|
||||
if (isset($content['status'])) {
|
||||
$content['status'] = $content['status'] === 2 ? false : true;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function convertOrder(array $content): array
|
||||
{
|
||||
if (isset($content['orderField'])) {
|
||||
$content['orderAttributes'] = [ $content['orderField'] ];
|
||||
unset($content['orderField']);
|
||||
}
|
||||
|
||||
if (isset($content['orderType'])) {
|
||||
$content['orderTypes'] = [ $content['orderType'] ];
|
||||
unset($content['orderType']);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function convertQueries(array $content): array
|
||||
{
|
||||
$queries = [];
|
||||
|
||||
if(!empty($content['filters'])) {
|
||||
foreach ($content['filters'] as $filter) {
|
||||
$operators = ['=' => 'equal', '!=' => 'notEqual', '>' => 'greater', '<' => 'lesser', '<=' => 'lesserEqual', '>=' => 'greaterEqual'];
|
||||
foreach ($operators as $operator => $operatorVerbose) {
|
||||
if (\str_contains($filter, $operator)) {
|
||||
$usedOperator = $operator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($usedOperator)) {
|
||||
[ $attributeKey, $filterValue ] = \explode($usedOperator, $filter);
|
||||
|
||||
$filterValue = \is_numeric($filterValue) ? $filterValue : '"' . $filterValue . '"';
|
||||
$query = $attributeKey . '.' . $operators[$usedOperator] . '(' . $filterValue . ')';
|
||||
\array_push($queries, $query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot migrate search properly
|
||||
unset($content['search']);
|
||||
|
||||
unset($content['filters']);
|
||||
unset($content['search']);
|
||||
$content['queries'] = $queries;
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,11 @@ use Appwrite\Utopia\Response\Model\Tag;
|
||||
use Appwrite\Utopia\Response\Model\Token;
|
||||
use Appwrite\Utopia\Response\Model\Webhook;
|
||||
use Appwrite\Utopia\Response\Model\Preferences;
|
||||
use Appwrite\Utopia\Response\Model\HealthAntivirus;
|
||||
use Appwrite\Utopia\Response\Model\HealthQueue;
|
||||
use Appwrite\Utopia\Response\Model\HealthStatus;
|
||||
use Appwrite\Utopia\Response\Model\HealthTime;
|
||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||
@@ -162,6 +167,13 @@ class Response extends SwooleResponse
|
||||
const MODEL_PLATFORM_LIST = 'platformList';
|
||||
const MODEL_DOMAIN = 'domain';
|
||||
const MODEL_DOMAIN_LIST = 'domainList';
|
||||
|
||||
// Health
|
||||
const MODEL_HEALTH_STATUS = 'healthStatus';
|
||||
const MODEL_HEALTH_VERSION = 'healthVersion';
|
||||
const MODEL_HEALTH_QUEUE = 'healthQueue';
|
||||
const MODEL_HEALTH_TIME = 'healthTime';
|
||||
const MODEL_HEALTH_ANTIVIRUS = 'healthAntivirus';
|
||||
|
||||
// Deprecated
|
||||
const MODEL_PERMISSIONS = 'permissions';
|
||||
@@ -259,6 +271,11 @@ class Response extends SwooleResponse
|
||||
->setModel(new Language())
|
||||
->setModel(new Currency())
|
||||
->setModel(new Phone())
|
||||
->setModel(new HealthAntivirus())
|
||||
->setModel(new HealthQueue())
|
||||
->setModel(new HealthStatus())
|
||||
->setModel(new HealthTime())
|
||||
->setModel(new HealthVersion())
|
||||
->setModel(new Metric())
|
||||
->setModel(new UsageDatabase())
|
||||
->setModel(new UsageCollection())
|
||||
@@ -336,7 +353,7 @@ class Response extends SwooleResponse
|
||||
$output = $this->output($document, $model);
|
||||
|
||||
// If filter is set, parse the output
|
||||
if (self::isFilter()) {
|
||||
if (self::hasFilter()) {
|
||||
$output = self::getFilter()->parse($output, $model);
|
||||
}
|
||||
|
||||
@@ -475,7 +492,7 @@ class Response extends SwooleResponse
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFilter(): bool
|
||||
public static function hasFilter(): bool
|
||||
{
|
||||
return self::$filter != null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Filters;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filter;
|
||||
use Exception;
|
||||
|
||||
class V11 extends Filter
|
||||
{
|
||||
// Convert 0.12 Data format to 0.11 format
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
$parsedResponse = $content;
|
||||
|
||||
switch ($model) {
|
||||
// Update permissions
|
||||
case Response::MODEL_DOCUMENT:
|
||||
$parsedResponse = $this->parsePermissions($content);
|
||||
break;
|
||||
case Response::MODEL_DOCUMENT_LIST:
|
||||
$parsedResponse = $this->parseDocumentList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_FILE:
|
||||
$parsedResponse = $this->parsePermissions($content);
|
||||
break;
|
||||
case Response::MODEL_FILE_LIST:
|
||||
$parsedResponse = $this->parseFileList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_EXECUTION:
|
||||
$parsedResponse = $this->parseExecutionPermissions($content);
|
||||
break;
|
||||
case Response::MODEL_EXECUTION_LIST:
|
||||
$parsedResponse = $this->parseExecutionsList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_FUNCTION:
|
||||
$parsedResponse = $this->parseFunctionPermissions($content);
|
||||
break;
|
||||
case Response::MODEL_FUNCTION_LIST:
|
||||
$parsedResponse = $this->parseFunctionsList($content);
|
||||
break;
|
||||
|
||||
// Convert status from boolean to int
|
||||
case Response::MODEL_USER:
|
||||
$parsedResponse = $this->parseStatus($content);
|
||||
break;
|
||||
case Response::MODEL_USER_LIST:
|
||||
$parsedResponse = $this->parseUserList($content);
|
||||
break;
|
||||
|
||||
// Convert all Health responses back to original
|
||||
case Response::MODEL_HEALTH_STATUS:
|
||||
$parsedResponse = $this->parseHealthStatus($content);
|
||||
break;
|
||||
case Response::MODEL_HEALTH_VERSION:
|
||||
$parsedResponse = $this->parseHealthVersion($content);
|
||||
break;
|
||||
case Response::MODEL_HEALTH_TIME:
|
||||
$parsedResponse = $this->parseHealthTime($content);
|
||||
break;
|
||||
case Response::MODEL_HEALTH_QUEUE:
|
||||
$parsedResponse = $this->parseHealthQueue($content);
|
||||
break;
|
||||
case Response::MODEL_HEALTH_ANTIVIRUS:
|
||||
$parsedResponse = $this->parseHealthAntivirus($content);
|
||||
break;
|
||||
|
||||
// Complex filters
|
||||
case Response::MODEL_COLLECTION:
|
||||
$parsedResponse = $this->parseCollection($content);
|
||||
break;
|
||||
case Response::MODEL_COLLECTION_LIST:
|
||||
$parsedResponse = $this->parseCollectionList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_LOG:
|
||||
$parsedResponse = $this->parseLog($content);
|
||||
break;
|
||||
case Response::MODEL_LOG_LIST:
|
||||
$parsedResponse = $this->parseLogList($content);
|
||||
break;
|
||||
|
||||
case Response::MODEL_PROJECT:
|
||||
$parsedResponse = $this->parseProject($content);
|
||||
break;
|
||||
case Response::MODEL_PROJECT_LIST:
|
||||
$parsedResponse = $this->parseProjectList($content);
|
||||
break;
|
||||
}
|
||||
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseDocumentList(array $content)
|
||||
{
|
||||
$documents = $content['documents'];
|
||||
$parsedResponse = [];
|
||||
foreach ($documents as $document) {
|
||||
$parsedResponse[] = $this->parsePermissions($document);
|
||||
}
|
||||
$content['documents'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseFileList(array $content)
|
||||
{
|
||||
$files = $content['files'];
|
||||
$parsedResponse = [];
|
||||
foreach ($files as $file) {
|
||||
$parsedResponse[] = $this->parsePermissions($file);
|
||||
}
|
||||
$content['files'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecutionsList(array $content)
|
||||
{
|
||||
$executions = $content['executions'];
|
||||
$parsedResponse = [];
|
||||
foreach ($executions as $execution) {
|
||||
$parsedResponse[] = $this->parseExecutionPermissions($execution);
|
||||
}
|
||||
$content['executions'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseFunctionsList(array $content)
|
||||
{
|
||||
$functions = $content['functions'];
|
||||
$parsedResponse = [];
|
||||
foreach ($functions as $function) {
|
||||
$parsedResponse[] = $this->parseFunctionPermissions($function);
|
||||
}
|
||||
$content['functions'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseUserList(array $content)
|
||||
{
|
||||
$users = $content['users'];
|
||||
$parsedResponse = [];
|
||||
foreach ($users as $user) {
|
||||
$parsedResponse[] = $this->parseStatus($user);
|
||||
}
|
||||
$content['users'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseCollection(array $content)
|
||||
{
|
||||
$parsedResponse = [];
|
||||
$parsedResponse = $this->parsePermissions($content);
|
||||
$parsedResponse = $this->removeRule($content, 'enabled');
|
||||
$parsedResponse = $this->removeRule($content, 'permission');
|
||||
$parsedResponse = $this->removeRule($content, 'indexes');
|
||||
$parsedResponse = $this->removeRule($content, 'enabled');
|
||||
$parsedResponse = $this->addDate($content, 'dateCreated');
|
||||
$parsedResponse = $this->addDate($content, 'dateUpdated');
|
||||
$parsedResponse = $this->parseAttributes($content);
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseCollectionList(array $content)
|
||||
{
|
||||
$collections = $content['collections'];
|
||||
$parsedResponse = [];
|
||||
foreach ($collections as $collection) {
|
||||
$parsedResponse[] = $this->parseCollection($collection);
|
||||
}
|
||||
$content['collections'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseLog(array $content)
|
||||
{
|
||||
$parsedResponse = [];
|
||||
$parsedResponse = $this->removeRule($content, 'userId');
|
||||
$parsedResponse = $this->removeRule($content, 'userEmail');
|
||||
$parsedResponse = $this->removeRule($content, 'userName');
|
||||
$parsedResponse = $this->removeRule($content, 'mode');
|
||||
$parsedResponse = $this->removeRule($content, 'sum');
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseLogList(array $content)
|
||||
{
|
||||
$logs = $content['logs'];
|
||||
$parsedResponse = [];
|
||||
foreach ($logs as $log) {
|
||||
$parsedResponse[] = $this->parseLog($log);
|
||||
}
|
||||
$content['logs'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseProject(array $content)
|
||||
{
|
||||
$parsedResponse = [];
|
||||
$parsedResponse = $this->addTasks($content);
|
||||
$parsedResponse = $this->parseAuthLimit($content);
|
||||
$parsedResponse = $this->parseOAuths($content);
|
||||
$parsedResponse = $this->parseAuthsStatus($content);
|
||||
$parsedResponse = $this->removeServicesStatus($content);
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
protected function parseProjectList(array $content)
|
||||
{
|
||||
$projects = $content['projects'];
|
||||
$parsedResponse = [];
|
||||
foreach ($projects as $project) {
|
||||
$parsedResponse[] = $this->parseProject($project);
|
||||
}
|
||||
$content['projects'] = $parsedResponse;
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseHealthAntivirus(array $content)
|
||||
{
|
||||
if($content['status'] === 'pass') {
|
||||
$content['status'] = 'online';
|
||||
}
|
||||
|
||||
if($content['status'] === 'fail') {
|
||||
$content['status'] = 'offline';
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseHealthTime(array $content)
|
||||
{
|
||||
$content['remote'] = $content['remoteTime'];
|
||||
unset($content['remoteTime']);
|
||||
|
||||
$content['local'] = $content['localTime'];
|
||||
unset($content['localTime']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseHealthVersion(array $content)
|
||||
{
|
||||
// Did not change
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
protected function parseHealthQueue(array $content)
|
||||
{
|
||||
// Did not change
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseHealthStatus(array $content)
|
||||
{
|
||||
$content['status'] = 'OK'; // Is always returning pass, was always returning OK
|
||||
unset($content['ping']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseStatus(array $content)
|
||||
{
|
||||
$content['status'] = $content['status'] === true ?
|
||||
$content['emailVerification'] === true ? 1 : 0
|
||||
: 2;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseAttributes(array $content)
|
||||
{
|
||||
$content['rules'] = \array_map(function($attribute) use($content) {
|
||||
return [
|
||||
'$id' => $attribute['key'],
|
||||
'$collection' => $content['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'key' => $attribute['key'],
|
||||
'label' => $attribute['key'],
|
||||
'default' => $attribute['default'],
|
||||
'array' => $attribute['array'],
|
||||
'required' => $attribute['required'],
|
||||
'list' => $attribute['elements'],
|
||||
];
|
||||
}, $content['attributes']);
|
||||
unset($content['attributes']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseAuthLimit(array $content)
|
||||
{
|
||||
$content['usersAuthLimit'] = $content['authLimit'];
|
||||
unset($content['authLimit']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseOAuths(array $content)
|
||||
{
|
||||
$regexPattern = "/provider([a-zA-Z0-9]+)(Appid|Secret)/";
|
||||
|
||||
foreach ($content as $key => $value) {
|
||||
\preg_match_all($regexPattern, $key, $regexGroups);
|
||||
if(\count($regexGroups[1]) > 0 && \count($regexGroups[2]) > 0) {
|
||||
$providerName = $regexGroups[1][0];
|
||||
$valueKey = $regexGroups[2][0];
|
||||
$content['usersOauth2' . $providerName . $valueKey] = $value;
|
||||
unset($content['provider' . $providerName . $valueKey]);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseAuthsStatus(array $content)
|
||||
{
|
||||
$regexPattern = "/auth([a-zA-Z0-9]+)/";
|
||||
|
||||
foreach ($content as $key => $value) {
|
||||
\preg_match_all($regexPattern, $key, $regexGroups);
|
||||
if(\count($regexGroups[1]) > 0) {
|
||||
$providerName = $regexGroups[1][0];
|
||||
|
||||
$content[$providerName] = $value;
|
||||
unset($content['auth' . $providerName]);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function removeServicesStatus(array $content)
|
||||
{
|
||||
// Such a key is part of new response, but is not part of old one. We simply remove it, older version never
|
||||
// expected it anyway.
|
||||
foreach ($content as $key => $value) {
|
||||
if(\str_starts_with($key, 'serviceStatusFor')) {
|
||||
unset($content[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function removeRule(array $content, $key)
|
||||
{
|
||||
// Such a key is part of new response, but is not part of old one. We simply remove it, older version never
|
||||
// expected it anyway.
|
||||
unset($content[$key]);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function addDate(array $content, $key)
|
||||
{
|
||||
// We simply don't have the date available in the content anymore.
|
||||
// We set it to valid integer that indicates the value is not right
|
||||
$content[$key] = 0;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function addTasks(array $content)
|
||||
{
|
||||
// We simply don't have the date available in the content anymore.
|
||||
// We set it to valid array
|
||||
$content['tasks'] = [];
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parsePermissions(array $content)
|
||||
{
|
||||
$content['$permissions'] = [ 'read' => $content['$read'], 'write' => $content['$write'] ];
|
||||
unset($content['$read']);
|
||||
unset($content['$write']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseFunctionPermissions(array $content)
|
||||
{
|
||||
$content['$permissions'] = [ 'execute' => $content['execute'] ];
|
||||
unset($content['execute']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseExecutionPermissions(array $content)
|
||||
{
|
||||
$content['$permissions'] = [ 'read' => $content['$read'] ];
|
||||
unset($content['$read']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class Any extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -58,7 +58,7 @@ class BaseList extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -85,7 +85,7 @@ class Collection extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,7 @@ class Continent extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,7 @@ class Country extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -66,7 +66,7 @@ class Currency extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@ class Document extends Any
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -65,7 +65,7 @@ class Domain extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,7 @@ class Error extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ class ErrorDev extends Error
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -85,7 +85,7 @@ class Execution extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -80,7 +80,7 @@ class File extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -110,7 +110,7 @@ class Func extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class HealthAntivirus extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('version', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Antivirus version.',
|
||||
'default' => '',
|
||||
'example' => '1.0.0',
|
||||
])
|
||||
->addRule('status', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Antivirus status. Possible values can are: `disabled`, `offline`, `online`',
|
||||
'default' => '',
|
||||
'example' => 'online',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Health Antivirus';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_HEALTH_ANTIVIRUS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class HealthQueue extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('size', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Amount of actions in the queue.',
|
||||
'default' => 0,
|
||||
'example' => 8,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Health Queue';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_HEALTH_QUEUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class HealthStatus extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('ping', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Duration in milliseconds how long the health check took.',
|
||||
'default' => 0,
|
||||
'example' => 128,
|
||||
])
|
||||
->addRule('status', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Service status. Possible values can are: `pass`, `fail`',
|
||||
'default' => '',
|
||||
'example' => 'pass',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Health Status';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_HEALTH_STATUS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class HealthTime extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('remoteTime', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Current unix timestamp on trustful remote server.',
|
||||
'default' => 0,
|
||||
'example' => 1639490751,
|
||||
])
|
||||
->addRule('localTime', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Current unix timestamp of local server where Appwrite runs.',
|
||||
'default' => 0,
|
||||
'example' => 1639490844,
|
||||
])
|
||||
->addRule('diff', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Difference of unix remote and local timestamps in milliseconds.',
|
||||
'default' => 0,
|
||||
'example' => 93,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Health Time';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_HEALTH_TIME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class HealthVersion extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('version', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Version of the Appwrite instance.',
|
||||
'default' => '',
|
||||
'example' => '0.11.0',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'Health Version';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType():string
|
||||
{
|
||||
return Response::MODEL_HEALTH_VERSION;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class Index extends Model
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Index type.',
|
||||
'default' => '',
|
||||
'example' => '',
|
||||
'example' => 'primary',
|
||||
])
|
||||
->addRule('status', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -29,7 +29,7 @@ class JWT extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,7 @@ class Key extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,7 @@ class Language extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -66,7 +66,7 @@ class Locale extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -150,7 +150,7 @@ class Log extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -79,7 +79,7 @@ class Membership extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ class Mock extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,7 @@ class None extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,7 @@ class Phone extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -76,7 +76,7 @@ class Platform extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ class Preferences extends Any
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -196,7 +196,7 @@ class Project extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -156,7 +156,7 @@ class Session extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,7 @@ class Tag extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,7 @@ class Team extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,7 @@ class Token extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
@@ -61,6 +62,24 @@ class User extends Model
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function filter(Document $document): Document
|
||||
{
|
||||
$prefs = $document->getAttribute('prefs');
|
||||
if($prefs instanceof Document) {
|
||||
$prefs = $prefs->getArrayCopy();
|
||||
}
|
||||
|
||||
if(is_array($prefs) && empty($prefs)) {
|
||||
$document->setAttribute('prefs', new \stdClass);
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
@@ -72,7 +91,7 @@ class User extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -72,7 +72,7 @@ class Webhook extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user