Merge branch '1.9.x' into feat-add-telemetry-for-ss-success-rates

This commit is contained in:
Harsh Mahajan
2026-04-21 13:07:40 +05:30
committed by GitHub
50 changed files with 1346 additions and 620 deletions
+3 -1
View File
@@ -71,7 +71,9 @@ class Mails extends Listener
throw new \Exception('Invalid template path');
}
$customTemplate = $project->getAttribute('templates', [])["email.sessionAlert-$event->locale"] ?? [];
$customTemplate =
$project->getAttribute('templates', [])["email.sessionAlert-" . $locale->default] ??
$project->getAttribute('templates', [])['email.sessionAlert-' . $locale->fallback] ?? [];
$isBranded = $smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE;
$subject = $customTemplate['subject'] ?? $locale->getText('emails.sessionAlert.subject');
+97 -6
View File
@@ -114,14 +114,24 @@ class Realtime extends MessagingAdapter
}
}
// Keep userId from onOpen/authentication when provided.
// Fallback to existing stored value for subsequent subscribe upserts.
$this->connections[$identifier] = [
// Union channels/roles across all subscriptions on the connection; overwriting would
// leave getSubscriptionMetadata and full unsubscribe operating on stale state.
$existing = $this->connections[$identifier] ?? [];
$existingChannels = $existing['channels'] ?? [];
$existingRoles = $existing['roles'] ?? [];
$entry = [
'projectId' => $projectId,
'roles' => $roles,
'userId' => $userId ?? ($this->connections[$identifier]['userId'] ?? ''),
'channels' => $channels
'roles' => \array_values(\array_unique(\array_merge($existingRoles, $roles))),
'userId' => $userId ?? ($existing['userId'] ?? ''),
'channels' => \array_values(\array_unique(\array_merge($existingChannels, $channels))),
];
if (\array_key_exists('authorization', $existing)) {
$entry['authorization'] = $existing['authorization'];
}
$this->connections[$identifier] = $entry;
}
/**
@@ -206,6 +216,87 @@ class Realtime extends MessagingAdapter
}
}
/**
* Removes a single subscription from a connection, keeping the connection alive so
* the client can resubscribe. Idempotent — returns true only when something was removed.
*
* @param mixed $connection
* @param string $subscriptionId
* @return bool
*/
public function unsubscribeSubscription(mixed $connection, string $subscriptionId): bool
{
$projectId = $this->connections[$connection]['projectId'] ?? '';
if ($projectId === '' || !isset($this->subscriptions[$projectId])) {
return false;
}
$removed = false;
foreach ($this->subscriptions[$projectId] as $role => $byChannel) {
foreach ($byChannel as $channel => $byConnection) {
if (!isset($byConnection[$connection][$subscriptionId])) {
continue;
}
unset($this->subscriptions[$projectId][$role][$channel][$connection][$subscriptionId]);
$removed = true;
if (empty($this->subscriptions[$projectId][$role][$channel][$connection])) {
unset($this->subscriptions[$projectId][$role][$channel][$connection]);
}
if (empty($this->subscriptions[$projectId][$role][$channel])) {
unset($this->subscriptions[$projectId][$role][$channel]);
}
}
if (empty($this->subscriptions[$projectId][$role])) {
unset($this->subscriptions[$projectId][$role]);
}
}
if (empty($this->subscriptions[$projectId])) {
unset($this->subscriptions[$projectId]);
}
if ($removed) {
$this->recomputeConnectionState($connection);
}
return $removed;
}
/**
* Recomputes the cached channels on the connection entry from the subscriptions tree.
* Called after per-subscription removal so stale channel entries do not linger for later reads.
*
* Roles are deliberately NOT recomputed here. They represent the connection's authorization
* context (set at onOpen, replaced on `authentication` / permission-change) and must survive
* per-subscription removal — otherwise a client that unsubscribes every subscription and then
* resubscribes would subscribe with an empty roles array and silently receive nothing.
*
* @param mixed $connection
* @return void
*/
private function recomputeConnectionState(mixed $connection): void
{
if (!isset($this->connections[$connection])) {
return;
}
$projectId = $this->connections[$connection]['projectId'] ?? '';
$channels = [];
foreach ($this->subscriptions[$projectId] ?? [] as $byChannel) {
foreach ($byChannel as $channel => $byConnection) {
if (isset($byConnection[$connection])) {
$channels[$channel] = true;
}
}
}
$this->connections[$connection]['channels'] = \array_keys($channels);
}
/**
* Checks if Channel has a subscriber.
* @param string $projectId
@@ -170,11 +170,6 @@ class Create extends Action
$message = Template::fromFile($templatesPath . '/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.mfaChallenge-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $projectName)
@@ -223,7 +218,9 @@ class Create extends Action
$preview = $locale->getText("emails.mfaChallenge.preview");
$heading = $locale->getText("emails.mfaChallenge.heading");
$customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? [];
$customTemplate =
$project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ??
$project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->fallback] ?? [];
$smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base');
$validator = new FileName();
@@ -49,11 +49,6 @@ class Create extends Action
$databaseOverride = '';
$dbScheme = '';
$databaseSharedTables = [];
$databaseSharedTablesV1 = [];
$databaseSharedTablesV2 = [];
$projectSharedTables = [];
$projectSharedTablesV1 = [];
$projectSharedTablesV2 = [];
switch ($databasetype) {
case DOCUMENTSDB:
@@ -62,7 +57,6 @@ class Create extends Action
$databaseOverride = System::getEnv('_APP_DATABASE_DOCUMENTSDB_OVERRIDE');
$dbScheme = System::getEnv('_APP_DB_HOST_DOCUMENTSDB', 'mongodb');
$databaseSharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES', '')));
$databaseSharedTablesV1 = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES_V1', '')));
break;
case VECTORSDB:
$databases = Config::getParam('pools-vectorsdb', []);
@@ -70,7 +64,6 @@ class Create extends Action
$databaseOverride = System::getEnv('_APP_DATABASE_VECTORSDB_OVERRIDE');
$dbScheme = System::getEnv('_APP_DB_HOST_VECTORSDB', 'postgresql');
$databaseSharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_VECTORSDB_SHARED_TABLES', '')));
$databaseSharedTablesV1 = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_VECTORSDB_SHARED_TABLES_V1', '')));
break;
default:
// legacy/tablesdb
@@ -78,8 +71,7 @@ class Create extends Action
return $dsn;
}
$isSharedTablesV1 = false;
$isSharedTablesV2 = false;
$isSharedTables = false;
if (!empty($dsn)) {
try {
@@ -90,10 +82,7 @@ class Create extends Action
}
$projectSharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$projectSharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectSharedTablesV2 = \array_diff($projectSharedTables, $projectSharedTablesV1);
$isSharedTablesV1 = \in_array($dsnHost, $projectSharedTablesV1);
$isSharedTablesV2 = \in_array($dsnHost, $projectSharedTablesV2);
$isSharedTables = \in_array($dsnHost, $projectSharedTables);
}
if ($region !== 'default') {
@@ -102,18 +91,14 @@ class Create extends Action
return str_contains($value, $region);
});
}
$databaseSharedTablesV2 = \array_diff($databaseSharedTables, $databaseSharedTablesV1);
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
$selectedDsn = $databases[$index];
} else {
if (!empty($dsn) && !empty($databaseSharedTables)) {
$beforeFilter = \array_values($databases);
if ($isSharedTablesV1) {
$databases = array_filter($databases, fn ($value) => \in_array($value, $databaseSharedTablesV1));
} elseif ($isSharedTablesV2) {
$databases = array_filter($databases, fn ($value) => \in_array($value, $databaseSharedTablesV2));
if ($isSharedTables) {
$databases = array_filter($databases, fn ($value) => \in_array($value, $databaseSharedTables));
} else {
$databases = array_filter($databases, fn ($value) => !\in_array($value, $databaseSharedTables));
}
@@ -31,7 +31,7 @@ class Get extends Action
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/functions/:functionId/deployments/:deploymentId/download')
->httpAlias('/v1/functions/:functionId/deployments/:deploymentId/build/download', ['type' => 'output'])
->httpAlias('/v1/functions/:functionId/deployments/:deploymentId/build/download')
->groups(['api', 'functions'])
->desc('Get deployment download')
->label('scope', 'functions.read')
@@ -144,7 +144,8 @@ class Builds extends Action
$log,
$executor,
$plan,
$platform
$platform,
(int) ($payload['timeout'] ?? System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT', 900))
);
break;
@@ -179,7 +180,8 @@ class Builds extends Action
Log $log,
Executor $executor,
array $plan,
array $platform
array $platform,
int $timeout
): void {
Console::info('Deployment action started');
@@ -592,10 +594,7 @@ class Builds extends Action
$cpus = $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT;
$memory = max($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT, $minMemory);
$timeout = (int) System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT', 900);
$jwtExpiry = (int) System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $timeout, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
@@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Modules\Project\Http\Project\Protocols\Status;
namespace Appwrite\Platform\Modules\Project\Http\Project\Protocols;
use Appwrite\Event\Event;
use Appwrite\Platform\Action;
@@ -22,16 +22,17 @@ class Update extends Action
public static function getName()
{
return 'updateProjectProtocolStatus';
return 'updateProjectProtocol';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/project/protocols/:protocolId/status')
->setHttpPath('/v1/project/protocols/:protocolId')
->httpAlias('/v1/project/protocols/:protocolId/status')
->httpAlias('/v1/projects/:projectId/api')
->desc('Update project protocol status')
->desc('Update project protocol')
->groups(['api', 'project'])
->label('scope', 'project.write')
->label('event', 'protocols.[protocolId].update')
@@ -40,9 +41,9 @@ class Update extends Action
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'updateProtocolStatus',
name: 'updateProtocol',
description: <<<EOT
Update the status of a specific protocol. Use this endpoint to enable or disable a protocol in your project.
Update properties of a specific protocol. Use this endpoint to enable or disable a protocol in your project.
EOT,
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
@@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Modules\Project\Http\Project\Services\Status;
namespace Appwrite\Platform\Modules\Project\Http\Project\Services;
use Appwrite\Event\Event;
use Appwrite\Platform\Action;
@@ -22,16 +22,17 @@ class Update extends Action
public static function getName()
{
return 'updateProjectServiceStatus';
return 'updateProjectService';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/project/services/:serviceId/status')
->setHttpPath('/v1/project/services/:serviceId')
->httpAlias('/v1/project/services/:serviceId/status')
->httpAlias('/v1/projects/:projectId/service')
->desc('Update project service status')
->desc('Update project service')
->groups(['api', 'project'])
->label('scope', 'project.write')
->label('event', 'services.[serviceId].update')
@@ -40,9 +41,9 @@ class Update extends Action
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'updateServiceStatus',
name: 'updateService',
description: <<<EOT
Update the status of a specific service. Use this endpoint to enable or disable a service in your project.
Update properties of a specific service. Use this endpoint to enable or disable a service in your project.
EOT,
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
@@ -22,8 +22,8 @@ use Appwrite\Platform\Modules\Project\Http\Project\Platforms\Web\Update as Updat
use Appwrite\Platform\Modules\Project\Http\Project\Platforms\Windows\Create as CreateWindowsPlatform;
use Appwrite\Platform\Modules\Project\Http\Project\Platforms\Windows\Update as UpdateWindowsPlatform;
use Appwrite\Platform\Modules\Project\Http\Project\Platforms\XList as ListPlatforms;
use Appwrite\Platform\Modules\Project\Http\Project\Protocols\Status\Update as UpdateProjectProtocolStatus;
use Appwrite\Platform\Modules\Project\Http\Project\Services\Status\Update as UpdateProjectServiceStatus;
use Appwrite\Platform\Modules\Project\Http\Project\Protocols\Update as UpdateProjectProtocol;
use Appwrite\Platform\Modules\Project\Http\Project\Services\Update as UpdateProjectService;
use Appwrite\Platform\Modules\Project\Http\Project\Variables\Create as CreateVariable;
use Appwrite\Platform\Modules\Project\Http\Project\Variables\Delete as DeleteVariable;
use Appwrite\Platform\Modules\Project\Http\Project\Variables\Get as GetVariable;
@@ -42,8 +42,8 @@ class Http extends Service
// Project
$this->addAction(UpdateProjectLabels::getName(), new UpdateProjectLabels());
$this->addAction(UpdateProjectProtocolStatus::getName(), new UpdateProjectProtocolStatus());
$this->addAction(UpdateProjectServiceStatus::getName(), new UpdateProjectServiceStatus());
$this->addAction(UpdateProjectProtocol::getName(), new UpdateProjectProtocol());
$this->addAction(UpdateProjectService::getName(), new UpdateProjectService());
// Variables
$this->addAction(CreateVariable::getName(), new CreateVariable());
@@ -21,8 +21,6 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\UID;
use Utopia\DSN\DSN;
use Utopia\Platform\Scope\HTTP;
@@ -209,32 +207,16 @@ class Create extends Action
}
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
if (!$sharedTablesV2) {
if ($projectTables) {
$adapter = new DatabasePool($pools->get($dsn->getHost()));
$dbForProject = new Database($adapter, $cache);
$dbForProject->setDatabase(APP_DATABASE);
if ($sharedTables) {
$tenant = null;
if ($sharedTablesV1) {
$tenant = $project->getSequence();
}
$dbForProject
->setSharedTables(true)
->setTenant($tenant)
->setNamespace($dsn->getParam('namespace'));
} else {
$dbForProject
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
}
$dbForProject
->setDatabase(APP_DATABASE)
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
$create = true;
@@ -244,27 +226,11 @@ class Create extends Action
$create = false;
}
if ($create || $projectTables) {
$adapter = new AdapterDatabase($dbForProject);
$audit = new Audit($adapter);
$audit->setup();
}
$adapter = new AdapterDatabase($dbForProject);
$audit = new Audit($adapter);
$audit->setup();
if (!$create && $sharedTablesV1) {
$adapter = new AdapterDatabase($dbForProject);
$attributes = $adapter->getAttributeDocuments();
$indexes = $adapter->getIndexDocuments();
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom('audit'),
'$permissions' => [Permission::create(Role::any())],
'name' => 'audit',
'attributes' => $attributes,
'indexes' => $indexes,
'documentSecurity' => true
]));
}
if ($create || $sharedTablesV1) {
if ($create) {
/** @var array $collections */
$collections = Config::getParam('collections', [])['projects'] ?? [];
@@ -279,37 +245,7 @@ class Create extends Action
try {
$dbForProject->createCollection($key, $attributes, $indexes);
} catch (Duplicate) {
try {
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom($key),
'$permissions' => [Permission::create(Role::any())],
'name' => $key,
'attributes' => $attributes,
'indexes' => $indexes,
'documentSecurity' => true
]));
} catch (Duplicate) {
// Metadata already exists from concurrent creation
}
} catch (\Throwable $e) {
// PostgreSQL adapter may throw a non-Duplicate exception when
// a table or index already exists during concurrent project
// creation in shared mode. Treat as duplicate if metadata
// can be created successfully.
try {
$dbForProject->createDocument(Database::METADATA, new Document([
'$id' => ID::custom($key),
'$permissions' => [Permission::create(Role::any())],
'name' => $key,
'attributes' => $attributes,
'indexes' => $indexes,
'documentSecurity' => true
]));
} catch (Duplicate) {
// Metadata already exists from concurrent creation
} catch (\Throwable) {
throw $e; // Rethrow original if metadata creation also fails
}
// Collection already exists
}
}
}
@@ -54,7 +54,7 @@ class Get extends Action
->label('cache', true)
->label('cache.resourceType', 'bucket/{request.bucketId}')
->label('cache.resource', 'file/{request.fileId}')
->label('cache.params', ['width', 'height', 'gravity', 'quality', 'borderWidth', 'borderColor', 'borderRadius', 'opacity', 'rotation', 'background', 'output'])
->label('cache.params', ['width', 'height', 'gravity', 'quality', 'borderWidth', 'borderColor', 'borderRadius', 'opacity', 'rotation', 'background', 'output', 'project'])
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
@@ -324,7 +324,9 @@ class Create extends Action
$body = $locale->getText('emails.invitation.body');
$preview = $locale->getText('emails.invitation.preview');
$subject = $locale->getText('emails.invitation.subject');
$customTemplate = $project->getAttribute('templates', [])['email.invitation-' . $locale->default] ?? [];
$customTemplate =
$project->getAttribute('templates', [])['email.invitation-' . $locale->default] ??
$project->getAttribute('templates', [])['email.invitation-' . $locale->fallback] ?? [];
$message = Template::fromFile(APP_CE_CONFIG_DIR . '/locale/templates/email-inner-base.tpl');
$message
@@ -407,11 +409,6 @@ class Create extends Action
$message = Template::fromFile(APP_CE_CONFIG_DIR . '/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-' . $locale->default] ?? [];
if (! empty($customTemplate)) {
$message = $customTemplate['message'];
}
$message = $message->setParam('{{token}}', $url);
$message = $message->render();
@@ -313,6 +313,7 @@ class XList extends Action
}, $repos);
$response->dynamic(new Document([
'type' => $type,
$type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos,
'total' => $total,
]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST);
+4
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Platform\Tasks;
use Appwrite\SDK\Language\AgentSkills;
use Appwrite\SDK\Language\Android;
use Appwrite\SDK\Language\Apple;
use Appwrite\SDK\Language\ClaudePlugin;
use Appwrite\SDK\Language\CLI;
use Appwrite\SDK\Language\CursorPlugin;
use Appwrite\SDK\Language\Dart;
@@ -451,6 +452,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
case 'cursor-plugin':
$config = new CursorPlugin();
break;
case 'claude-plugin':
$config = new ClaudePlugin();
break;
default:
throw new \Exception('Language "' . $language['key'] . '" not supported');
}
+1 -20
View File
@@ -651,11 +651,8 @@ class Deletes extends Action
];
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$allDatabases = [
new Document([
@@ -758,23 +755,7 @@ class Deletes extends Action
),
$databasesToClean
));
} elseif ($sharedTablesV1) {
/**
* Temporary disabling deletes for internal collections
*/
$queries = \array_map(
fn ($id) => Query::notEqual('$id', $id),
$projectCollectionIds
);
$queries[] = Query::orderAsc();
$this->deleteByGroup(
Database::METADATA,
$queries,
$dbForProject
);
} elseif ($sharedTablesV2) {
} else {
$queries = \array_map(
fn ($id) => Query::notEqual('$id', $id),
$projectCollectionIds
+21 -4
View File
@@ -195,9 +195,25 @@ class Migrations extends Action
$migrationOptions = $migration->getAttribute('options');
/** @var Database|null $projectDB */
$projectDB = null;
if ($credentials['projectId']) {
$useAppwriteApiSource = false;
if ($source === SourceAppwrite::getName() && empty($credentials['projectId'])) {
throw new \Exception('Source projectId is required for Appwrite migrations');
}
if (! empty($credentials['projectId'])) {
$this->sourceProject = $this->dbForPlatform->getDocument('projects', $credentials['projectId']);
$projectDB = call_user_func($this->getProjectDB, $this->sourceProject);
if ($this->sourceProject->isEmpty()) {
throw new \Exception('Source project not found for provided projectId');
}
$sourceRegion = $this->sourceProject->getAttribute('region', 'default');
$destinationRegion = $this->project->getAttribute('region', 'default');
$useAppwriteApiSource = $source === SourceAppwrite::getName()
&& $destination === DestinationAppwrite::getName()
&& $sourceRegion !== $destinationRegion;
if (! $useAppwriteApiSource) {
$projectDB = call_user_func($this->getProjectDB, $this->sourceProject);
}
}
$getDatabasesDB = fn (Document $database): Database =>
$this->getDatabasesDBForProject($database);
@@ -233,7 +249,7 @@ class Migrations extends Action
$credentials['endpoint'],
$credentials['apiKey'],
$getDatabasesDB,
SourceAppwrite::SOURCE_DATABASE,
$useAppwriteApiSource ? SourceAppwrite::SOURCE_API : SourceAppwrite::SOURCE_DATABASE,
$projectDB,
$queries
),
@@ -578,9 +594,10 @@ class Migrations extends Action
protected function getDatabasesDBForProject(Document $database)
{
if ($this->sourceProject) {
if (isset($this->sourceProject) && ! $this->sourceProject->isEmpty()) {
return ($this->getDatabasesDB)($database, $this->sourceProject);
}
return ($this->getDatabasesDB)($database);
}
-10
View File
@@ -771,16 +771,6 @@ abstract class Format
return 'EmailTemplateLocale';
}
break;
case 'getSmsTemplate':
case 'updateSmsTemplate':
case 'deleteSmsTemplate':
switch ($param) {
case 'type':
return 'SmsTemplateType';
case 'locale':
return 'SmsTemplateLocale';
}
break;
case 'createPlatform':
switch ($param) {
case 'type':
+3
View File
@@ -238,6 +238,9 @@ class Request extends UtopiaRequest
if ($allowedParams !== null) {
$params = array_intersect_key($params, array_flip($allowedParams));
}
if (!isset($params['project'])) {
$params['project'] = $this->getHeader('x-appwrite-project', '');
}
ksort($params);
return md5($this->getURI() . '*' . serialize($params) . '*' . APP_CACHE_BUSTER);
}
@@ -35,6 +35,13 @@ class V19 extends Filter
case 'functions.updateVariable':
$content['secret'] = false;
break;
case 'functions.getDeploymentDownload':
// Pre-1.7.0 clients call the legacy alias
// `/v1/functions/:functionId/deployments/:deploymentId/build/download`,
// which always downloaded the build output. The merged 1.7.0 endpoint
// requires an explicit `type` param, so force it to `output` here.
$content['type'] = 'output';
break;
}
return $content;
}
+2 -2
View File
@@ -73,10 +73,10 @@ class V22 extends Filter
public function parse(array $content, string $model): array
{
switch ($model) {
case 'project.updateServiceStatus':
case 'project.updateService':
$content = $this->parseUpdateServiceStatus($content);
break;
case 'project.updateProtocolStatus':
case 'project.updateProtocol':
$content = $this->parseUpdateProtocolStatus($content);
break;
case 'project.createKey':
-1
View File
@@ -265,7 +265,6 @@ class Response extends SwooleResponse
public const MODEL_VARIABLE = 'variable';
public const MODEL_VARIABLE_LIST = 'variableList';
public const MODEL_VCS = 'vcs';
public const MODEL_SMS_TEMPLATE = 'smsTemplate';
public const MODEL_EMAIL_TEMPLATE = 'emailTemplate';
// Health
@@ -0,0 +1,29 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class ProviderRepositoryFrameworkList extends BaseList
{
public array $conditions = [
'type' => 'framework',
];
public function __construct()
{
parent::__construct(
'Framework Provider Repositories List',
Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST,
'frameworkProviderRepositories',
Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK
);
$this->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Provider repository list type.',
'default' => 'framework',
'example' => 'framework',
]);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class ProviderRepositoryRuntimeList extends BaseList
{
public array $conditions = [
'type' => 'runtime',
];
public function __construct()
{
parent::__construct(
'Runtime Provider Repositories List',
Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST,
'runtimeProviderRepositories',
Response::MODEL_PROVIDER_REPOSITORY_RUNTIME
);
$this->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Provider repository list type.',
'default' => 'runtime',
'example' => 'runtime',
]);
}
}
@@ -1,32 +0,0 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class TemplateSMS extends Template
{
public function __construct()
{
parent::__construct();
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'SmsTemplate';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_SMS_TEMPLATE;
}
}