mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 00:49:02 +00:00
Merge pull request #11056 from appwrite/feat-project-labels
Feat: Project labels
This commit is contained in:
commit
b1373c02bc
7 changed files with 322 additions and 2 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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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