Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller

This commit is contained in:
Prateek Banga
2023-09-20 17:39:36 +05:30
129 changed files with 1300 additions and 550 deletions
+7 -4
View File
@@ -17,14 +17,17 @@ class Exception extends AppwriteException
$decoded = json_decode($response, true);
if (\is_array($decoded)) {
if (\is_array($decoded['error'] ?? '')) {
$this->error = $decoded['error']['status'];
$this->errorDescription = $decoded['error']['message'];
$this->message = $this->error . ': ' . $this->errorDescription;
$this->error = $decoded['error']['status'] ?? 'Unknown error';
$this->errorDescription = $decoded['error']['message'] ?? 'No description';
} elseif (\is_array($decoded['errors'] ?? '')) {
$this->error = $decoded['error'] ?? $decoded['message'] ?? 'Unknown error';
$this->errorDescription = $decoded['errors'][0]['message'] ?? 'No description';
} else {
$this->error = $decoded['error'] ?? $decoded['message'] ?? 'Unknown error';
$this->errorDescription = $decoded['error_description'] ?? 'No description';
$this->message = $this->error . ': ' . $this->errorDescription;
}
$this->message = $this->error . ': ' . $this->errorDescription;
}
$type = match ($code) {
400 => AppwriteException::USER_OAUTH2_BAD_REQUEST,
+1
View File
@@ -66,6 +66,7 @@ abstract class Migration
'1.3.8' => 'V18',
'1.4.0' => 'V19',
'1.4.1' => 'V19',
'1.4.2' => 'V19',
];
/**
+202 -67
View File
@@ -10,7 +10,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception;
use Utopia\Database\Query;
class V19 extends Migration
{
@@ -51,27 +50,37 @@ class V19 extends Migration
protected function migrateDomains(): void
{
foreach ($this->documentsIterator('domains') as $domain) {
$status = 'created';
if ($domain->getAttribute('verification', false)) {
$status = 'verified';
}
if ($this->consoleDB->exists($this->consoleDB->getDefaultDatabase(), 'domains')) {
foreach ($this->documentsIterator('domains') as $domain) {
$status = 'created';
if ($domain->getAttribute('verification', false)) {
$status = 'verified';
}
$ruleDocument = new Document([
'projectId' => $domain->getAttribute('projectId'),
'projectInternalId' => $domain->getAttribute('projectInternalId'),
'domain' => $domain->getAttribute('domain'),
'resourceType' => 'api',
'resourceInternalId' => '',
'resourceId' => '',
'status' => $status,
'certificateId' => $domain->getAttribute('certificateId'),
]);
$projectId = $domain->getAttribute('projectId');
$projectInternalId = $domain->getAttribute('projectInternalId');
try {
$this->consoleDB->createDocument('rules', $ruleDocument);
} catch (\Throwable $th) {
Console::warning("Error migrating domain {$domain->getAttribute('domain')}: {$th->getMessage()}");
if (empty($projectId) || empty($projectInternalId)) {
Console::warning("Error migrating domain {$domain->getAttribute('domain')}: Missing projectId or projectInternalId");
continue;
}
$ruleDocument = new Document([
'projectId' => $domain->getAttribute('projectId'),
'projectInternalId' => $domain->getAttribute('projectInternalId'),
'domain' => $domain->getAttribute('domain'),
'resourceType' => 'api',
'resourceInternalId' => '',
'resourceId' => '',
'status' => $status,
'certificateId' => $domain->getAttribute('certificateId'),
]);
try {
$this->consoleDB->createDocument('rules', $ruleDocument);
} catch (\Throwable $th) {
Console::warning("Error migrating domain {$domain->getAttribute('domain')}: {$th->getMessage()}");
}
}
}
}
@@ -183,16 +192,23 @@ class V19 extends Migration
break;
case 'builds':
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'deploymentInternalId');
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
$attributesToCreate = [
'size',
'deploymentInternalId',
'logs',
];
foreach ($attributesToCreate as $attribute) {
try {
$this->createAttributeFromCollection($this->projectDB, $id, $attribute);
} catch (\Throwable $th) {
Console::warning("$attribute from {$id}: {$th->getMessage()}");
}
}
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'logs');
$this->projectDB->renameAttribute($id, 'outputPath', 'path');
} catch (\Throwable $th) {
Console::warning("'logs' from {$id}: {$th->getMessage()}");
Console::warning("'path' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
@@ -202,13 +218,13 @@ class V19 extends Migration
try {
$this->projectDB->renameAttribute($id, 'log', 'logs');
} catch (\Throwable $th) {
Console::warning("'errors' from {$id}: {$th->getMessage()}");
Console::warning("'logs' from {$id}: {$th->getMessage()}");
}
try {
$this->projectDB->updateAttribute($id, 'logs', size: 1000000);
} catch (\Throwable $th) {
Console::warning("'errors' from {$id}: {$th->getMessage()}");
Console::warning("'logs' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
@@ -228,7 +244,25 @@ class V19 extends Migration
$attributesToCreate = [
'resourceInternalId',
'buildInternalId',
'commands',
'type',
'installationId',
'installationInternalId',
'providerRepositoryId',
'repositoryId',
'repositoryInternalId',
'providerRepositoryName',
'providerRepositoryOwner',
'providerRepositoryUrl',
'providerCommitHash',
'providerCommitAuthorUrl',
'providerCommitAuthor',
'providerCommitMessage',
'providerCommitUrl',
'providerBranch',
'providerBranchUrl',
'providerRootDirectory',
'providerCommentId',
];
foreach ($attributesToCreate as $attribute) {
try {
@@ -270,16 +304,31 @@ class V19 extends Migration
break;
case 'executions':
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'functionInternalId');
} catch (\Throwable $th) {
Console::warning("'functionInternalId' from {$id}: {$th->getMessage()}");
$attributesToCreate = [
'functionInternalId',
'deploymentInternalId',
'requestMethod',
'requestPath',
'requestHeaders',
'responseHeaders',
];
foreach ($attributesToCreate as $attribute) {
try {
$this->createAttributeFromCollection($this->projectDB, $id, $attribute);
} catch (\Throwable $th) {
Console::warning("$attribute from {$id}: {$th->getMessage()}");
}
}
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'deploymentInternalId');
} catch (\Throwable $th) {
Console::warning("'deploymentInternalId' from {$id}: {$th->getMessage()}");
$attributesToDelete = [
'response'
];
foreach ($attributesToDelete as $attribute) {
try {
$this->projectDB->deleteAttribute($id, $attribute);
} catch (\Throwable $th) {
Console::warning("'$attribute' from {$id}: {$th->getMessage()}");
}
}
try {
@@ -300,6 +349,18 @@ class V19 extends Migration
Console::warning("'responseStatusCode' from {$id}: {$th->getMessage()}");
}
try {
$this->projectDB->deleteIndex($id, '_key_statusCode');
} catch (\Throwable $th) {
Console::warning("'_key_statusCode' from {$id}: {$th->getMessage()}");
}
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_responseStatusCode');
} catch (\Throwable $th) {
Console::warning("'_key_responseStatusCode' from {$id}: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection($id);
break;
@@ -343,11 +404,13 @@ class V19 extends Migration
'providerSilentMode',
'logging',
'deploymentInternalId',
'schedule',
'scheduleInternalId',
'scheduleId',
'version',
'entrypoint',
'commands',
'varsProject'
];
foreach ($attributesToCreate as $attribute) {
try {
@@ -558,6 +621,25 @@ class V19 extends Migration
}
}
private function getFunctionCommands(Document $function): string
{
$runtime = $function->getAttribute('runtime');
$language = explode('-', $runtime)[0];
$commands = match ($language) {
'dart' => 'dart pub get',
'deno' => 'deno cache ' . $function->getAttribute('entrypoint'),
'dotnet' => 'dotnet restore',
'node' => 'npm install',
'php' => 'composer install',
'python' => 'pip install -r requirements.txt',
'ruby' => 'bundle install',
'swift' => 'swift package resolve',
default => '',
};
return $commands;
}
/**
* Fix run on each document
*
@@ -569,9 +651,9 @@ class V19 extends Migration
switch ($document->getCollection()) {
case 'attributes':
case 'indexes':
$status = $document->getAttribute('status', '');
$status = $document->getAttribute('status');
if ($status === 'failed') {
$document->setAttribute('error', 'Unknown problem');
$document->setAttribute('error', $document->getAttribute('error', 'Unknown problem'));
}
break;
case 'builds':
@@ -581,10 +663,10 @@ class V19 extends Migration
$stdout = $document->getAttribute('stdout', '');
$stderr = $document->getAttribute('stderr', '');
$document->setAttribute('logs', $stdout . PHP_EOL . $stderr);
$document->setAttribute('logs', $document->getAttribute('logs', $stdout . PHP_EOL . $stderr));
break;
case 'databases':
$document->setAttribute('enabled', true);
$document->setAttribute('enabled', $document->getAttribute('enabled', true));
break;
case 'deployments':
$resourceId = $document->getAttribute('resourceId');
@@ -597,7 +679,9 @@ class V19 extends Migration
$document->setAttribute('buildInternalId', $build->getInternalId());
}
$document->setAttribute('type', 'manual');
$commands = $this->getFunctionCommands($function);
$document->setAttribute('commands', $document->getAttribute('commands', $commands));
$document->setAttribute('type', $document->getAttribute('type', 'manual'));
break;
case 'executions':
$functionId = $document->getAttribute('functionId');
@@ -609,9 +693,9 @@ class V19 extends Migration
$document->setAttribute('deploymentInternalId', $deployment->getInternalId());
break;
case 'functions':
$document->setAttribute('live', true);
$document->setAttribute('logging', true);
$document->setAttribute('version', 'v2');
$document->setAttribute('live', $document->getAttribute('live', true));
$document->setAttribute('logging', $document->getAttribute('logging', true));
$document->setAttribute('version', $document->getAttribute('version', 'v2'));
$deploymentId = $document->getAttribute('deployment');
if (!empty($deploymentId)) {
@@ -620,19 +704,25 @@ class V19 extends Migration
$document->setAttribute('entrypoint', $deployment->getAttribute('entrypoint'));
}
$schedule = $this->consoleDB->createDocument('schedules', new Document([
'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'resourceType' => 'function',
'resourceId' => $document->getId(),
'resourceInternalId' => $document->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $this->project->getId(),
'schedule' => $document->getAttribute('schedule'),
'active' => !empty($document->getAttribute('schedule')) && !empty($document->getAttribute('deployment')),
]));
$commands = $this->getFunctionCommands($document);
$document->setAttribute('commands', $document->getAttribute('commands', $commands));
if (empty($document->getAttribute('scheduleId', null))) {
$schedule = $this->consoleDB->createDocument('schedules', new Document([
'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'resourceType' => 'function',
'resourceId' => $document->getId(),
'resourceInternalId' => $document->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $this->project->getId(),
'schedule' => $document->getAttribute('schedule'),
'active' => !empty($document->getAttribute('schedule')) && !empty($document->getAttribute('deployment')),
]));
$document->setAttribute('scheduleId', $schedule->getId());
$document->setAttribute('scheduleInternalId', $schedule->getInternalId());
}
$document->setAttribute('scheduleId', $schedule->getId());
$document->setAttribute('scheduleInternalId', $schedule->getInternalId());
break;
case 'projects':
$document->setAttribute('version', '1.4.0');
@@ -640,10 +730,13 @@ class V19 extends Migration
$databases = Config::getParam('pools-database', []);
$database = $databases[0];
$document->setAttribute('database', $database);
$document->setAttribute('smtp', []);
$document->setAttribute('templates', []);
$document->setAttribute('database', $document->getAttribute('database', $database));
$document->setAttribute('smtp', $document->getAttribute('smtp', []));
$document->setAttribute('templates', $document->getAttribute('templates', []));
break;
case 'variables':
$document->setAttribute('resourceType', $document->getAttribute('resourceType', 'function'));
break;
default:
break;
@@ -662,14 +755,6 @@ class V19 extends Migration
$this->projectDB->deleteCachedCollection('projects');
try {
$this->projectDB->deleteAttribute('functions', 'schedule');
} catch (\Throwable $th) {
Console::warning("'schedule' from functions: {$th->getMessage()}");
}
$this->projectDB->deleteCachedCollection('functions');
try {
$this->projectDB->deleteAttribute('builds', 'stderr');
} catch (\Throwable $th) {
@@ -684,4 +769,54 @@ class V19 extends Migration
$this->projectDB->deleteCachedCollection('builds');
}
/**
* Overwrite parent to skip cache collection as well
*
* @param callable $callback
* @return void
*/
public function forEachDocument(callable $callback): void
{
$internalProjectId = $this->project->getInternalId();
$collections = match ($internalProjectId) {
'console' => $this->collections['console'],
default => $this->collections['projects'],
};
foreach ($collections as $collection) {
// Also skip cache collection because the we don't need to migrate
// it and the $ids cause issues with the cursor pagination
if ($collection['$collection'] !== Database::METADATA || $collection['$id'] === 'cache') {
continue;
}
Console::log('Migrating Collection ' . $collection['$id'] . ':');
\Co\run(function (array $collection, callable $callback) {
foreach ($this->documentsIterator($collection['$id']) as $document) {
go(function (Document $document, callable $callback) {
if (empty($document->getId()) || empty($document->getCollection())) {
return;
}
$old = $document->getArrayCopy();
$new = call_user_func($callback, $document);
if (is_null($new) || $new->getArrayCopy() == $old) {
return;
}
try {
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
} catch (\Throwable $th) {
Console::error('Failed to update document: ' . $th->getMessage());
return;
}
}, $document, $callback);
}
}, $collection, $callback);
}
}
}
+11 -10
View File
@@ -40,6 +40,7 @@ class Install extends Action
$config = Config::getParam('variables');
$defaultHTTPPort = '80';
$defaultHTTPSPort = '443';
/** @var array<string, array<string, string>> $vars array whre key is variable name and value is variable */
$vars = [];
/**
@@ -50,7 +51,7 @@ class Install extends Action
foreach ($config as $category) {
foreach ($category['variables'] ?? [] as $var) {
$vars[] = $var;
$vars[$var['name']] = $var;
}
}
@@ -70,7 +71,7 @@ class Install extends Action
if ($interactive == 'Y' && Console::isInteractive()) {
$answer = Console::confirm('Previous installation found, do you want to overwrite it (a backup will be created before overwriting)? (Y/n)');
if ($answer !== 'Y') {
if (\strtolower($answer) !== 'y') {
Console::info('No action taken.');
return;
}
@@ -104,10 +105,10 @@ class Install extends Action
if (is_null($value)) {
continue;
}
foreach ($vars as $i => $var) {
if ($var['name'] === $key) {
$vars[$i]['default'] = $value;
}
$configVar = $vars[$key] ?? [];
if (!empty($configVar) && !($configVar['overwrite'] ?? false)) {
$vars[$key]['default'] = $value;
}
}
}
@@ -123,10 +124,10 @@ class Install extends Action
if (is_null($value)) {
continue;
}
foreach ($vars as $i => $var) {
if ($var['name'] === $key) {
$vars[$i]['default'] = $value;
}
$configVar = $vars[$key] ?? [];
if (!empty($configVar) && !($configVar['overwrite'] ?? false)) {
$vars[$key]['default'] = $value;
}
}
}
+2
View File
@@ -216,6 +216,8 @@ class Schedule extends Action
$functions
->setType('schedule')
->setFunction($schedule['function'])
->setMethod('POST')
->setPath('/')
->setProject($schedule['project'])
->trigger();
}
+1 -1
View File
@@ -17,7 +17,7 @@ class V16 extends Filter
$content['commands'] = $this->getCommands($content['runtime'] ?? '');
break;
case 'functions.createExecution':
$content['body'] = $content['data'];
$content['body'] = $content['data'] ?? '';
unset($content['data']);
break;
}
+7 -12
View File
@@ -8,11 +8,6 @@ use Utopia\Database\DateTime;
class Message extends Any
{
/**
* @var bool
*/
protected bool $public = false;
public function __construct()
{
$this
@@ -30,14 +25,14 @@ class Message extends Any
])
->addRule('to', [
'type' => self::TYPE_STRING,
'description' => 'Recipient of message.',
'description' => 'Message recipients.',
'default' => '',
'array' => true,
'example' => ['user-1'],
])
->addRule('deliveryTime', [
'type' => self::TYPE_DATETIME,
'description' => 'Recipient of message.',
'description' => 'Time the message is delivered at.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
@@ -48,7 +43,7 @@ class Message extends Any
'required' => false,
'default' => '',
'array' => true,
'example' => 'Credentials not valid.',
'example' => ['Failed to send message to target 5e5ea5c16897e: Credentials not valid.'],
])
->addRule('deliveredTo', [
'type' => self::TYPE_INTEGER,
@@ -56,11 +51,11 @@ class Message extends Any
'default' => 0,
'example' => 1,
])
->addRule('delivered', [
'type' => self::TYPE_BOOLEAN,
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Status of delivery.',
'default' => false,
'example' => true,
'default' => 'processing',
'example' => 'Message status can be one of the following: processing, sent, failed.',
]);
}
@@ -7,8 +7,6 @@ use Appwrite\Utopia\Response\Model;
class Provider extends Model
{
protected bool $public = false;
public function __construct()
{
$this