Merge branch 'feat-git-integration' into feat-git-integration-migrations

This commit is contained in:
Bradley Schofield 2023-08-18 11:18:58 +01:00
commit 0ebd6d43d5
No known key found for this signature in database
GPG key ID: CDDF1217D7D66C9D
21 changed files with 260 additions and 115 deletions

View file

@ -46,7 +46,7 @@ jobs:
- name: Start Appwrite
run: |
docker compose up -d
sleep 60
sleep 30
- name: Doctor
run: |

View file

@ -363,6 +363,11 @@ return [
'description' => 'The requested range is not satisfiable. Please check the value of the Range header.',
'code' => 416,
],
Exception::STORAGE_INVALID_APPWRITE_ID => [
'name' => Exception::STORAGE_INVALID_APPWRITE_ID,
'description' => 'The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is valid id and not unique().',
'code' => 400,
],
/** VCS */
Exception::INSTALLATION_NOT_FOUND => [
@ -385,6 +390,11 @@ return [
'description' => 'External contribution is already authorized.',
'code' => 409,
],
Exception::GENERAL_PROVIDER_FAILURE => [
'name' => Exception::GENERAL_PROVIDER_FAILURE,
'description' => 'VCS (Version Control System) provider failed to proccess the request.',
'code' => 400,
],
/** Functions */
Exception::FUNCTION_NOT_FOUND => [

View file

@ -17,7 +17,7 @@
"emails.magicSession.signature": "Equipa {{project}}",
"emails.recovery.subject": "Redefinição de senha",
"emails.recovery.hello": "Olá {{name}}",
"emails.recovery.body": "tilize este link para redefinir a palavra-passe do seu projecto {{project}}",
"emails.recovery.body": "Utilize este link para redefinir a palavra-passe do seu projecto {{project}}",
"emails.recovery.footer": "Se não pediu para redefinir a sua palavra-passe, pode ignorar esta mensagem.",
"emails.recovery.thanks": "Obrigado",
"emails.recovery.signature": "Equipa {{project}}",

@ -1 +1 @@
Subproject commit a1ec99e86331e5eeb56614c69f50e83f334deafb
Subproject commit 451e591a1d6c0843fdaef131f1064fd6cdbb2a3b

View file

@ -1086,11 +1086,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->param('encrypt', false, new Boolean(), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.', true)
->inject('response')
->inject('dbForProject')
->inject('database')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
// Ensure attribute default is within required size
$validator = new Text($size, 0);
@ -1098,6 +1099,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
$filters = [];
if ($encrypt) {
$filters[] = 'encrypt';
}
$attribute = createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_STRING,
@ -1105,6 +1112,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
'required' => $required,
'default' => $default,
'array' => $array,
'filters' => $filters,
]), $response, $dbForProject, $database, $events);
$response
@ -1509,6 +1517,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
$filters[] = 'datetime';
$attribute = createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_DATETIME,
@ -1516,7 +1526,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
'required' => $required,
'default' => $default,
'array' => $array,
'filters' => ['datetime']
'filters' => $filters,
]), $response, $dbForProject, $database, $events);
$response
@ -1739,6 +1749,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->inject('dbForProject')
->inject('events')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,

View file

@ -142,7 +142,7 @@ App::post('/v1/functions')
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->param('logging', true, new Boolean(), 'Do executions get logged?', true)
->param('entrypoint', '', new Text(1028), 'Entrypoint File.')
->param('commands', '', new Text(1028, 0), 'Build Commands.', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
@ -165,7 +165,12 @@ App::post('/v1/functions')
// build from template
$template = new Document([]);
if (!empty($templateRepository) && !empty($templateOwner) && !empty($templateRootDirectory) && !empty($templateBranch)) {
if (
!empty($templateRepository)
&& !empty($templateOwner)
&& !empty($templateRootDirectory)
&& !empty($templateBranch)
) {
$template->setAttribute('repositoryName', $templateRepository)
->setAttribute('ownerName', $templateOwner)
->setAttribute('rootDirectory', $templateRootDirectory)
@ -609,7 +614,7 @@ App::put('/v1/functions/:functionId')
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->param('logging', true, new Boolean(), 'Do executions get logged?', true)
->param('entrypoint', '', new Text(1028), 'Entrypoint File.')
->param('commands', '', new Text(1028, 0), 'Build Commands.', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
@ -1273,7 +1278,8 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->inject('dbForConsole')
->inject('project')
->inject('gitHub')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, GitHub $github) use ($redeployVcs) {
->inject('events')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, GitHub $github, Event $events) use ($redeployVcs) {
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
@ -1298,6 +1304,10 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
$redeployVcs($request, $function, $project, $installation, $dbForProject, new Document([]), $github);
$events
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->noContent();
});
@ -1318,7 +1328,7 @@ App::post('/v1/functions/:functionId/executions')
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new Assoc(), 'HTP headers of execution. Defaults to empty.', true)
->param('headers', [], new Assoc(), 'HTTP headers of execution. Defaults to empty.', true)
->inject('response')
->inject('project')
->inject('dbForProject')
@ -1476,11 +1486,9 @@ App::post('/v1/functions/:functionId/executions')
$vars = [];
// Shared vars
$varsShared = $project->getAttribute('variables', []);
$vars = \array_merge($vars, \array_reduce($varsShared, function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []));
foreach ($project->getAttribute('variables', []) as $var) {
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
// Function vars
$vars = \array_merge($vars, array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
@ -1825,7 +1833,12 @@ App::get('/v1/functions/:functionId/variables/:variableId')
}
$variable = $dbForProject->getDocument('variables', $variableId);
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getInternalId() || $variable->getAttribute('resourceType') !== 'function') {
if (
$variable === false ||
$variable->isEmpty() ||
$variable->getAttribute('resourceInternalId') !== $function->getInternalId() ||
$variable->getAttribute('resourceType') !== 'function'
) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}

View file

@ -56,7 +56,7 @@ App::post('/v1/proxy/rules')
$message .= '.';
} else {
$message = "Domain already assigned to different project.";
$message = 'Domain already assigned to different project.';
}
throw new Exception(Exception::RULE_ALREADY_EXISTS, $message);

View file

@ -443,6 +443,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
}
$idValidator = new UID();
if (!$idValidator->isValid($request->getHeader('x-appwrite-id'))) {
throw new Exception(Exception::STORAGE_INVALID_APPWRITE_ID);
}
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
if ($end === $fileSize - 1 || $end === $fileSize) {
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to -1 notify it's last chunk

View file

@ -36,6 +36,7 @@ use Utopia\Detector\Adapter\Swift;
use Utopia\Detector\Detector;
use Utopia\Validator\Boolean;
use function PHPUnit\Framework\throwException;
use function Swoole\Coroutine\batch;
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, callable $getProjectDB, Request $request) {
@ -702,7 +703,11 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
}
$repository = $oauth2->createRepository($accessToken, $name, $private);
try {
$repository = $oauth2->createRepository($accessToken, $name, $private);
} catch (Exception $exception) {
throw new Exception(Exception::GENERAL_PROVIDER_FAILURE, "GitHub failed to process the request: " . $exception->getMessage());
}
} else {
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
@ -710,7 +715,11 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
$repository = $github->createRepository($owner, $name, $private);
try {
$repository = $github->createRepository($owner, $name, $private);
} catch (Exception $exception) {
throw new Exception(Exception::GENERAL_PROVIDER_FAILURE, "GitHub failed to process the request: " . $exception->getMessage());
}
}
if (isset($repository['message'])) {

View file

@ -204,7 +204,7 @@ App::init()
Request::setRoute($route);
if ($route === null) {
return $response->setStatusCode(404)->send("Not Found");
return $response->setStatusCode(404)->send('Not Found');
}
$requestFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));

View file

@ -330,14 +330,15 @@ class BuildsV1 extends Worker
$vars = [];
// global vars
$vars = \array_merge($vars, \array_reduce($dbForProject->find('variables', [
// Global vars
$varsFromProject = $dbForProject->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []));
]);
foreach ($varsFromProject as $var) {
$vars[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
}
// Function vars
$vars = \array_merge($vars, array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
@ -454,12 +455,15 @@ class BuildsV1 extends Worker
/** Update function schedule */
$dbForConsole = $this->getConsoleDB();
// Inform scheduler if function is still active
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
$scheduleId = $function->getAttribute('scheduleId', '');
if (!empty($scheduleId)) {
$schedule = $dbForConsole->getDocument('schedules', $scheduleId);
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('schedule', $function->getAttribute('schedule'))
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
}
} catch (\Throwable $th) {
$endTime = DateTime::now();
$durationEnd = \microtime(true);

View file

@ -85,7 +85,7 @@
}
],
"require-dev": {
"appwrite/sdk-generator": "dev-master as 0.33.99",
"appwrite/sdk-generator": "0.34.*",
"ext-fileinfo": "*",
"phpunit/phpunit": "9.5.20",
"squizlabs/php_codesniffer": "^3.7",

70
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b87e28f6f096af1fd3b1ddee62fe2f13",
"content-hash": "e4934eff80bec5e9fe402528df07d72d",
"packages": [
{
"name": "adhocore/jwt",
@ -727,16 +727,16 @@
},
{
"name": "matomo/device-detector",
"version": "6.1.4",
"version": "6.1.5",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "74f6c4f6732b3ad6cdf25560746841d522969112"
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/74f6c4f6732b3ad6cdf25560746841d522969112",
"reference": "74f6c4f6732b3ad6cdf25560746841d522969112",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/40ca2990dba2c1719e5c62168e822e0b86c167d4",
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4",
"shasum": ""
},
"require": {
@ -792,7 +792,7 @@
"source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/"
},
"time": "2023-08-02T08:48:53+00:00"
"time": "2023-08-17T16:17:41+00:00"
},
{
"name": "mongodb/mongodb",
@ -1963,16 +1963,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.3.0",
"version": "0.3.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "8a1d4de19002e4c8eabf368edff6b653b6722880"
"reference": "af4233f4ff6a37982dad294033199ce29cafc00c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/8a1d4de19002e4c8eabf368edff6b653b6722880",
"reference": "8a1d4de19002e4c8eabf368edff6b653b6722880",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/af4233f4ff6a37982dad294033199ce29cafc00c",
"reference": "af4233f4ff6a37982dad294033199ce29cafc00c",
"shasum": ""
},
"require": {
@ -2015,9 +2015,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.3.0"
"source": "https://github.com/utopia-php/migration/tree/0.3.1"
},
"time": "2023-08-16T11:57:13+00:00"
"time": "2023-08-17T14:18:09+00:00"
},
{
"name": "utopia-php/mongo",
@ -2564,6 +2564,12 @@
"url": "https://github.com/utopia-php/vcs.git",
"reference": "fc9c38a3f84a4391470cc7184199dec6f953b144"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/fc9c38a3f84a4391470cc7184199dec6f953b144",
"reference": "fc9c38a3f84a4391470cc7184199dec6f953b144",
"shasum": ""
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
@ -2582,25 +2588,7 @@
"Utopia\\Detector\\": "src/Detector"
}
},
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/VCS"
}
},
"scripts": {
"lint": [
"./vendor/bin/pint --test --config pint.json"
],
"format": [
"./vendor/bin/pint --config pint.json"
],
"check": [
"./vendor/bin/phpstan analyse --level 8 -c phpstan.neon src tests"
],
"test": [
"./vendor/bin/phpunit --configuration phpunit.xml --debug"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -2611,6 +2599,10 @@
"utopia",
"vcs"
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.1.0"
},
"time": "2023-08-09T20:48:51+00:00"
},
{
@ -2798,7 +2790,7 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "dev-master",
"version": "0.34.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
@ -2823,7 +2815,6 @@
"phpunit/phpunit": "^9.5.21",
"squizlabs/php_codesniffer": "^3.6"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
@ -5409,18 +5400,9 @@
"time": "2023-07-26T07:16:09+00:00"
}
],
"aliases": [
{
"package": "appwrite/sdk-generator",
"version": "9999999-dev",
"alias": "0.33.99",
"alias_normalized": "0.33.99.0"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"appwrite/sdk-generator": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View file

@ -115,12 +115,14 @@ class Exception extends \Exception
public const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
public const STORAGE_INVALID_RANGE = 'storage_invalid_range';
public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id';
/** VCS */
public const INSTALLATION_NOT_FOUND = 'installation_not_found';
public const PROVIDER_REPOSITORY_NOT_FOUND = 'provider_repository_not_found';
public const REPOSITORY_NOT_FOUND = 'repository_not_found';
public const PROVIDER_CONTRIBUTION_CONFLICT = 'provider_contribution_conflict';
public const GENERAL_PROVIDER_FAILURE = 'general_provider_failure';
/** Functions */
public const FUNCTION_NOT_FOUND = 'function_not_found';

View file

@ -29,6 +29,7 @@ class V19 extends Migration
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
$this->alterPermissionIndex('_metadata');
$this->alterUidType('_metadata');
Console::info('Migrating Databases');
$this->migrateDatabases();
@ -57,11 +58,13 @@ class V19 extends Migration
$databaseTable = "database_{$database->getInternalId()}";
$this->alterPermissionIndex($databaseTable);
$this->alterUidType($databaseTable);
foreach ($this->documentsIterator($databaseTable) as $collection) {
$collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}";
Console::log("Migrating Collections of {$collectionTable} {$collection->getId()} ({$collection->getAttribute('name')})");
$this->alterPermissionIndex($collectionTable);
$this->alterUidType($collectionTable);
}
}
}
@ -98,6 +101,7 @@ class V19 extends Migration
}
if (!in_array($id, ['files', 'collections'])) {
$this->alterPermissionIndex($id);
$this->alterUidType($id);
}
usleep(50000);
@ -131,7 +135,7 @@ class V19 extends Migration
protected function alterPermissionIndex($collectionName): void
{
try {
$table = "`{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collectionName}_perms";
$table = "`{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collectionName}_perms`";
$this->pdo->prepare("
ALTER TABLE {$table}
DROP INDEX `_permission`,
@ -142,6 +146,19 @@ class V19 extends Migration
}
}
protected function alterUidType($collectionName): void
{
try {
$table = "`{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$collectionName}`";
$this->pdo->prepare("
ALTER TABLE {$table}
CHANGE COLUMN `_uid` `_uid` VARCHAR(255) NOT NULL ;
")->execute();
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
}
/**
* Migrating all Bucket tables.
*
@ -155,6 +172,7 @@ class V19 extends Migration
$id = "bucket_{$bucket->getInternalId()}";
Console::log("Migrating Bucket {$id} {$bucket->getId()} ({$bucket->getAttribute('name')})");
$this->alterPermissionIndex($id);
$this->alterUidType($id);
}
}
}

View file

@ -81,7 +81,6 @@ class V12 extends Filter
case Response::MODEL_WEBHOOK_LIST:
case Response::MODEL_KEY_LIST:
case Response::MODEL_PLATFORM_LIST:
// case Response::MODEL_DOMAIN_LIST:
case Response::MODEL_COUNTRY_LIST:
case Response::MODEL_CONTINENT_LIST:
case Response::MODEL_LANGUAGE_LIST:

View file

@ -100,8 +100,6 @@ class Executor
}
/**
*
*
* Listen to realtime logs stream of a runtime
*
* @param string $deploymentId

View file

@ -593,6 +593,110 @@ class DatabasesCustomServerTest extends Scope
$this->assertFalse($collection['body']['enabled']);
}
/**
* @depends testListCollections
*/
public function testCreateEncryptedAttribute(array $data): void
{
$databaseId = $data['databaseId'];
/**
* Test for SUCCESS
*/
// Create collection
$actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'Encrypted Actors Data',
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'documentSecurity' => true,
]);
$this->assertEquals(201, $actors['headers']['status-code']);
$this->assertEquals($actors['body']['name'], 'Encrypted Actors Data');
/**
* Test for creating encrypted attributes
*/
$attributesPath = '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes';
$firstName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'firstName',
'size' => 256,
'required' => true,
]);
$lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'lastName',
'size' => 256,
'required' => true,
'encrypt' => true,
]);
/**
* Check status of every attribute
*/
$this->assertEquals(202, $firstName['headers']['status-code']);
$this->assertEquals('firstName', $firstName['body']['key']);
$this->assertEquals('string', $firstName['body']['type']);
$this->assertEquals(202, $lastName['headers']['status-code']);
$this->assertEquals('lastName', $lastName['body']['key']);
$this->assertEquals('string', $lastName['body']['type']);
// Wait for database worker to finish creating attributes
sleep(2);
// Creating document to ensure cache is purged on schema change
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::unique(),
'data' => [
'firstName' => 'Jonah',
'lastName' => 'Jameson',
],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
// Check document to ensure cache is purged on schema change
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/documents/' . $document['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $document['headers']['status-code']);
$this->assertEquals('Jonah', $document['body']['firstName']);
$this->assertEquals('Jameson', $document['body']['lastName']);
}
public function testDeleteAttribute(): array
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([

View file

@ -241,6 +241,29 @@ trait StorageBase
$this->assertEquals(400, $failedBucket['headers']['status-code']);
/**
* Test for FAILURE set x-appwrite-id to unique()
*/
$source = realpath(__DIR__ . '/../../../resources/logo.png');
$totalSize = \filesize($source);
$res = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'content-range' => 'bytes 0-' . $size . '/' . $size,
'x-appwrite-id' => 'unique()',
], $this->getHeaders()), [
'fileId' => ID::unique(),
'file' => new CURLFile($source, 'image/png', 'logo.png'),
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$this->assertEquals(400, $res['headers']['status-code']);
$this->assertEquals('The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is valid id and not unique().', $res['body']['message']);
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
}

View file

@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Teams;
use Tests\E2E\Client;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Helpers\ID;
trait TeamsBaseServer
{

View file

@ -562,40 +562,6 @@ class V15Test extends TestCase
$this->assertEquals($expected, $result);
}
// /**
// * @dataProvider createdAtUpdatedAtProvider
// */
// public function testDomain(array $content, array $expected): void
// {
// $model = Response::MODEL_DOMAIN;
// $result = $this->filter->parse($content, $model);
// $this->assertEquals($expected, $result);
// }
// /**
// * @dataProvider createdAtUpdatedAtProvider
// */
// public function testDomainList(array $content, array $expected): void
// {
// $model = Response::MODEL_DOMAIN_LIST;
// $content = [
// 'domains' => [$content],
// 'total' => 1,
// ];
// $expected = [
// 'domains' => [$expected],
// 'total' => 1,
// ];
// $result = $this->filter->parse($content, $model);
// $this->assertEquals($expected, $result);
// }
public function executionProvider(): array
{
return [