mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 16:38:32 +00:00
Merge branch '1.8.x' of https://github.com/appwrite/appwrite into migrations-cleanup
This commit is contained in:
commit
2a679ead32
10 changed files with 335 additions and 8 deletions
|
|
@ -330,7 +330,18 @@ $platformCollections = [
|
|||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('labels'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ App::post('/v1/projects')
|
|||
'accessedAt' => DateTime::now(),
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
'database' => $dsn,
|
||||
'labels' => [],
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
|
||||
|
|
|
|||
|
|
@ -360,6 +360,8 @@ $register->set('smtp', function () {
|
|||
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
|
||||
$mail->SMTPAutoTLS = false;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Timeout = 10; /* Connection timeout */
|
||||
$mail->getSMTPInstance()->Timelimit = 30; /* Timeout for each individual SMTP command (e.g. HELO, EHLO, etc.) */
|
||||
|
||||
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
|
||||
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Projects\Http\Projects\Labels;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'updateProjectLabels';
|
||||
}
|
||||
|
||||
protected function getQueriesValidator(): Validator
|
||||
{
|
||||
return new Projects();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT)
|
||||
->setHttpPath('/v1/projects/:projectId/labels')
|
||||
->desc('Update project labels')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateLabels',
|
||||
description: <<<EOT
|
||||
Update the project labels by its unique ID. Labels can be used to easily filter projects in an organization.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_LABELS_SIZE), 'Array of project labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_LABELS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $labels
|
||||
*/
|
||||
public function action(
|
||||
string $projectId,
|
||||
array $labels,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
): void {
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$project->setAttribute('labels', (array) \array_values(\array_unique($labels)));
|
||||
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ use Appwrite\Platform\Modules\Projects\Http\DevKeys\Delete as DeleteDevKey;
|
|||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\Get as GetDevKey;
|
||||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\Update as UpdateDevKey;
|
||||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\XList as ListDevKeys;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Labels\Update as UpdateProjectLabels;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\XList as ListProjects;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
|
|
@ -22,5 +23,6 @@ class Http extends Service
|
|||
$this->addAction(DeleteDevKey::getName(), new DeleteDevKey());
|
||||
|
||||
$this->addAction(ListProjects::getName(), new ListProjects());
|
||||
$this->addAction(UpdateProjectLabels::getName(), new UpdateProjectLabels());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class Deletes extends Action
|
|||
break;
|
||||
case DELETE_TYPE_AUDIT:
|
||||
if (!$project->isEmpty()) {
|
||||
$this->deleteAuditLogs($project, $auditRetention, $getAudit);
|
||||
$this->deleteAuditLogs($project, $getAudit, $auditRetention);
|
||||
}
|
||||
break;
|
||||
case DELETE_TYPE_REALTIME:
|
||||
|
|
@ -783,14 +783,13 @@ class Deletes extends Action
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Database $dbForPlatform
|
||||
* @param callable $getProjectDB
|
||||
* @param string $auditRetention
|
||||
* @param Document $project
|
||||
* @param callable $getAudit
|
||||
* @param string $auditRetention
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function deleteAuditLogs(Document $project, string $auditRetention, callable $getAudit): void
|
||||
private function deleteAuditLogs(Document $project, callable $getAudit, string $auditRetention): void
|
||||
{
|
||||
$projectId = $project->getId();
|
||||
/** @var Audit $audit */
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ class Mails extends Action
|
|||
throw new Exception('Skipped mail processing. No SMTP configuration has been set.');
|
||||
}
|
||||
|
||||
$log->addTag('type', empty($smtp) ? 'cloud' : 'smtp');
|
||||
$type = empty($smtp) ? 'cloud' : 'smtp';
|
||||
$log->addTag('type', $type);
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN');
|
||||
|
|
@ -182,6 +183,9 @@ class Mails extends Action
|
|||
try {
|
||||
$mail->send();
|
||||
} catch (\Throwable $error) {
|
||||
if ($type === 'smtp') {
|
||||
throw new Exception('Error sending mail: ' . $error->getMessage(), 401);
|
||||
}
|
||||
throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
|
@ -209,6 +213,8 @@ class Mails extends Action
|
|||
$mail->SMTPSecure = $smtp['secure'];
|
||||
$mail->SMTPAutoTLS = false;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Timeout = 10; /* Connection timeout */
|
||||
$mail->getSMTPInstance()->Timelimit = 30; /* Timeout for each individual SMTP command (e.g. HELO, EHLO, etc.) */
|
||||
|
||||
$mail->setFrom($smtp['senderEmail'], $smtp['senderName']);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ class Projects extends Base
|
|||
{
|
||||
public const ALLOWED_ATTRIBUTES = [
|
||||
'name',
|
||||
'teamId'
|
||||
'teamId',
|
||||
'labels',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -276,6 +276,13 @@ class Project extends Model
|
|||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('labels', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Labels for the project.',
|
||||
'default' => [],
|
||||
'example' => ['vip'],
|
||||
'array' => true,
|
||||
])
|
||||
;
|
||||
|
||||
$services = Config::getParam('services', []);
|
||||
|
|
|
|||
|
|
@ -5382,4 +5382,216 @@ class ProjectsConsoleClientTest extends Scope
|
|||
/**
|
||||
* Devkeys Tests ends here ------------------------------------------------
|
||||
*/
|
||||
|
||||
public function testProjectLabels(): void
|
||||
{
|
||||
// Setup: Prepare team
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Query Select Test Team',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
$teamId = $team['body']['$id'];
|
||||
|
||||
// Setup: Prepare project
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Test project - Labels 1',
|
||||
'teamId' => $teamId,
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
$this->assertIsArray($project['body']['labels']);
|
||||
$this->assertCount(0, $project['body']['labels']);
|
||||
$projectId = $project['body']['$id'];
|
||||
|
||||
// Apply labels
|
||||
$project = $this->client->call(Client::METHOD_PUT, '/projects/' . $projectId . '/labels', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'labels' => ['vip', 'imagine', 'blocked']
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $project['headers']['status-code']);
|
||||
$this->assertIsArray($project['body']['labels']);
|
||||
$this->assertCount(3, $project['body']['labels']);
|
||||
$this->assertEquals('vip', $project['body']['labels'][0]);
|
||||
$this->assertEquals('imagine', $project['body']['labels'][1]);
|
||||
$this->assertEquals('blocked', $project['body']['labels'][2]);
|
||||
|
||||
// Update labels
|
||||
$project = $this->client->call(Client::METHOD_PUT, '/projects/' . $projectId . '/labels', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'labels' => ['nonvip', 'imagine']
|
||||
]);
|
||||
$this->assertEquals(200, $project['headers']['status-code']);
|
||||
$this->assertIsArray($project['body']['labels']);
|
||||
$this->assertCount(2, $project['body']['labels']);
|
||||
$this->assertEquals('nonvip', $project['body']['labels'][0]);
|
||||
$this->assertEquals('imagine', $project['body']['labels'][1]);
|
||||
|
||||
// Filter by labels
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['nonvip'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(1, $projects['body']['total']);
|
||||
$this->assertEquals($projectId, $projects['body']['projects'][0]['$id']);
|
||||
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['vip'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(0, $projects['body']['total']);
|
||||
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['imagine'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(1, $projects['body']['total']);
|
||||
$this->assertEquals($projectId, $projects['body']['projects'][0]['$id']);
|
||||
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['nonvip', 'imagine'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(1, $projects['body']['total']);
|
||||
$this->assertEquals($projectId, $projects['body']['projects'][0]['$id']);
|
||||
|
||||
// Setup: Second project with only imagine label
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Test project - Labels 2',
|
||||
'teamId' => $teamId,
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
$this->assertIsArray($project['body']['labels']);
|
||||
$this->assertCount(0, $project['body']['labels']);
|
||||
$projectId2 = $project['body']['$id'];
|
||||
|
||||
$project = $this->client->call(Client::METHOD_PUT, '/projects/' . $projectId2 . '/labels', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'labels' => ['vip', 'imagine']
|
||||
]);
|
||||
$this->assertEquals(200, $project['headers']['status-code']);
|
||||
$this->assertIsArray($project['body']['labels']);
|
||||
$this->assertCount(2, $project['body']['labels']);
|
||||
$this->assertEquals('vip', $project['body']['labels'][0]);
|
||||
$this->assertEquals('imagine', $project['body']['labels'][1]);
|
||||
|
||||
// List of imagine has both
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['imagine'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(2, $projects['body']['total']);
|
||||
$this->assertEquals($projectId, $projects['body']['projects'][0]['$id']);
|
||||
$this->assertEquals($projectId2, $projects['body']['projects'][1]['$id']);
|
||||
|
||||
// List of vip only has second
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['vip'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(1, $projects['body']['total']);
|
||||
$this->assertEquals($projectId2, $projects['body']['projects'][0]['$id']);
|
||||
|
||||
// List of vip and imagine has second
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['vip'])->toString(),
|
||||
Query::contains('labels', ['imagine'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(1, $projects['body']['total']);
|
||||
$this->assertEquals($projectId2, $projects['body']['projects'][0]['$id']);
|
||||
|
||||
// List of vip or imagine has second
|
||||
$projects = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('labels', ['vip', 'imagine'])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $projects['headers']['status-code']);
|
||||
$this->assertEquals(2, $projects['body']['total']);
|
||||
$this->assertEquals($projectId, $projects['body']['projects'][0]['$id']);
|
||||
$this->assertEquals($projectId2, $projects['body']['projects'][1]['$id']);
|
||||
|
||||
// Cleanup
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $projectId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $projectId2, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue