appwrite/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2026-02-12 16:36:37 +02:00

9809 lines
442 KiB
PHP

<?php
namespace Tests\E2E\Services\Databases\TablesDB;
use Appwrite\Extend\Exception;
use Tests\E2E\Client;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Operator;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
trait DatabasesBase
{
public function testCreateDatabase(): array
{
/**
* Test for SUCCESS
*/
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Test Database'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('Test Database', $database['body']['name']);
$this->assertEquals('tablesdb', $database['body']['type']);
return ['databaseId' => $database['body']['$id']];
}
/**
* @depends testCreateDatabase
*/
public function testCreateTable(array $data): array
{
$databaseId = $data['databaseId'];
/**
* Test for SUCCESS
*/
$movies = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Movies',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $movies['headers']['status-code']);
$this->assertEquals($movies['body']['name'], 'Movies');
$this->assertArrayHasKey('bytesMax', $movies['body']);
$this->assertArrayHasKey('bytesUsed', $movies['body']);
$this->assertIsInt($movies['body']['bytesMax']);
$this->assertIsInt($movies['body']['bytesUsed']);
$this->assertGreaterThanOrEqual(0, $movies['body']['bytesMax']);
$this->assertGreaterThanOrEqual(0, $movies['body']['bytesUsed']);
$actors = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Actors',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $actors['headers']['status-code']);
$this->assertEquals($actors['body']['name'], 'Actors');
return [
'databaseId' => $databaseId,
'moviesId' => $movies['body']['$id'],
'actorsId' => $actors['body']['$id'],
];
}
/**
* @depends testCreateTable
*/
public function testConsoleProject(array $data): void
{
if ($this->getSide() === 'server') {
// Server side can't get past the invalid key check anyway
$this->expectNotToPerformAssertions();
return;
}
$response = $this->client->call(
Client::METHOD_GET,
'/tablesdb/console/tables/' . $data['moviesId'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
], $this->getHeaders())
);
$this->assertEquals(401, $response['headers']['status-code']);
$this->assertEquals('general_access_forbidden', $response['body']['type']);
$this->assertEquals('This endpoint is not available for the console project. The Appwrite Console is a reserved project ID and cannot be used with the Appwrite SDKs and APIs. Please check if your project ID is correct.', $response['body']['message']);
$response = $this->client->call(
Client::METHOD_GET,
'/tablesdb/console/tables/' . $data['moviesId'] . '/rows',
array_merge([
'content-type' => 'application/json',
// 'x-appwrite-project' => '', empty header
], $this->getHeaders())
);
$this->assertEquals(401, $response['headers']['status-code']);
$this->assertEquals('No Appwrite project was specified. Please specify your project ID when initializing your Appwrite SDK.', $response['body']['message']);
}
/**
* @depends testCreateTable
*/
public function testDisableTable(array $data): void
{
$databaseId = $data['databaseId'];
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'name' => 'Movies',
'enabled' => false,
'rowSecurity' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertFalse($response['body']['enabled']);
$this->assertArrayHasKey('bytesMax', $response['body']);
$this->assertArrayHasKey('bytesUsed', $response['body']);
if ($this->getSide() === 'client') {
$responseCreateRow = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(404, $responseCreateRow['headers']['status-code']);
$responseListRow = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $responseListRow['headers']['status-code']);
$responseGetRow = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/someID', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $responseGetRow['headers']['status-code']);
}
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'name' => 'Movies',
'enabled' => true,
'rowSecurity' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertTrue($response['body']['enabled']);
}
/**
* @depends testCreateTable
*/
public function testCreateColumns(array $data): array
{
$databaseId = $data['databaseId'];
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$description = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'description',
'size' => 512,
'required' => false,
'default' => '',
]);
$tagline = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'tagline',
'size' => 512,
'required' => false,
'default' => '',
]);
$releaseYear = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'releaseYear',
'required' => true,
'min' => 1900,
'max' => 2200,
]);
$duration = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'duration',
'required' => false,
'min' => 60,
]);
$actors = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
]);
$datetime = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/datetime', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'birthDay',
'required' => false,
]);
$relationship = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $data['actorsId'],
'type' => 'oneToMany',
'twoWay' => true,
'key' => 'starringActors',
'twoWayKey' => 'movie'
]);
$integers = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'integers',
'required' => false,
'array' => true,
'min' => 10,
'max' => 99,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$this->assertEquals($title['body']['key'], 'title');
$this->assertEquals($title['body']['type'], 'string');
$this->assertEquals($title['body']['size'], 256);
$this->assertEquals($title['body']['required'], true);
$this->assertEquals(202, $description['headers']['status-code']);
$this->assertEquals($description['body']['key'], 'description');
$this->assertEquals($description['body']['type'], 'string');
$this->assertEquals($description['body']['size'], 512);
$this->assertEquals($description['body']['required'], false);
$this->assertEquals($description['body']['default'], '');
$this->assertEquals(202, $tagline['headers']['status-code']);
$this->assertEquals($tagline['body']['key'], 'tagline');
$this->assertEquals($tagline['body']['type'], 'string');
$this->assertEquals($tagline['body']['size'], 512);
$this->assertEquals($tagline['body']['required'], false);
$this->assertEquals($tagline['body']['default'], '');
$this->assertEquals(202, $releaseYear['headers']['status-code']);
$this->assertEquals($releaseYear['body']['key'], 'releaseYear');
$this->assertEquals($releaseYear['body']['type'], 'integer');
$this->assertEquals($releaseYear['body']['required'], true);
$this->assertEquals(202, $duration['headers']['status-code']);
$this->assertEquals($duration['body']['key'], 'duration');
$this->assertEquals($duration['body']['type'], 'integer');
$this->assertEquals($duration['body']['required'], false);
$this->assertEquals(202, $actors['headers']['status-code']);
$this->assertEquals($actors['body']['key'], 'actors');
$this->assertEquals($actors['body']['type'], 'string');
$this->assertEquals($actors['body']['size'], 256);
$this->assertEquals($actors['body']['required'], false);
$this->assertEquals($actors['body']['array'], true);
$this->assertEquals($datetime['headers']['status-code'], 202);
$this->assertEquals($datetime['body']['key'], 'birthDay');
$this->assertEquals($datetime['body']['type'], 'datetime');
$this->assertEquals($datetime['body']['required'], false);
$this->assertEquals($relationship['headers']['status-code'], 202);
$this->assertEquals($relationship['body']['key'], 'starringActors');
$this->assertEquals($relationship['body']['type'], 'relationship');
$this->assertEquals($relationship['body']['relatedTable'], $data['actorsId']);
$this->assertEquals($relationship['body']['relationType'], 'oneToMany');
$this->assertEquals($relationship['body']['twoWay'], true);
$this->assertEquals($relationship['body']['twoWayKey'], 'movie');
$this->assertEquals(202, $integers['headers']['status-code']);
$this->assertEquals($integers['body']['key'], 'integers');
$this->assertEquals($integers['body']['type'], 'integer');
$this->assertArrayNotHasKey('size', $integers['body']);
$this->assertEquals($integers['body']['required'], false);
$this->assertEquals($integers['body']['array'], true);
// wait for database worker to create attributes
sleep(2);
$movies = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertIsArray($movies['body']['columns']);
$this->assertCount(9, $movies['body']['columns']);
$this->assertArrayHasKey('bytesMax', $movies['body']);
$this->assertArrayHasKey('bytesUsed', $movies['body']);
$this->assertGreaterThanOrEqual(0, $movies['body']['bytesUsed']);
$this->assertEquals($movies['body']['columns'][0]['key'], $title['body']['key']);
$this->assertEquals($movies['body']['columns'][1]['key'], $description['body']['key']);
$this->assertEquals($movies['body']['columns'][2]['key'], $tagline['body']['key']);
$this->assertEquals($movies['body']['columns'][3]['key'], $releaseYear['body']['key']);
$this->assertEquals($movies['body']['columns'][4]['key'], $duration['body']['key']);
$this->assertEquals($movies['body']['columns'][5]['key'], $actors['body']['key']);
$this->assertEquals($movies['body']['columns'][6]['key'], $datetime['body']['key']);
$this->assertEquals($movies['body']['columns'][7]['key'], $relationship['body']['key']);
$this->assertEquals($movies['body']['columns'][8]['key'], $integers['body']['key']);
return $data;
}
/**
* @depends testCreateColumns
*/
public function testListColumns(array $data): void
{
$databaseId = $data['databaseId'];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => [
Query::equal('type', ['string'])->toString(),
Query::limit(2)->toString(),
Query::cursorAfter(new Document(['$id' => 'title']))->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['columns']));
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => [Query::select(['key'])->toString()],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testCreateDatabase
*/
public function testPatchColumn(array $data): void
{
$databaseId = $data['databaseId'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'patch',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$this->assertEquals($table['body']['name'], 'patch');
$attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/'.$databaseId.'/tables/'.$table['body']['$id'].'/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'required' => true,
'size' => 100,
]);
$this->assertEquals(202, $attribute['headers']['status-code']);
$this->assertEquals($attribute['body']['size'], 100);
sleep(1);
$index = $this->client->call(Client::METHOD_POST, '/tablesdb/'.$databaseId.'/tables/'.$table['body']['$id'].'/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'titleIndex',
'type' => 'key',
'columns' => ['title'],
]);
$this->assertEquals(202, $index['headers']['status-code']);
sleep(1);
/**
* Update column size to exceed Index maximum length
*/
$attribute = $this->client->call(Client::METHOD_PATCH, '/tablesdb/'.$databaseId.'/tables/'.$table['body']['$id'].'/columns/string/'.$attribute['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'size' => 1000,
'required' => true,
'default' => null,
]);
$this->assertEquals(400, $attribute['headers']['status-code']);
$this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']);
}
public function testUpdateColumnEnum(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Test Database 2'
]);
$players = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $database['body']['$id'] . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Players',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
// Create enum column
$attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $database['body']['$id'] . '/tables/' . $players['body']['$id'] . '/columns/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'position',
'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward'],
'required' => true,
'array' => false,
]);
$this->assertEquals(202, $attribute['headers']['status-code']);
$this->assertEquals($attribute['body']['key'], 'position');
$this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward']);
\sleep(2);
// Update enum column
$attribute = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $database['body']['$id'] . '/tables/' . $players['body']['$id'] . '/columns/enum/' . $attribute['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach'],
'required' => true,
'default' => null
]);
$this->assertEquals(200, $attribute['headers']['status-code']);
$this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach']);
}
/**
* @depends testCreateColumns
*/
public function testColumnResponseModels(array $data): array
{
$databaseId = $data['databaseId'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Response Models',
// 'permissions' missing on purpose to make sure it's optional
'rowSecurity' => true,
]);
$this->assertEquals(201, $table['headers']['status-code']);
$this->assertEquals($table['body']['name'], 'Response Models');
$tableId = $table['body']['$id'];
$columnsPath = "/tablesdb/" . $databaseId . "/tables/{$tableId}/columns";
$string = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'string',
'size' => 16,
'required' => false,
'default' => 'default',
]);
$email = $this->client->call(Client::METHOD_POST, $columnsPath . '/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'email',
'required' => false,
'default' => 'default@example.com',
]);
$enum = $this->client->call(Client::METHOD_POST, $columnsPath . '/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
'default' => 'maybe',
]);
$ip = $this->client->call(Client::METHOD_POST, $columnsPath . '/ip', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'ip',
'required' => false,
'default' => '192.0.2.0',
]);
$url = $this->client->call(Client::METHOD_POST, $columnsPath . '/url', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'url',
'required' => false,
'default' => 'http://example.com',
]);
$integer = $this->client->call(Client::METHOD_POST, $columnsPath . '/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'integer',
'required' => false,
'min' => 1,
'max' => 5,
'default' => 3
]);
$float = $this->client->call(Client::METHOD_POST, $columnsPath . '/float', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'float',
'required' => false,
'min' => 1.5,
'max' => 5.5,
'default' => 3.5
]);
$boolean = $this->client->call(Client::METHOD_POST, $columnsPath . '/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'boolean',
'required' => false,
'default' => true,
]);
$datetime = $this->client->call(Client::METHOD_POST, $columnsPath . '/datetime', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'datetime',
'required' => false,
'default' => null,
]);
$relationship = $this->client->call(Client::METHOD_POST, $columnsPath . '/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $data['actorsId'],
'type' => 'oneToMany',
'twoWay' => true,
'key' => 'relationship',
'twoWayKey' => 'twoWayKey'
]);
$strings = $this->client->call(Client::METHOD_POST, $columnsPath . '/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'names',
'size' => 512,
'required' => false,
'array' => true,
]);
$integers = $this->client->call(Client::METHOD_POST, $columnsPath . '/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'numbers',
'required' => false,
'array' => true,
'min' => 1,
'max' => 999,
]);
$this->assertEquals(202, $string['headers']['status-code']);
$this->assertEquals('string', $string['body']['key']);
$this->assertEquals('string', $string['body']['type']);
$this->assertEquals(false, $string['body']['required']);
$this->assertEquals(false, $string['body']['array']);
$this->assertEquals(16, $string['body']['size']);
$this->assertEquals('default', $string['body']['default']);
$this->assertEquals(202, $email['headers']['status-code']);
$this->assertEquals('email', $email['body']['key']);
$this->assertEquals('string', $email['body']['type']);
$this->assertEquals(false, $email['body']['required']);
$this->assertEquals(false, $email['body']['array']);
$this->assertEquals('email', $email['body']['format']);
$this->assertEquals('default@example.com', $email['body']['default']);
$this->assertEquals(202, $enum['headers']['status-code']);
$this->assertEquals('enum', $enum['body']['key']);
$this->assertEquals('string', $enum['body']['type']);
$this->assertEquals(false, $enum['body']['required']);
$this->assertEquals(false, $enum['body']['array']);
$this->assertEquals('enum', $enum['body']['format']);
$this->assertEquals('maybe', $enum['body']['default']);
$this->assertIsArray($enum['body']['elements']);
$this->assertEquals(['yes', 'no', 'maybe'], $enum['body']['elements']);
$this->assertEquals(202, $ip['headers']['status-code']);
$this->assertEquals('ip', $ip['body']['key']);
$this->assertEquals('string', $ip['body']['type']);
$this->assertEquals(false, $ip['body']['required']);
$this->assertEquals(false, $ip['body']['array']);
$this->assertEquals('ip', $ip['body']['format']);
$this->assertEquals('192.0.2.0', $ip['body']['default']);
$this->assertEquals(202, $url['headers']['status-code']);
$this->assertEquals('url', $url['body']['key']);
$this->assertEquals('string', $url['body']['type']);
$this->assertEquals(false, $url['body']['required']);
$this->assertEquals(false, $url['body']['array']);
$this->assertEquals('url', $url['body']['format']);
$this->assertEquals('http://example.com', $url['body']['default']);
$this->assertEquals(202, $integer['headers']['status-code']);
$this->assertEquals('integer', $integer['body']['key']);
$this->assertEquals('integer', $integer['body']['type']);
$this->assertEquals(false, $integer['body']['required']);
$this->assertEquals(false, $integer['body']['array']);
$this->assertEquals(1, $integer['body']['min']);
$this->assertEquals(5, $integer['body']['max']);
$this->assertEquals(3, $integer['body']['default']);
$this->assertEquals(202, $float['headers']['status-code']);
$this->assertEquals('float', $float['body']['key']);
$this->assertEquals('double', $float['body']['type']);
$this->assertEquals(false, $float['body']['required']);
$this->assertEquals(false, $float['body']['array']);
$this->assertEquals(1.5, $float['body']['min']);
$this->assertEquals(5.5, $float['body']['max']);
$this->assertEquals(3.5, $float['body']['default']);
$this->assertEquals(202, $boolean['headers']['status-code']);
$this->assertEquals('boolean', $boolean['body']['key']);
$this->assertEquals('boolean', $boolean['body']['type']);
$this->assertEquals(false, $boolean['body']['required']);
$this->assertEquals(false, $boolean['body']['array']);
$this->assertEquals(true, $boolean['body']['default']);
$this->assertEquals(202, $datetime['headers']['status-code']);
$this->assertEquals('datetime', $datetime['body']['key']);
$this->assertEquals('datetime', $datetime['body']['type']);
$this->assertEquals(false, $datetime['body']['required']);
$this->assertEquals(false, $datetime['body']['array']);
$this->assertEquals(null, $datetime['body']['default']);
$this->assertEquals(202, $relationship['headers']['status-code']);
$this->assertEquals('relationship', $relationship['body']['key']);
$this->assertEquals('relationship', $relationship['body']['type']);
$this->assertEquals(false, $relationship['body']['required']);
$this->assertEquals(false, $relationship['body']['array']);
$this->assertEquals($data['actorsId'], $relationship['body']['relatedTable']);
$this->assertEquals('oneToMany', $relationship['body']['relationType']);
$this->assertEquals(true, $relationship['body']['twoWay']);
$this->assertEquals('twoWayKey', $relationship['body']['twoWayKey']);
$this->assertEquals(202, $strings['headers']['status-code']);
$this->assertEquals('names', $strings['body']['key']);
$this->assertEquals('string', $strings['body']['type']);
$this->assertEquals(false, $strings['body']['required']);
$this->assertEquals(true, $strings['body']['array']);
$this->assertEquals(null, $strings['body']['default']);
$this->assertEquals(202, $integers['headers']['status-code']);
$this->assertEquals('numbers', $integers['body']['key']);
$this->assertEquals('integer', $integers['body']['type']);
$this->assertEquals(false, $integers['body']['required']);
$this->assertEquals(true, $integers['body']['array']);
$this->assertEquals(1, $integers['body']['min']);
$this->assertEquals(999, $integers['body']['max']);
$this->assertEquals(null, $integers['body']['default']);
// Wait for database worker to create attributes
sleep(5);
$stringResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $string['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$emailResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $email['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$enumResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $enum['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$ipResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $ip['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$urlResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $url['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$integerResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $integer['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$floatResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $float['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$booleanResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $boolean['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$datetimeResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $datetime['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$relationshipResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $relationship['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$stringsResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $strings['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$integersResponse = $this->client->call(Client::METHOD_GET, $columnsPath . '/' . $integers['body']['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $stringResponse['headers']['status-code']);
$this->assertEquals($string['body']['key'], $stringResponse['body']['key']);
$this->assertEquals($string['body']['type'], $stringResponse['body']['type']);
$this->assertEquals('available', $stringResponse['body']['status']);
$this->assertEquals($string['body']['required'], $stringResponse['body']['required']);
$this->assertEquals($string['body']['array'], $stringResponse['body']['array']);
$this->assertEquals(16, $stringResponse['body']['size']);
$this->assertEquals($string['body']['default'], $stringResponse['body']['default']);
$this->assertEquals(200, $emailResponse['headers']['status-code']);
$this->assertEquals($email['body']['key'], $emailResponse['body']['key']);
$this->assertEquals($email['body']['type'], $emailResponse['body']['type']);
$this->assertEquals('available', $emailResponse['body']['status']);
$this->assertEquals($email['body']['required'], $emailResponse['body']['required']);
$this->assertEquals($email['body']['array'], $emailResponse['body']['array']);
$this->assertEquals($email['body']['format'], $emailResponse['body']['format']);
$this->assertEquals($email['body']['default'], $emailResponse['body']['default']);
$this->assertEquals(200, $enumResponse['headers']['status-code']);
$this->assertEquals($enum['body']['key'], $enumResponse['body']['key']);
$this->assertEquals($enum['body']['type'], $enumResponse['body']['type']);
$this->assertEquals('available', $enumResponse['body']['status']);
$this->assertEquals($enum['body']['required'], $enumResponse['body']['required']);
$this->assertEquals($enum['body']['array'], $enumResponse['body']['array']);
$this->assertEquals($enum['body']['format'], $enumResponse['body']['format']);
$this->assertEquals($enum['body']['default'], $enumResponse['body']['default']);
$this->assertEquals($enum['body']['elements'], $enumResponse['body']['elements']);
$this->assertEquals(200, $ipResponse['headers']['status-code']);
$this->assertEquals($ip['body']['key'], $ipResponse['body']['key']);
$this->assertEquals($ip['body']['type'], $ipResponse['body']['type']);
$this->assertEquals('available', $ipResponse['body']['status']);
$this->assertEquals($ip['body']['required'], $ipResponse['body']['required']);
$this->assertEquals($ip['body']['array'], $ipResponse['body']['array']);
$this->assertEquals($ip['body']['format'], $ipResponse['body']['format']);
$this->assertEquals($ip['body']['default'], $ipResponse['body']['default']);
$this->assertEquals(200, $urlResponse['headers']['status-code']);
$this->assertEquals($url['body']['key'], $urlResponse['body']['key']);
$this->assertEquals($url['body']['type'], $urlResponse['body']['type']);
$this->assertEquals('available', $urlResponse['body']['status']);
$this->assertEquals($url['body']['required'], $urlResponse['body']['required']);
$this->assertEquals($url['body']['array'], $urlResponse['body']['array']);
$this->assertEquals($url['body']['format'], $urlResponse['body']['format']);
$this->assertEquals($url['body']['default'], $urlResponse['body']['default']);
$this->assertEquals(200, $integerResponse['headers']['status-code']);
$this->assertEquals($integer['body']['key'], $integerResponse['body']['key']);
$this->assertEquals($integer['body']['type'], $integerResponse['body']['type']);
$this->assertEquals('available', $integerResponse['body']['status']);
$this->assertEquals($integer['body']['required'], $integerResponse['body']['required']);
$this->assertEquals($integer['body']['array'], $integerResponse['body']['array']);
$this->assertEquals($integer['body']['min'], $integerResponse['body']['min']);
$this->assertEquals($integer['body']['max'], $integerResponse['body']['max']);
$this->assertEquals($integer['body']['default'], $integerResponse['body']['default']);
$this->assertEquals(200, $floatResponse['headers']['status-code']);
$this->assertEquals($float['body']['key'], $floatResponse['body']['key']);
$this->assertEquals($float['body']['type'], $floatResponse['body']['type']);
$this->assertEquals('available', $floatResponse['body']['status']);
$this->assertEquals($float['body']['required'], $floatResponse['body']['required']);
$this->assertEquals($float['body']['array'], $floatResponse['body']['array']);
$this->assertEquals($float['body']['min'], $floatResponse['body']['min']);
$this->assertEquals($float['body']['max'], $floatResponse['body']['max']);
$this->assertEquals($float['body']['default'], $floatResponse['body']['default']);
$this->assertEquals(200, $booleanResponse['headers']['status-code']);
$this->assertEquals($boolean['body']['key'], $booleanResponse['body']['key']);
$this->assertEquals($boolean['body']['type'], $booleanResponse['body']['type']);
$this->assertEquals('available', $booleanResponse['body']['status']);
$this->assertEquals($boolean['body']['required'], $booleanResponse['body']['required']);
$this->assertEquals($boolean['body']['array'], $booleanResponse['body']['array']);
$this->assertEquals($boolean['body']['default'], $booleanResponse['body']['default']);
$this->assertEquals(200, $datetimeResponse['headers']['status-code']);
$this->assertEquals($datetime['body']['key'], $datetimeResponse['body']['key']);
$this->assertEquals($datetime['body']['type'], $datetimeResponse['body']['type']);
$this->assertEquals('available', $datetimeResponse['body']['status']);
$this->assertEquals($datetime['body']['required'], $datetimeResponse['body']['required']);
$this->assertEquals($datetime['body']['array'], $datetimeResponse['body']['array']);
$this->assertEquals($datetime['body']['default'], $datetimeResponse['body']['default']);
$this->assertEquals(200, $relationshipResponse['headers']['status-code']);
$this->assertEquals($relationship['body']['key'], $relationshipResponse['body']['key']);
$this->assertEquals($relationship['body']['type'], $relationshipResponse['body']['type']);
$this->assertEquals('available', $relationshipResponse['body']['status']);
$this->assertEquals($relationship['body']['required'], $relationshipResponse['body']['required']);
$this->assertEquals($relationship['body']['array'], $relationshipResponse['body']['array']);
$this->assertEquals($relationship['body']['relatedTable'], $relationshipResponse['body']['relatedTable']);
$this->assertEquals($relationship['body']['relationType'], $relationshipResponse['body']['relationType']);
$this->assertEquals($relationship['body']['twoWay'], $relationshipResponse['body']['twoWay']);
$this->assertEquals($relationship['body']['twoWayKey'], $relationshipResponse['body']['twoWayKey']);
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $columns['headers']['status-code']);
$this->assertEquals(12, $columns['body']['total']);
$columns = $columns['body']['columns'];
$this->assertIsArray($columns);
$this->assertCount(12, $columns);
$this->assertEquals($stringResponse['body']['key'], $columns[0]['key']);
$this->assertEquals($stringResponse['body']['type'], $columns[0]['type']);
$this->assertEquals($stringResponse['body']['status'], $columns[0]['status']);
$this->assertEquals($stringResponse['body']['required'], $columns[0]['required']);
$this->assertEquals($stringResponse['body']['array'], $columns[0]['array']);
$this->assertEquals($stringResponse['body']['size'], $columns[0]['size']);
$this->assertEquals($stringResponse['body']['default'], $columns[0]['default']);
$this->assertEquals($emailResponse['body']['key'], $columns[1]['key']);
$this->assertEquals($emailResponse['body']['type'], $columns[1]['type']);
$this->assertEquals($emailResponse['body']['status'], $columns[1]['status']);
$this->assertEquals($emailResponse['body']['required'], $columns[1]['required']);
$this->assertEquals($emailResponse['body']['array'], $columns[1]['array']);
$this->assertEquals($emailResponse['body']['default'], $columns[1]['default']);
$this->assertEquals($emailResponse['body']['format'], $columns[1]['format']);
$this->assertEquals($enumResponse['body']['key'], $columns[2]['key']);
$this->assertEquals($enumResponse['body']['type'], $columns[2]['type']);
$this->assertEquals($enumResponse['body']['status'], $columns[2]['status']);
$this->assertEquals($enumResponse['body']['required'], $columns[2]['required']);
$this->assertEquals($enumResponse['body']['array'], $columns[2]['array']);
$this->assertEquals($enumResponse['body']['default'], $columns[2]['default']);
$this->assertEquals($enumResponse['body']['format'], $columns[2]['format']);
$this->assertEquals($enumResponse['body']['elements'], $columns[2]['elements']);
$this->assertEquals($ipResponse['body']['key'], $columns[3]['key']);
$this->assertEquals($ipResponse['body']['type'], $columns[3]['type']);
$this->assertEquals($ipResponse['body']['status'], $columns[3]['status']);
$this->assertEquals($ipResponse['body']['required'], $columns[3]['required']);
$this->assertEquals($ipResponse['body']['array'], $columns[3]['array']);
$this->assertEquals($ipResponse['body']['default'], $columns[3]['default']);
$this->assertEquals($ipResponse['body']['format'], $columns[3]['format']);
$this->assertEquals($urlResponse['body']['key'], $columns[4]['key']);
$this->assertEquals($urlResponse['body']['type'], $columns[4]['type']);
$this->assertEquals($urlResponse['body']['status'], $columns[4]['status']);
$this->assertEquals($urlResponse['body']['required'], $columns[4]['required']);
$this->assertEquals($urlResponse['body']['array'], $columns[4]['array']);
$this->assertEquals($urlResponse['body']['default'], $columns[4]['default']);
$this->assertEquals($urlResponse['body']['format'], $columns[4]['format']);
$this->assertEquals($integerResponse['body']['key'], $columns[5]['key']);
$this->assertEquals($integerResponse['body']['type'], $columns[5]['type']);
$this->assertEquals($integerResponse['body']['status'], $columns[5]['status']);
$this->assertEquals($integerResponse['body']['required'], $columns[5]['required']);
$this->assertEquals($integerResponse['body']['array'], $columns[5]['array']);
$this->assertEquals($integerResponse['body']['default'], $columns[5]['default']);
$this->assertEquals($integerResponse['body']['min'], $columns[5]['min']);
$this->assertEquals($integerResponse['body']['max'], $columns[5]['max']);
$this->assertEquals($floatResponse['body']['key'], $columns[6]['key']);
$this->assertEquals($floatResponse['body']['type'], $columns[6]['type']);
$this->assertEquals($floatResponse['body']['status'], $columns[6]['status']);
$this->assertEquals($floatResponse['body']['required'], $columns[6]['required']);
$this->assertEquals($floatResponse['body']['array'], $columns[6]['array']);
$this->assertEquals($floatResponse['body']['default'], $columns[6]['default']);
$this->assertEquals($floatResponse['body']['min'], $columns[6]['min']);
$this->assertEquals($floatResponse['body']['max'], $columns[6]['max']);
$this->assertEquals($booleanResponse['body']['key'], $columns[7]['key']);
$this->assertEquals($booleanResponse['body']['type'], $columns[7]['type']);
$this->assertEquals($booleanResponse['body']['status'], $columns[7]['status']);
$this->assertEquals($booleanResponse['body']['required'], $columns[7]['required']);
$this->assertEquals($booleanResponse['body']['array'], $columns[7]['array']);
$this->assertEquals($booleanResponse['body']['default'], $columns[7]['default']);
$this->assertEquals($datetimeResponse['body']['key'], $columns[8]['key']);
$this->assertEquals($datetimeResponse['body']['type'], $columns[8]['type']);
$this->assertEquals($datetimeResponse['body']['status'], $columns[8]['status']);
$this->assertEquals($datetimeResponse['body']['required'], $columns[8]['required']);
$this->assertEquals($datetimeResponse['body']['array'], $columns[8]['array']);
$this->assertEquals($datetimeResponse['body']['default'], $columns[8]['default']);
$this->assertEquals($relationshipResponse['body']['key'], $columns[9]['key']);
$this->assertEquals($relationshipResponse['body']['type'], $columns[9]['type']);
$this->assertEquals($relationshipResponse['body']['status'], $columns[9]['status']);
$this->assertEquals($relationshipResponse['body']['required'], $columns[9]['required']);
$this->assertEquals($relationshipResponse['body']['array'], $columns[9]['array']);
$this->assertEquals($relationshipResponse['body']['relatedTable'], $columns[9]['relatedTable']);
$this->assertEquals($relationshipResponse['body']['relationType'], $columns[9]['relationType']);
$this->assertEquals($relationshipResponse['body']['twoWay'], $columns[9]['twoWay']);
$this->assertEquals($relationshipResponse['body']['twoWayKey'], $columns[9]['twoWayKey']);
$this->assertEquals($stringsResponse['body']['key'], $columns[10]['key']);
$this->assertEquals($stringsResponse['body']['type'], $columns[10]['type']);
$this->assertEquals($stringsResponse['body']['status'], $columns[10]['status']);
$this->assertEquals($stringsResponse['body']['required'], $columns[10]['required']);
$this->assertEquals($stringsResponse['body']['array'], $columns[10]['array']);
$this->assertEquals($stringsResponse['body']['default'], $columns[10]['default']);
$this->assertEquals($integersResponse['body']['key'], $columns[11]['key']);
$this->assertEquals($integersResponse['body']['type'], $columns[11]['type']);
$this->assertEquals($integersResponse['body']['status'], $columns[11]['status']);
$this->assertEquals($integersResponse['body']['required'], $columns[11]['required']);
$this->assertEquals($integersResponse['body']['array'], $columns[11]['array']);
$this->assertEquals($integersResponse['body']['default'], $columns[11]['default']);
$this->assertEquals($integersResponse['body']['min'], $columns[11]['min']);
$this->assertEquals($integersResponse['body']['max'], $columns[11]['max']);
$table = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $table['headers']['status-code']);
$columns = $table['body']['columns'];
$this->assertIsArray($columns);
$this->assertCount(12, $columns);
$this->assertEquals($stringResponse['body']['key'], $columns[0]['key']);
$this->assertEquals($stringResponse['body']['type'], $columns[0]['type']);
$this->assertEquals($stringResponse['body']['status'], $columns[0]['status']);
$this->assertEquals($stringResponse['body']['required'], $columns[0]['required']);
$this->assertEquals($stringResponse['body']['array'], $columns[0]['array']);
$this->assertEquals($stringResponse['body']['size'], $columns[0]['size']);
$this->assertEquals($stringResponse['body']['default'], $columns[0]['default']);
$this->assertEquals($emailResponse['body']['key'], $columns[1]['key']);
$this->assertEquals($emailResponse['body']['type'], $columns[1]['type']);
$this->assertEquals($emailResponse['body']['status'], $columns[1]['status']);
$this->assertEquals($emailResponse['body']['required'], $columns[1]['required']);
$this->assertEquals($emailResponse['body']['array'], $columns[1]['array']);
$this->assertEquals($emailResponse['body']['default'], $columns[1]['default']);
$this->assertEquals($emailResponse['body']['format'], $columns[1]['format']);
$this->assertEquals($enumResponse['body']['key'], $columns[2]['key']);
$this->assertEquals($enumResponse['body']['type'], $columns[2]['type']);
$this->assertEquals($enumResponse['body']['status'], $columns[2]['status']);
$this->assertEquals($enumResponse['body']['required'], $columns[2]['required']);
$this->assertEquals($enumResponse['body']['array'], $columns[2]['array']);
$this->assertEquals($enumResponse['body']['default'], $columns[2]['default']);
$this->assertEquals($enumResponse['body']['format'], $columns[2]['format']);
$this->assertEquals($enumResponse['body']['elements'], $columns[2]['elements']);
$this->assertEquals($ipResponse['body']['key'], $columns[3]['key']);
$this->assertEquals($ipResponse['body']['type'], $columns[3]['type']);
$this->assertEquals($ipResponse['body']['status'], $columns[3]['status']);
$this->assertEquals($ipResponse['body']['required'], $columns[3]['required']);
$this->assertEquals($ipResponse['body']['array'], $columns[3]['array']);
$this->assertEquals($ipResponse['body']['default'], $columns[3]['default']);
$this->assertEquals($ipResponse['body']['format'], $columns[3]['format']);
$this->assertEquals($urlResponse['body']['key'], $columns[4]['key']);
$this->assertEquals($urlResponse['body']['type'], $columns[4]['type']);
$this->assertEquals($urlResponse['body']['status'], $columns[4]['status']);
$this->assertEquals($urlResponse['body']['required'], $columns[4]['required']);
$this->assertEquals($urlResponse['body']['array'], $columns[4]['array']);
$this->assertEquals($urlResponse['body']['default'], $columns[4]['default']);
$this->assertEquals($urlResponse['body']['format'], $columns[4]['format']);
$this->assertEquals($integerResponse['body']['key'], $columns[5]['key']);
$this->assertEquals($integerResponse['body']['type'], $columns[5]['type']);
$this->assertEquals($integerResponse['body']['status'], $columns[5]['status']);
$this->assertEquals($integerResponse['body']['required'], $columns[5]['required']);
$this->assertEquals($integerResponse['body']['array'], $columns[5]['array']);
$this->assertEquals($integerResponse['body']['default'], $columns[5]['default']);
$this->assertEquals($integerResponse['body']['min'], $columns[5]['min']);
$this->assertEquals($integerResponse['body']['max'], $columns[5]['max']);
$this->assertEquals($floatResponse['body']['key'], $columns[6]['key']);
$this->assertEquals($floatResponse['body']['type'], $columns[6]['type']);
$this->assertEquals($floatResponse['body']['status'], $columns[6]['status']);
$this->assertEquals($floatResponse['body']['required'], $columns[6]['required']);
$this->assertEquals($floatResponse['body']['array'], $columns[6]['array']);
$this->assertEquals($floatResponse['body']['default'], $columns[6]['default']);
$this->assertEquals($floatResponse['body']['min'], $columns[6]['min']);
$this->assertEquals($floatResponse['body']['max'], $columns[6]['max']);
$this->assertEquals($booleanResponse['body']['key'], $columns[7]['key']);
$this->assertEquals($booleanResponse['body']['type'], $columns[7]['type']);
$this->assertEquals($booleanResponse['body']['status'], $columns[7]['status']);
$this->assertEquals($booleanResponse['body']['required'], $columns[7]['required']);
$this->assertEquals($booleanResponse['body']['array'], $columns[7]['array']);
$this->assertEquals($booleanResponse['body']['default'], $columns[7]['default']);
$this->assertEquals($datetimeResponse['body']['key'], $columns[8]['key']);
$this->assertEquals($datetimeResponse['body']['type'], $columns[8]['type']);
$this->assertEquals($datetimeResponse['body']['status'], $columns[8]['status']);
$this->assertEquals($datetimeResponse['body']['required'], $columns[8]['required']);
$this->assertEquals($datetimeResponse['body']['array'], $columns[8]['array']);
$this->assertEquals($datetimeResponse['body']['default'], $columns[8]['default']);
$this->assertEquals($relationshipResponse['body']['key'], $columns[9]['key']);
$this->assertEquals($relationshipResponse['body']['type'], $columns[9]['type']);
$this->assertEquals($relationshipResponse['body']['status'], $columns[9]['status']);
$this->assertEquals($relationshipResponse['body']['required'], $columns[9]['required']);
$this->assertEquals($relationshipResponse['body']['array'], $columns[9]['array']);
$this->assertEquals($relationshipResponse['body']['relatedTable'], $columns[9]['relatedTable']);
$this->assertEquals($relationshipResponse['body']['relationType'], $columns[9]['relationType']);
$this->assertEquals($relationshipResponse['body']['twoWay'], $columns[9]['twoWay']);
$this->assertEquals($relationshipResponse['body']['twoWayKey'], $columns[9]['twoWayKey']);
$this->assertEquals($stringsResponse['body']['key'], $columns[10]['key']);
$this->assertEquals($stringsResponse['body']['type'], $columns[10]['type']);
$this->assertEquals($stringsResponse['body']['status'], $columns[10]['status']);
$this->assertEquals($stringsResponse['body']['required'], $columns[10]['required']);
$this->assertEquals($stringsResponse['body']['array'], $columns[10]['array']);
$this->assertEquals($stringsResponse['body']['default'], $columns[10]['default']);
$this->assertEquals($integersResponse['body']['key'], $columns[11]['key']);
$this->assertEquals($integersResponse['body']['type'], $columns[11]['type']);
$this->assertEquals($integersResponse['body']['status'], $columns[11]['status']);
$this->assertEquals($integersResponse['body']['required'], $columns[11]['required']);
$this->assertEquals($integersResponse['body']['array'], $columns[11]['array']);
$this->assertEquals($integersResponse['body']['default'], $columns[11]['default']);
$this->assertEquals($integersResponse['body']['min'], $columns[11]['min']);
$this->assertEquals($integersResponse['body']['max'], $columns[11]['max']);
/**
* Test for FAILURE
*/
$badEnum = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'enum',
'elements' => ['yes', 'no', ''],
'required' => false,
'default' => 'maybe',
]);
$this->assertEquals(400, $badEnum['headers']['status-code']);
$this->assertEquals('Invalid `elements` param: Value must a valid array no longer than 100 items and Value must be a valid string and at least 1 chars and no longer than 255 chars', $badEnum['body']['message']);
return $data;
}
/**
* @depends testCreateColumns
*/
public function testCreateIndexes(array $data): array
{
$databaseId = $data['databaseId'];
$titleIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'titleIndex',
'type' => 'fulltext',
'columns' => ['title'],
]);
$this->assertEquals(202, $titleIndex['headers']['status-code']);
$this->assertEquals('titleIndex', $titleIndex['body']['key']);
$this->assertEquals('fulltext', $titleIndex['body']['type']);
$this->assertCount(1, $titleIndex['body']['columns']);
$this->assertEquals('title', $titleIndex['body']['columns'][0]);
$releaseYearIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'releaseYear',
'type' => 'key',
'columns' => ['releaseYear'],
]);
$this->assertEquals(202, $releaseYearIndex['headers']['status-code']);
$this->assertEquals('releaseYear', $releaseYearIndex['body']['key']);
$this->assertEquals('key', $releaseYearIndex['body']['type']);
$this->assertCount(1, $releaseYearIndex['body']['columns']);
$this->assertEquals('releaseYear', $releaseYearIndex['body']['columns'][0]);
$releaseWithDate1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'releaseYearDated',
'type' => 'key',
'columns' => ['releaseYear', '$createdAt', '$updatedAt'],
]);
$this->assertEquals(202, $releaseWithDate1['headers']['status-code']);
$this->assertEquals('releaseYearDated', $releaseWithDate1['body']['key']);
$this->assertEquals('key', $releaseWithDate1['body']['type']);
$this->assertCount(3, $releaseWithDate1['body']['columns']);
$this->assertEquals('releaseYear', $releaseWithDate1['body']['columns'][0]);
$this->assertEquals('$createdAt', $releaseWithDate1['body']['columns'][1]);
$this->assertEquals('$updatedAt', $releaseWithDate1['body']['columns'][2]);
$releaseWithDate2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'birthDay',
'type' => 'key',
'columns' => ['birthDay'],
]);
$this->assertEquals(202, $releaseWithDate2['headers']['status-code']);
$this->assertEquals('birthDay', $releaseWithDate2['body']['key']);
$this->assertEquals('key', $releaseWithDate2['body']['type']);
$this->assertCount(1, $releaseWithDate2['body']['columns']);
$this->assertEquals('birthDay', $releaseWithDate2['body']['columns'][0]);
// Test for failure
$fulltextReleaseYear = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'releaseYearDated',
'type' => 'fulltext',
'columns' => ['releaseYear'],
]);
$this->assertEquals(400, $fulltextReleaseYear['headers']['status-code']);
$this->assertEquals($fulltextReleaseYear['body']['message'], 'Attribute "releaseYear" cannot be part of a fulltext index, must be of type string');
$noAttributes = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'none',
'type' => 'key',
'columns' => [],
]);
$this->assertEquals(400, $noAttributes['headers']['status-code']);
$this->assertEquals($noAttributes['body']['message'], 'No attributes provided for index');
$duplicates = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'duplicate',
'type' => 'fulltext',
'columns' => ['releaseYear', 'releaseYear'],
]);
$this->assertEquals(400, $duplicates['headers']['status-code']);
$this->assertEquals($duplicates['body']['message'], 'Duplicate attributes provided');
$tooLong = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'tooLong',
'type' => 'key',
'columns' => ['description', 'tagline'],
]);
$this->assertEquals(400, $tooLong['headers']['status-code']);
$this->assertStringContainsString('Index length is longer than the maximum', $tooLong['body']['message']);
$fulltextArray = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'ft',
'type' => 'fulltext',
'columns' => ['actors'],
]);
$this->assertEquals(400, $fulltextArray['headers']['status-code']);
$this->assertEquals('Creating indexes on array attributes is not currently supported.', $fulltextArray['body']['message']);
$actorsArray = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-actors',
'type' => 'key',
'columns' => ['actors'],
]);
$this->assertEquals(400, $actorsArray['headers']['status-code']);
$this->assertEquals('Creating indexes on array attributes is not currently supported.', $actorsArray['body']['message']);
$twoLevelsArray = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-ip-actors',
'type' => 'key',
'columns' => ['releaseYear', 'actors'], // 2 levels
'orders' => ['DESC', 'DESC'],
]);
$this->assertEquals(400, $twoLevelsArray['headers']['status-code']);
$this->assertEquals('Creating indexes on array attributes is not currently supported.', $twoLevelsArray['body']['message']);
$unknown = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'index-unknown',
'type' => 'key',
'columns' => ['Unknown'],
]);
$this->assertEquals(400, $unknown['headers']['status-code']);
$this->assertEquals('The column \'Unknown\' required for the index could not be found. Please confirm all your columns are in the available state.', $unknown['body']['message']);
$index1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'integers-order',
'type' => 'key',
'columns' => ['integers'], // array column
'orders' => ['DESC'], // Check order is removed in API
]);
$this->assertEquals(400, $index1['headers']['status-code']);
$this->assertEquals('Creating indexes on array attributes is not currently supported.', $index1['body']['message']);
$index2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'key' => 'integers-size',
'type' => 'key',
'columns' => ['integers'], // array column
]);
$this->assertEquals(400, $index2['headers']['status-code']);
$this->assertEquals('Creating indexes on array attributes is not currently supported.', $index2['body']['message']);
/**
* Create Indexes by worker
*/
sleep(2);
$movies = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertIsArray($movies['body']['indexes']);
$this->assertCount(4, $movies['body']['indexes']);
$this->assertEquals($titleIndex['body']['key'], $movies['body']['indexes'][0]['key']);
$this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']);
$this->assertEquals($releaseWithDate1['body']['key'], $movies['body']['indexes'][2]['key']);
$this->assertEquals($releaseWithDate2['body']['key'], $movies['body']['indexes'][3]['key']);
foreach ($movies['body']['indexes'] as $index) {
$this->assertEquals('available', $index['status']);
}
return $data;
}
/**
* @depends testCreateColumns
*/
public function testGetIndexByKeyWithLengths(array $data): void
{
$databaseId = $data['databaseId'];
$tableId = $data['moviesId'];
// Test case for valid lengths
$create = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'lengthTestIndex',
'type' => 'key',
'columns' => ['title','description'],
'lengths' => [128,200]
]);
$this->assertEquals(202, $create['headers']['status-code']);
// Fetch index and check correct lengths
$index = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes/lengthTestIndex", [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $index['headers']['status-code']);
$this->assertEquals('lengthTestIndex', $index['body']['key']);
$this->assertEquals([128, 200], $index['body']['lengths']);
// Test case for count of lengths greater than attributes (should throw 400)
$create = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'lengthCountExceededIndex',
'type' => 'key',
'columns' => ['title'],
'lengths' => [128, 128]
]);
$this->assertEquals(400, $create['headers']['status-code']);
// Test case for lengths exceeding total of 768
$create = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'lengthTooLargeIndex',
'type' => 'key',
'columns' => ['title','description','tagline','actors'],
'lengths' => [256,256,256,20]
]);
$this->assertEquals(400, $create['headers']['status-code']);
// Test case for negative length values
$create = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'negativeLengthIndex',
'type' => 'key',
'columns' => ['title'],
'lengths' => [-1]
]);
$this->assertEquals(400, $create['headers']['status-code']);
}
/**
* @depends testCreateIndexes
*/
public function testListIndexes(array $data): void
{
$databaseId = $data['databaseId'];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => [
Query::equal('type', ['key'])->toString(),
Query::limit(2)->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, \count($response['body']['indexes']));
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), [
'queries' => [
Query::select(['key'])->toString(),
],
]);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testCreateIndexes
*/
public function testCreateRow(array $data): array
{
$databaseId = $data['databaseId'];
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
'releaseYear' => 1944,
'birthDay' => '1975-06-12 14:12:55+02:00',
'actors' => [
'Chris Evans',
'Samuel Jackson',
]
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Spider-Man: Far From Home',
'releaseYear' => 2019,
'birthDay' => null,
'actors' => [
'Tom Holland',
'Zendaya Maree Stoermer',
'Samuel Jackson',
],
'integers' => [50,60]
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Spider-Man: Homecoming',
'releaseYear' => 2017,
'birthDay' => '1975-06-12 14:12:55 America/New_York',
'duration' => 65,
'actors' => [
'Tom Holland',
'Zendaya Maree Stoermer',
],
'integers' => [50]
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$row4 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'releaseYear' => 2020, // Missing title, expect an 400 error
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$this->assertEquals($data['moviesId'], $row1['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row1['body']);
$this->assertEquals($databaseId, $row1['body']['$databaseId']);
$this->assertEquals($row1['body']['title'], 'Captain America');
$this->assertEquals($row1['body']['releaseYear'], 1944);
$this->assertIsArray($row1['body']['$permissions']);
$this->assertCount(3, $row1['body']['$permissions']);
$this->assertCount(2, $row1['body']['actors']);
$this->assertEquals($row1['body']['actors'][0], 'Chris Evans');
$this->assertEquals($row1['body']['actors'][1], 'Samuel Jackson');
$this->assertEquals($row1['body']['birthDay'], '1975-06-12T12:12:55.000+00:00');
$this->assertTrue(array_key_exists('$sequence', $row1['body']));
$this->assertIsInt($row1['body']['$sequence']);
$this->assertEquals(201, $row2['headers']['status-code']);
$this->assertEquals($data['moviesId'], $row2['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row2['body']);
$this->assertEquals($databaseId, $row2['body']['$databaseId']);
$this->assertEquals($row2['body']['title'], 'Spider-Man: Far From Home');
$this->assertEquals($row2['body']['releaseYear'], 2019);
$this->assertEquals($row2['body']['duration'], null);
$this->assertIsArray($row2['body']['$permissions']);
$this->assertCount(3, $row2['body']['$permissions']);
$this->assertCount(3, $row2['body']['actors']);
$this->assertEquals($row2['body']['actors'][0], 'Tom Holland');
$this->assertEquals($row2['body']['actors'][1], 'Zendaya Maree Stoermer');
$this->assertEquals($row2['body']['actors'][2], 'Samuel Jackson');
$this->assertEquals($row2['body']['birthDay'], null);
$this->assertEquals($row2['body']['integers'][0], 50);
$this->assertEquals($row2['body']['integers'][1], 60);
$this->assertEquals(201, $row3['headers']['status-code']);
$this->assertEquals($data['moviesId'], $row3['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row3['body']);
$this->assertEquals($databaseId, $row3['body']['$databaseId']);
$this->assertEquals($row3['body']['title'], 'Spider-Man: Homecoming');
$this->assertEquals($row3['body']['releaseYear'], 2017);
$this->assertEquals($row3['body']['duration'], 65);
$this->assertIsArray($row3['body']['$permissions']);
$this->assertCount(3, $row3['body']['$permissions']);
$this->assertCount(2, $row3['body']['actors']);
$this->assertEquals($row3['body']['actors'][0], 'Tom Holland');
$this->assertEquals($row3['body']['actors'][1], 'Zendaya Maree Stoermer');
$this->assertEquals($row3['body']['birthDay'], '1975-06-12T18:12:55.000+00:00'); // UTC for NY
$this->assertEquals(400, $row4['headers']['status-code']);
return $data;
}
/**
* @depends testCreateIndexes
*/
public function testUpsertRow(array $data): void
{
$databaseId = $data['databaseId'];
$rowId = ID::unique();
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2000
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertCount(3, $row['body']['$permissions']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
/**
* Resubmit same document, nothing to update
*/
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2000,
'integers' => [],
'birthDay' => null,
'duration' => null,
'starringActors' => [],
'actors' => [],
'tagline' => '',
'description' => '',
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$this->assertCount(3, $row['body']['$permissions']);
/**
* Do not allow array list
*/
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
[
'title' => 'Thor: Ragnarok 1',
],
[
'title' => 'Thor: Ragnarok 2',
]
],
'permissions' => [
Permission::read(Role::users()),
],
]);
$this->assertEquals(400, $row['headers']['status-code']);
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Love and Thunder',
'releaseYear' => 2000
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Love and Thunder', $row['body']['title']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals('Thor: Love and Thunder', $row['body']['title']);
// removing permission to read and delete
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Love and Thunder',
'releaseYear' => 2000
],
'permissions' => [
Permission::update(Role::users())
],
]);
// shouldn't be able to read as no read permission
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
switch ($this->getSide()) {
case 'client':
$this->assertEquals(404, $row['headers']['status-code']);
break;
case 'server':
$this->assertEquals(200, $row['headers']['status-code']);
break;
}
// shouldn't be able to delete as no delete permission
$row = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// simulating for the client
// the row should not be allowed to be deleted as needed downward
if ($this->getSide() === 'client') {
$this->assertEquals(401, $row['headers']['status-code']);
}
// giving the delete permission
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Love and Thunder',
'releaseYear' => 2000
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users())
],
]);
$row = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $row['headers']['status-code']);
// relationship behaviour
$person = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'person-upsert',
'name' => 'person',
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
Permission::create(Role::users()),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $person['headers']['status-code']);
$library = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'library-upsert',
'name' => 'library',
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::create(Role::users()),
Permission::delete(Role::users()),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $library['headers']['status-code']);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'fullName',
'size' => 255,
'required' => false,
]);
sleep(1); // Wait for worker
$relation = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => 'library-upsert',
'type' => Database::RELATION_ONE_TO_ONE,
'key' => 'library',
'twoWay' => true,
'onDelete' => Database::RELATION_MUTATE_CASCADE,
]);
sleep(1); // Wait for worker
$libraryName = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'libraryName',
'size' => 255,
'required' => true,
]);
sleep(1); // Wait for worker
$this->assertEquals(202, $libraryName['headers']['status-code']);
// upserting values
$rowId = ID::unique();
$person1 = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.$rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'library' => [
'$id' => 'library1',
'$permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
'libraryName' => 'Library 1',
],
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
]
]);
$this->assertEquals('Library 1', $person1['body']['library']['libraryName']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', 'library.*'])->toString(),
Query::equal('library', ['library1'])->toString(),
],
]);
$this->assertEquals(1, $rows['body']['total']);
$this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']);
$person1 = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.$rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'library' => [
'$id' => 'library1',
'$permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
'libraryName' => 'Library 2',
],
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
]
]);
// data should get updated
$this->assertEquals('Library 2', $person1['body']['library']['libraryName']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', 'library.*'])->toString(),
Query::equal('library', ['library1'])->toString(),
],
]);
$this->assertEquals(1, $rows['body']['total']);
$this->assertEquals('Library 2', $rows['body']['rows'][0]['library']['libraryName']);
// data should get added
$person1 = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/'.ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'library' => [
'$id' => 'library2',
'$permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
'libraryName' => 'Library 2',
],
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
]
]);
$this->assertEquals('Library 2', $person1['body']['library']['libraryName']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', 'library.*'])->toString()
],
]);
$this->assertEquals(2, $rows['body']['total']);
// test without passing permissions
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2000
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $row['headers']['status-code']);
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
if ($this->getSide() === 'client') {
// Skipped on server side: Creating a row with no permissions results in an empty permissions array, whereas on client side it assigns permissions to the current user
// test without passing permissions
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2000
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$this->assertCount(3, $row['body']['$permissions']);
$permissionsCreated = $row['body']['$permissions'];
// checking the default created permission
$defaultPermission = [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id']))
];
// ignoring the order of the permission and checking the permissions
$this->assertEqualsCanonicalizing($defaultPermission, $permissionsCreated);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()));
$this->assertEquals(200, $row['headers']['status-code']);
// updating the created doc
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2002
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$this->assertEquals(2002, $row['body']['releaseYear']);
$this->assertCount(3, $row['body']['$permissions']);
$this->assertEquals($permissionsCreated, $row['body']['$permissions']);
// removing the delete permission
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2002
],
'permissions' => [
Permission::update(Role::user($this->getUser()['$id']))
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$this->assertEquals(2002, $row['body']['releaseYear']);
$this->assertCount(1, $row['body']['$permissions']);
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()));
$this->assertEquals(401, $deleteResponse['headers']['status-code']);
// giving the delete permission
$row = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2002
],
'permissions' => [
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id']))
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals('Thor: Ragnarok', $row['body']['title']);
$this->assertEquals(2002, $row['body']['releaseYear']);
$this->assertCount(2, $row['body']['$permissions']);
$deleteResponse = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()));
$this->assertEquals(204, $deleteResponse['headers']['status-code']);
// upsert for the related row without passing permissions
// data should get added
$newPersonId = ID::unique();
$personNoPerm = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/' . $newPersonId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'library' => [
'$id' => 'library3',
'libraryName' => 'Library 3',
],
],
]);
$this->assertEquals('Library 3', $personNoPerm['body']['library']['libraryName']);
$this->assertCount(3, $personNoPerm['body']['library']['$permissions']);
$this->assertCount(3, $personNoPerm['body']['$permissions']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', 'library.*'])->toString()
],
]);
$this->assertGreaterThanOrEqual(1, $rows['body']['total']);
$rowsDetails = $rows['body']['rows'];
foreach ($rowsDetails as $doc) {
$this->assertCount(3, $doc['$permissions']);
}
$found = false;
foreach ($rows['body']['rows'] as $doc) {
if (isset($doc['library']['libraryName']) && $doc['library']['libraryName'] === 'Library 3') {
$found = true;
break;
}
}
$this->assertTrue($found, 'Library 3 should be present in the upserted rows.');
// Fetch the related library and assert on its permissions (should be default/inherited)
$library3 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $library['body']['$id'] . '/rows/library3', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $library3['headers']['status-code']);
$this->assertEquals('Library 3', $library3['body']['libraryName']);
$this->assertArrayHasKey('$permissions', $library3['body']);
$this->assertCount(3, $library3['body']['$permissions']);
$this->assertNotEmpty($library3['body']['$permissions']);
}
}
/**
* @depends testCreateRow
*/
public function testListRows(array $data): array
{
$databaseId = $data['databaseId'];
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']);
$this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $rows['body']['rows'][2]['releaseYear']);
$this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][0]));
$this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][1]));
$this->assertFalse(array_key_exists('$internalId', $rows['body']['rows'][2]));
$this->assertCount(3, $rows['body']['rows']);
foreach ($rows['body']['rows'] as $row) {
$this->assertArrayNotHasKey('$table', $row);
$this->assertEquals($databaseId, $row['$databaseId']);
$this->assertEquals($data['moviesId'], $row['$tableId']);
}
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1944, $rows['body']['rows'][2]['releaseYear']);
$this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $rows['body']['rows'][0]['releaseYear']);
$this->assertCount(3, $rows['body']['rows']);
// changing description column to be null by default instead of empty string
$patchNull = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/columns/string/description', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'default' => null,
'required' => false,
]);
// creating a dummy doc with null description
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Dummy',
'releaseYear' => 1944,
'birthDay' => '1975-06-12 14:12:55+02:00',
'actors' => [
'Dummy',
],
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
// fetching docs with cursor after the dummy doc with order attr description which is null
$rowsPaginated = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('dummy')->toString(),
Query::cursorAfter(new Document(['$id' => $row1['body']['$id']]))->toString()
],
]);
// should throw 400 as the order attr description of the selected doc is null
$this->assertEquals(400, $rowsPaginated['headers']['status-code']);
// deleting the dummy doc created
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $row1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
return ['rows' => $rows['body']['rows'], 'databaseId' => $databaseId];
}
/**
* @depends testListRows
*/
public function testGetRow(array $data): void
{
$databaseId = $data['databaseId'];
foreach ($data['rows'] as $row) {
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($response['body']['$id'], $row['$id']);
$this->assertEquals($row['$tableId'], $response['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $response['body']);
$this->assertEquals($row['$databaseId'], $response['body']['$databaseId']);
$this->assertEquals($response['body']['title'], $row['title']);
$this->assertEquals($response['body']['releaseYear'], $row['releaseYear']);
$this->assertEquals($response['body']['$permissions'], $row['$permissions']);
$this->assertEquals($response['body']['birthDay'], $row['birthDay']);
$this->assertFalse(array_key_exists('$internalId', $response['body']));
$this->assertFalse(array_key_exists('$tenant', $response['body']));
}
}
/**
* @depends testListRows
*/
public function testGetRowWithQueries(array $data): void
{
$databaseId = $data['databaseId'];
$row = $data['rows'][0];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['title', 'releaseYear', '$id'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($row['title'], $response['body']['title']);
$this->assertEquals($row['releaseYear'], $response['body']['releaseYear']);
$this->assertArrayNotHasKey('birthDay', $response['body']);
$sequence = $response['body']['$sequence'];
// Query by sequence on get single row route
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows/' . $row['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])->toString()
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals('Invalid query method: equal', $response['body']['message']);
// Query by sequence
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence.''])->toString()
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($row['title'], $response['body']['rows'][0]['title']);
$this->assertEquals($row['releaseYear'], $response['body']['rows'][0]['releaseYear']);
$this->assertTrue(array_key_exists('$sequence', $response['body']['rows'][0]));
}
/**
* @depends testCreateRow
*/
public function testListRowsAfterPagination(array $data): array
{
$databaseId = $data['databaseId'];
/**
* Test after without order.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals('Captain America', $base['body']['rows'][0]['title']);
$this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']);
$this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString()
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][1]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][1]['$id']);
$this->assertCount(2, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString()
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEmpty($rows['body']['rows']);
/**
* Test with ASC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']);
$this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(),
Query::orderAsc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertCount(1, $rows['body']['rows']);
/**
* Test with DESC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderDesc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']);
$this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorAfter(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(),
Query::orderDesc('releaseYear')->toString()
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][2]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertCount(1, $rows['body']['rows']);
/**
* Test after with unknown row.
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(),
],
]);
$this->assertEquals(400, $rows['headers']['status-code']);
/**
* Test null value for cursor
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'{"method":"cursorAfter","values":[null]}',
],
]);
$this->assertEquals(400, $rows['headers']['status-code']);
return [];
}
/**
* @depends testCreateRow
*/
public function testListRowsBeforePagination(array $data): array
{
$databaseId = $data['databaseId'];
/**
* Test before without order.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals('Captain America', $base['body']['rows'][0]['title']);
$this->assertEquals('Spider-Man: Far From Home', $base['body']['rows'][1]['title']);
$this->assertEquals('Spider-Man: Homecoming', $base['body']['rows'][2]['title']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['rows'][2]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertEquals($base['body']['rows'][1]['$id'], $rows['body']['rows'][1]['$id']);
$this->assertCount(2, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['rows'][0]['$id']]))->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEmpty($rows['body']['rows']);
/**
* Test with ASC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals(1944, $base['body']['rows'][0]['releaseYear']);
$this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['rows'][2]['releaseYear']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(),
Query::orderAsc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertCount(1, $rows['body']['rows']);
/**
* Test with DESC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $base['headers']['status-code']);
$this->assertEquals(1944, $base['body']['rows'][2]['releaseYear']);
$this->assertEquals(2017, $base['body']['rows'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['rows'][0]['releaseYear']);
$this->assertCount(3, $base['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::cursorBefore(new Document(['$id' => $base['body']['rows'][1]['$id']]))->toString(),
Query::orderDesc('releaseYear')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals($base['body']['rows'][0]['$id'], $rows['body']['rows'][0]['$id']);
$this->assertCount(1, $rows['body']['rows']);
return [];
}
/**
* @depends testCreateRow
*/
public function testListRowsLimitAndOffset(array $data): array
{
$databaseId = $data['databaseId'];
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('releaseYear')->toString(),
Query::limit(1)->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']);
$this->assertCount(1, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::orderAsc('releaseYear')->toString(),
Query::limit(2)->toString(),
Query::offset(1)->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(2017, $rows['body']['rows'][0]['releaseYear']);
$this->assertEquals(2019, $rows['body']['rows'][1]['releaseYear']);
$this->assertCount(2, $rows['body']['rows']);
return [];
}
/**
* @depends testCreateRow
*/
public function testRowsListQueries(array $data): array
{
$databaseId = $data['databaseId'];
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::search('title', 'Captain America')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']);
$this->assertCount(1, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$id', [$rows['body']['rows'][0]['$id']])->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1944, $rows['body']['rows'][0]['releaseYear']);
$this->assertCount(1, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::search('title', 'Homecoming')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(2017, $rows['body']['rows'][0]['releaseYear']);
$this->assertCount(1, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::search('title', 'spider')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(2019, $rows['body']['rows'][0]['releaseYear']);
$this->assertEquals(2017, $rows['body']['rows'][1]['releaseYear']);
$this->assertCount(2, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'{"method":"contains","attribute":"title","values":[bad]}'
],
]);
$this->assertEquals(400, $rows['headers']['status-code']);
$this->assertEquals('Invalid query: Syntax error', $rows['body']['message']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::contains('title', ['spi'])->toString(), // like query
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(2, $rows['body']['total']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('releaseYear', [1944])->toString(),
],
]);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Captain America', $rows['body']['rows'][0]['title']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::notEqual('releaseYear', 1944)->toString(),
],
]);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('Spider-Man: Far From Home', $rows['body']['rows'][0]['title']);
$this->assertEquals('Spider-Man: Homecoming', $rows['body']['rows'][1]['title']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('$createdAt', '1976-06-12')->toString(),
],
]);
$this->assertCount(3, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::lessThan('$createdAt', '1976-06-12')->toString(),
],
]);
$this->assertCount(0, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::contains('actors', ['Tom Holland', 'Samuel Jackson'])->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(3, $rows['body']['total']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::contains('actors', ['Tom'])->toString(), // Full-match not like
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(0, $rows['body']['total']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('birthDay', '16/01/2024 12:00:00AM')->toString(),
],
]);
$this->assertEquals(400, $rows['headers']['status-code']);
$this->assertEquals('Invalid query: Query value is invalid for attribute "birthDay"', $rows['body']['message']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::greaterThan('birthDay', '1960-01-01 10:10:10+02:30')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals('1975-06-12T12:12:55.000+00:00', $rows['body']['rows'][0]['birthDay']);
$this->assertEquals('1975-06-12T18:12:55.000+00:00', $rows['body']['rows'][1]['birthDay']);
$this->assertCount(2, $rows['body']['rows']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::isNull('integers')->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1, $rows['body']['total']);
/**
* Test for Failure
*/
$conditions = [];
for ($i = 0; $i < APP_DATABASE_QUERY_MAX_VALUES + 1; $i++) {
$conditions[] = $i;
}
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('releaseYear', $conditions)->toString(),
],
]);
$this->assertEquals(400, $rows['headers']['status-code']);
$this->assertEquals('Invalid query: Query on attribute has greater than '.APP_DATABASE_QUERY_MAX_VALUES.' values: releaseYear', $rows['body']['message']);
$value = '';
for ($i = 0; $i < 101; $i++) {
$value .= "[" . $i . "] Too long title to cross 2k chars query limit ";
}
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::search('title', $value)->toString(),
],
]);
// Todo: Not sure what to do we with Query length Test VS old? JSON validator will fails if query string will be truncated?
//$this->assertEquals(400, $rows['headers']['status-code']);
// Todo: Disabled for CL - Uncomment after ProxyDatabase cleanup for find method
// $rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'queries' => [
// Query::search('actors', 'Tom')->toString(),
// ],
// ]);
// $this->assertEquals(400, $rows['headers']['status-code']);
// $this->assertEquals('Invalid query: Cannot query search on attribute "actors" because it is an array.', $rows['body']['message']);
return [];
}
/**
* @depends testCreateRow
*/
public function testUpdateRow(array $data): array
{
$databaseId = $data['databaseId'];
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Thor: Ragnaroc',
'releaseYear' => 2017,
'birthDay' => '1976-06-12 14:12:55',
'actors' => [],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$id = $row['body']['$id'];
$this->assertEquals(201, $row['headers']['status-code']);
$this->assertEquals($data['moviesId'], $row['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row['body']);
$this->assertEquals($databaseId, $row['body']['$databaseId']);
$this->assertEquals($row['body']['title'], 'Thor: Ragnaroc');
$this->assertEquals($row['body']['releaseYear'], 2017);
$dateValidator = new DatetimeValidator();
$this->assertEquals(true, $dateValidator->isValid($row['body']['$createdAt']));
$this->assertEquals(true, $dateValidator->isValid($row['body']['birthDay']));
$this->assertContains(Permission::read(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals($row['body']['$id'], $id);
$this->assertEquals($data['moviesId'], $row['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row['body']);
$this->assertEquals($databaseId, $row['body']['$databaseId']);
$this->assertEquals($row['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($row['body']['releaseYear'], 2017);
$this->assertContains(Permission::read(Role::users()), $row['body']['$permissions']);
$this->assertContains(Permission::update(Role::users()), $row['body']['$permissions']);
$this->assertContains(Permission::delete(Role::users()), $row['body']['$permissions']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$id = $row['body']['$id'];
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals($data['moviesId'], $row['body']['$tableId']);
$this->assertArrayNotHasKey('$table', $row['body']);
$this->assertEquals($databaseId, $row['body']['$databaseId']);
$this->assertEquals($row['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($row['body']['releaseYear'], 2017);
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-timestamp' => DateTime::formatTz(DateTime::now()),
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test for failure
*/
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-timestamp' => 'invalid',
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals('Invalid X-Appwrite-Timestamp header value', $response['body']['message']);
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-timestamp' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -1000)),
], $this->getHeaders()), [
'data' => [
'title' => 'Thor: Ragnarok',
],
]);
$this->assertEquals(409, $response['headers']['status-code']);
$this->assertEquals('Remote row is newer than local.', $response['body']['message']);
$this->assertEquals(Exception::ROW_UPDATE_CONFLICT, $response['body']['type']);
return [];
}
public function testOperators(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Test Database for Operators'
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
// Create table
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'tableId' => ID::unique(),
'name' => 'Operator Tests',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create columns
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'releaseYear',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'duration',
'required' => false,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'integers',
'required' => false,
'array' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'tagline',
'size' => 512,
'required' => false,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'birthDay',
'required' => false,
]);
// Wait for columns to be created
sleep(2);
// Create a row to test operators
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Operator Test',
'releaseYear' => 2020,
'duration' => 120,
'actors' => ['Actor1', 'Actor2'],
'integers' => [10, 20],
'tagline' => 'Original',
'birthDay' => '2020-01-01 12:00:00',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $row['headers']['status-code']);
$rowId = $row['body']['$id'];
// Test increment operator on integer
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'releaseYear' => Operator::increment(5)->toString(),
'duration' => Operator::increment(10)->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals(2025, $updated['body']['releaseYear']);
$this->assertEquals(130, $updated['body']['duration']);
// Test decrement operator
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'releaseYear' => Operator::decrement(3)->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals(2022, $updated['body']['releaseYear']);
// Test array append operator
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'actors' => Operator::arrayAppend(['Actor3'])->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals(['Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
// Test array prepend operator
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'actors' => Operator::arrayPrepend(['Actor0'])->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals(['Actor0', 'Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
// Test string concat operator
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'tagline' => Operator::stringConcat(' Appended')->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals('Original Appended', $updated['body']['tagline']);
// Test multiple operators in a single update
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'releaseYear' => Operator::increment(1)->toString(),
'integers' => Operator::arrayAppend([30])->toString(),
],
]);
$this->assertEquals(200, $updated['headers']['status-code']);
$this->assertEquals(2023, $updated['body']['releaseYear']);
$this->assertEquals([10, 20, 30], $updated['body']['integers']);
// Test upsert with operators
$upsertId = ID::unique();
$upserted = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $upsertId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Upsert Test',
'releaseYear' => 2020,
'actors' => [],
'birthDay' => '2020-01-01 12:00:00',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(200, $upserted['headers']['status-code']);
$upserted = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $upsertId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Upsert Test Updated',
'releaseYear' => Operator::increment(5)->toString(),
'actors' => [],
'birthDay' => '2020-01-01 12:00:00',
],
]);
$this->assertEquals(200, $upserted['headers']['status-code']);
$this->assertEquals(2025, $upserted['body']['releaseYear']);
}
public function testBulkOperators(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Test Database for Bulk Operators'
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
// Create table
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'tableId' => ID::unique(),
'name' => 'Bulk Operator Tests',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::users()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create columns
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'releaseYear',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'birthDay',
'required' => false,
]);
// Wait for columns to be created
sleep(2);
// Create multiple rows
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Bulk Test 1',
'releaseYear' => 2020,
'actors' => ['Actor1'],
'birthDay' => '2020-01-01 12:00:00',
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Bulk Test 2',
'releaseYear' => 2021,
'actors' => ['Actor2'],
'birthDay' => '2020-01-01 12:00:00',
],
'permissions' => [
Permission::read(Role::users()),
Permission::update(Role::users()),
Permission::delete(Role::users()),
],
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$this->assertEquals(201, $row2['headers']['status-code']);
// Test bulk update with operators
$bulkUpdate = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'data' => [
'releaseYear' => Operator::increment(10)->toString(),
],
'queries' => [
Query::startsWith('title', 'Bulk Test')->toString(),
],
]);
$this->assertEquals(200, $bulkUpdate['headers']['status-code']);
$this->assertGreaterThanOrEqual(2, $bulkUpdate['body']['total']);
// Verify the updates
$verify1 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $verify1['headers']['status-code']);
$this->assertEquals(2030, $verify1['body']['releaseYear']);
$verify2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row2['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $verify2['headers']['status-code']);
$this->assertEquals(2031, $verify2['body']['releaseYear']);
}
/**
* @depends testCreateRow
*/
public function testDeleteRow(array $data): array
{
$databaseId = $data['databaseId'];
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Thor: Ragnarok',
'releaseYear' => 2017,
'birthDay' => '1975-06-12 14:12:55',
'actors' => [],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$id = $row['body']['$id'];
$this->assertEquals(201, $row['headers']['status-code']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $row['headers']['status-code']);
$row = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $row['headers']['status-code']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $row['headers']['status-code']);
return $data;
}
public function testInvalidRowStructure(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'InvalidRowDatabase',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('InvalidRowDatabase', $database['body']['name']);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'invalidRowStructure',
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $table['headers']['status-code']);
$this->assertEquals('invalidRowStructure', $table['body']['name']);
$tableId = $table['body']['$id'];
$email = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'email',
'required' => false,
]);
$enum = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
]);
$ip = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'ip',
'required' => false,
]);
$url = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'url',
'size' => 256,
'required' => false,
]);
$range = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'range',
'required' => false,
'min' => 1,
'max' => 10,
]);
// TODO@kodumbeats min and max are rounded in error message
$floatRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'floatRange',
'required' => false,
'min' => 1.1,
'max' => 1.4,
]);
$probability = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/float', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'probability',
'required' => false,
'default' => 0,
'min' => 0,
'max' => 1,
]);
$upperBound = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'upperBound',
'required' => false,
'max' => 10,
]);
$lowerBound = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'lowerBound',
'required' => false,
'min' => 5,
]);
/**
* Test for failure
*/
$invalidRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'invalidRange',
'required' => false,
'min' => 4,
'max' => 3,
]);
$defaultArray = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'defaultArray',
'required' => false,
'default' => 42,
'array' => true,
]);
$defaultRequired = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => ID::custom('defaultRequired'),
'required' => true,
'default' => 12
]);
$enumDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => ID::custom('enumDefault'),
'elements' => ['north', 'west'],
'default' => 'south'
]);
$enumDefaultStrict = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/enum', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => ID::custom('enumDefault'),
'elements' => ['north', 'west'],
'default' => 'NORTH'
]);
$goodDatetime = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'birthDay',
'required' => false,
'default' => null
]);
$datetimeDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'badBirthDay',
'required' => false,
'default' => 'bad'
]);
$this->assertEquals(202, $email['headers']['status-code']);
$this->assertEquals(202, $ip['headers']['status-code']);
$this->assertEquals(202, $url['headers']['status-code']);
$this->assertEquals(202, $range['headers']['status-code']);
$this->assertEquals(202, $floatRange['headers']['status-code']);
$this->assertEquals(202, $probability['headers']['status-code']);
$this->assertEquals(202, $upperBound['headers']['status-code']);
$this->assertEquals(202, $lowerBound['headers']['status-code']);
$this->assertEquals(202, $enum['headers']['status-code']);
$this->assertEquals(202, $goodDatetime['headers']['status-code']);
$this->assertEquals(400, $invalidRange['headers']['status-code']);
$this->assertEquals(400, $defaultArray['headers']['status-code']);
$this->assertEquals(400, $defaultRequired['headers']['status-code']);
$this->assertEquals(400, $enumDefault['headers']['status-code']);
$this->assertEquals(400, $enumDefaultStrict['headers']['status-code']);
$this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']);
$this->assertEquals('Cannot set default value for array columns', $defaultArray['body']['message']);
$this->assertEquals(400, $datetimeDefault['headers']['status-code']);
// wait for worker to add attributes
sleep(3);
$table = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
$this->assertCount(10, $table['body']['columns']);
/**
* Test for successful validation
*/
$goodEmail = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'email' => 'user@example.com',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodEnum = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'enum' => 'yes',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodIp = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'ip' => '1.1.1.1',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodUrl = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'url' => 'http://www.example.com',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'range' => 3,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodFloatRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'floatRange' => 1.4,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$goodProbability = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'probability' => 0.99999,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$notTooHigh = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'upperBound' => 8,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$notTooLow = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'lowerBound' => 8,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$this->assertEquals(201, $goodEmail['headers']['status-code']);
$this->assertEquals(201, $goodEnum['headers']['status-code']);
$this->assertEquals(201, $goodIp['headers']['status-code']);
$this->assertEquals(201, $goodUrl['headers']['status-code']);
$this->assertEquals(201, $goodRange['headers']['status-code']);
$this->assertEquals(201, $goodFloatRange['headers']['status-code']);
$this->assertEquals(201, $goodProbability['headers']['status-code']);
$this->assertEquals(201, $notTooHigh['headers']['status-code']);
$this->assertEquals(201, $notTooLow['headers']['status-code']);
/*
* Test that custom validators reject rows
*/
$badEmail = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'email' => 'user@@example.com',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badEnum = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'enum' => 'badEnum',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badIp = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'ip' => '1.1.1.1.1',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badUrl = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'url' => 'example...com',
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'range' => 11,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badFloatRange = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'floatRange' => 2.5,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badProbability = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'probability' => 1.1,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$tooHigh = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'upperBound' => 11,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$tooLow = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'lowerBound' => 3,
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$badTime = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => 'unique()',
'data' => [
'birthDay' => '2020-10-10 27:30:10+01:00',
],
'read' => ['user:' . $this->getUser()['$id']],
'write' => ['user:' . $this->getUser()['$id']],
]);
$this->assertEquals(400, $badEmail['headers']['status-code']);
$this->assertEquals(400, $badEnum['headers']['status-code']);
$this->assertEquals(400, $badIp['headers']['status-code']);
$this->assertEquals(400, $badUrl['headers']['status-code']);
$this->assertEquals(400, $badRange['headers']['status-code']);
$this->assertEquals(400, $badFloatRange['headers']['status-code']);
$this->assertEquals(400, $badProbability['headers']['status-code']);
$this->assertEquals(400, $tooHigh['headers']['status-code']);
$this->assertEquals(400, $tooLow['headers']['status-code']);
$this->assertEquals(400, $badTime['headers']['status-code']);
// TODO: @itznotabug - database library needs to throw error based on context!
$this->assertEquals('Invalid document structure: Attribute "email" has invalid format. Value must be a valid email address', $badEmail['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "enum" has invalid format. Value must be one of (yes, no, maybe)', $badEnum['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "ip" has invalid format. Value must be a valid IP address', $badIp['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "url" has invalid format. Value must be a valid URL', $badUrl['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "range" has invalid format. Value must be a valid range between 1 and 10', $badRange['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']);
$this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']);
}
/**
* @depends testDeleteRow
*/
public function testDefaultPermissions(array $data): array
{
$databaseId = $data['databaseId'];
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
'releaseYear' => 1944,
'actors' => [],
],
]);
$id = $row['body']['$id'];
$this->assertEquals(201, $row['headers']['status-code']);
$this->assertEquals($row['body']['title'], 'Captain America');
$this->assertEquals($row['body']['releaseYear'], 1944);
$this->assertIsArray($row['body']['$permissions']);
if ($this->getSide() == 'client') {
$this->assertCount(3, $row['body']['$permissions']);
$this->assertContains(Permission::read(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
}
if ($this->getSide() == 'server') {
$this->assertCount(0, $row['body']['$permissions']);
$this->assertEquals([], $row['body']['$permissions']);
}
// Updated Permissions
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Captain America 2',
'releaseYear' => 1945,
'actors' => [],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id']))
],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals($row['body']['title'], 'Captain America 2');
$this->assertEquals($row['body']['releaseYear'], 1945);
// This differs from the old permissions model because we don't inherit
// existing row permissions on update, unless none were supplied,
// so that specific types can be removed if wanted.
$this->assertCount(2, $row['body']['$permissions']);
$this->assertEquals([
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
], $row['body']['$permissions']);
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals($row['body']['title'], 'Captain America 2');
$this->assertEquals($row['body']['releaseYear'], 1945);
$this->assertCount(2, $row['body']['$permissions']);
$this->assertEquals([
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
], $row['body']['$permissions']);
// Reset Permissions
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'title' => 'Captain America 3',
'releaseYear' => 1946,
'actors' => [],
],
'permissions' => [],
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertEquals($row['body']['title'], 'Captain America 3');
$this->assertEquals($row['body']['releaseYear'], 1946);
$this->assertCount(0, $row['body']['$permissions']);
$this->assertEquals([], $row['body']['$permissions']);
// Check client side can no longer read the row.
$row = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
switch ($this->getSide()) {
case 'client':
$this->assertEquals(404, $row['headers']['status-code']);
break;
case 'server':
$this->assertEquals(200, $row['headers']['status-code']);
break;
}
return $data;
}
public function testEnforceTableAndRowPermissions(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'EnforceCollectionAndRowPermissions',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('EnforceCollectionAndRowPermissions', $database['body']['name']);
$databaseId = $database['body']['$id'];
$user = $this->getUser()['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'enforceCollectionAndRowPermissions',
'rowSecurity' => true,
'permissions' => [
Permission::read(Role::user($user)),
Permission::create(Role::user($user)),
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$this->assertEquals($table['body']['name'], 'enforceCollectionAndRowPermissions');
$this->assertEquals($table['body']['rowSecurity'], true);
$tableId = $table['body']['$id'];
sleep(2);
$attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'column',
'size' => 64,
'required' => true,
]);
$this->assertEquals(202, $attribute['headers']['status-code'], 202);
$this->assertEquals('column', $attribute['body']['key']);
// wait for db to add column
sleep(2);
$index = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'key_attribute',
'type' => 'key',
'columns' => [$attribute['body']['key']],
]);
$this->assertEquals(202, $index['headers']['status-code']);
$this->assertEquals('key_attribute', $index['body']['key']);
// wait for db to add column
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::read(Role::user($user)),
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::read(Role::user(ID::custom('other'))),
Permission::update(Role::user(ID::custom('other'))),
],
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$rowsUser1 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// Current user has read permission on the table so can get any row
$this->assertEquals(3, $rowsUser1['body']['total']);
$this->assertCount(3, $rowsUser1['body']['rows']);
$row3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// Current user has read permission on the table so can get any row
$this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']);
$email = uniqid() . 'user@localhost.test';
$password = 'password';
$name = 'User Name';
$this->client->call(Client::METHOD_POST, '/account', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'userId' => ID::custom('other'),
'email' => $email,
'password' => $password,
'name' => $name,
]);
$session2 = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'email' => $email,
'password' => $password,
]);
$session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']];
$row3GetWithRowRead = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// Current user has no table permissions but has read permission for this row
$this->assertEquals(200, $row3GetWithRowRead['headers']['status-code']);
$row2GetFailure = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row2['body']['$id'], [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// Current user has no table or row permissions for this row
$this->assertEquals(404, $row2GetFailure['headers']['status-code']);
$rowsUser2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// Current user has no table permissions but has read permission for one row
$this->assertEquals(1, $rowsUser2['body']['total']);
$this->assertCount(1, $rowsUser2['body']['rows']);
}
public function testEnforceTablePermissions(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'EnforceCollectionPermissions',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('EnforceCollectionPermissions', $database['body']['name']);
$databaseId = $database['body']['$id'];
$user = $this->getUser()['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'enforceCollectionPermissions',
'permissions' => [
Permission::read(Role::user($user)),
Permission::create(Role::user($user)),
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$this->assertEquals($table['body']['name'], 'enforceCollectionPermissions');
$this->assertEquals($table['body']['rowSecurity'], false);
$tableId = $table['body']['$id'];
$attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'column',
'size' => 64,
'required' => true,
]);
$this->assertEquals(202, $attribute['headers']['status-code'], 202);
$this->assertEquals('column', $attribute['body']['key']);
\sleep(2);
$index = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'key_attribute',
'type' => 'key',
'columns' => [$attribute['body']['key']],
]);
$this->assertEquals(202, $index['headers']['status-code']);
$this->assertEquals('key_attribute', $index['body']['key']);
\sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::read(Role::user($user)),
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::update(Role::user($user)),
Permission::delete(Role::user($user)),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'rowId' => ID::unique(),
'data' => [
'column' => 'one',
],
'permissions' => [
Permission::read(Role::user(ID::custom('other2'))),
Permission::update(Role::user(ID::custom('other2'))),
],
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$rowsUser1 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// Current user has read permission on the table so can get any row
$this->assertEquals(3, $rowsUser1['body']['total']);
$this->assertCount(3, $rowsUser1['body']['rows']);
$row3GetWithCollectionRead = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// Current user has read permission on the table so can get any row
$this->assertEquals(200, $row3GetWithCollectionRead['headers']['status-code']);
$email = uniqid() . 'user2@localhost.test';
$password = 'password';
$name = 'User Name';
$this->client->call(Client::METHOD_POST, '/account', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'userId' => ID::custom('other2'),
'email' => $email,
'password' => $password,
'name' => $name,
]);
$session2 = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'email' => $email,
'password' => $password,
]);
$session2 = $session2['cookies']['a_session_' . $this->getProject()['$id']];
$row3GetWithRowRead = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row3['body']['$id'], [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// other2 has no table permissions and row permissions are disabled
$this->assertEquals(404, $row3GetWithRowRead['headers']['status-code']);
$rowsUser2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// other2 has no table permissions and row permissions are disabled
$this->assertEquals(401, $rowsUser2['headers']['status-code']);
// Enable row permissions
$this->client->call(CLient::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'name' => $table['body']['name'],
'rowSecurity' => true,
]);
$rowsUser2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2,
]);
// Current user has no table permissions read access to one row
$this->assertEquals(1, $rowsUser2['body']['total']);
$this->assertCount(1, $rowsUser2['body']['rows']);
}
/**
* @depends testDefaultPermissions
*/
public function testUniqueIndexDuplicate(array $data): array
{
$databaseId = $data['databaseId'];
$uniqueIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'unique_title',
'type' => 'unique',
'columns' => ['title'],
]);
$this->assertEquals(202, $uniqueIndex['headers']['status-code']);
sleep(2);
// test for failure
$duplicate = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
'releaseYear' => 1944,
'actors' => [
'Chris Evans',
'Samuel Jackson',
]
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$this->assertEquals(409, $duplicate['headers']['status-code']);
// Test for exception when updating row to conflict
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America 5',
'releaseYear' => 1944,
'actors' => [
'Chris Evans',
'Samuel Jackson',
]
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$this->assertEquals(201, $row['headers']['status-code']);
// Test for exception when updating row to conflict
$duplicate = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $data['moviesId'] . '/rows/' . $row['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
'releaseYear' => 1944,
'actors' => [
'Chris Evans',
'Samuel Jackson',
]
],
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$this->assertEquals(409, $duplicate['headers']['status-code']);
return $data;
}
/**
* @depends testUniqueIndexDuplicate
*/
public function testPersistentCreatedAt(array $data): array
{
$headers = $this->getSide() === 'client' ? array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()) : [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
];
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows', $headers, [
'rowId' => ID::unique(),
'data' => [
'title' => 'Creation Date Test',
'releaseYear' => 2000
]
]);
$this->assertEquals($row['body']['title'], 'Creation Date Test');
$rowId = $row['body']['$id'];
$createdAt = $row['body']['$createdAt'];
$updatedAt = $row['body']['$updatedAt'];
\sleep(1);
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, $headers, [
'data' => [
'title' => 'Updated Date Test',
]
]);
$updatedAtSecond = $row['body']['$updatedAt'];
$this->assertEquals($row['body']['title'], 'Updated Date Test');
$this->assertEquals($row['body']['$createdAt'], $createdAt);
$this->assertNotEquals($row['body']['$updatedAt'], $updatedAt);
\sleep(1);
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['moviesId'] . '/rows/' . $rowId, $headers, [
'data' => [
'title' => 'Again Updated Date Test',
'$createdAt' => '2022-08-01 13:09:23.040',
'$updatedAt' => '2022-08-01 13:09:23.050'
]
]);
if ($this->getSide() === 'client') {
$this->assertEquals($row['body']['title'], 'Again Updated Date Test');
$this->assertNotEquals($row['body']['$createdAt'], DateTime::formatTz('2022-08-01 13:09:23.040'));
$this->assertNotEquals($row['body']['$updatedAt'], DateTime::formatTz('2022-08-01 13:09:23.050'));
} else {
$this->assertEquals($row['body']['$createdAt'], DateTime::formatTz('2022-08-01 13:09:23.040'));
$this->assertEquals($row['body']['$updatedAt'], DateTime::formatTz('2022-08-01 13:09:23.050'));
}
return $data;
}
public function testUpdatePermissionsWithEmptyPayload(): array
{
// Create Database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'Empty Permissions',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
// Create table
$movies = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Movies',
'permissions' => [
Permission::create(Role::user(ID::custom($this->getUser()['$id']))),
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $movies['headers']['status-code']);
$this->assertEquals($movies['body']['name'], 'Movies');
$moviesId = $movies['body']['$id'];
// create column
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $moviesId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
// wait for database worker to create attributes
sleep(2);
// add row
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $moviesId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Captain America',
],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$id = $row['body']['$id'];
$this->assertEquals(201, $row['headers']['status-code']);
$this->assertCount(3, $row['body']['$permissions']);
$this->assertContains(Permission::read(Role::any()), $row['body']['$permissions']);
$this->assertContains(Permission::update(Role::any()), $row['body']['$permissions']);
$this->assertContains(Permission::delete(Role::any()), $row['body']['$permissions']);
// Send only read permission
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $moviesId . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'permissions' => [
Permission::read(Role::user(ID::custom($this->getUser()['$id']))),
]
]);
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertCount(1, $row['body']['$permissions']);
// Send only mutation permissions
$row = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $moviesId . '/rows/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'permissions' => [
Permission::update(Role::user(ID::custom($this->getUser()['$id']))),
Permission::delete(Role::user(ID::custom($this->getUser()['$id']))),
],
]);
if ($this->getSide() == 'server') {
$this->assertEquals(200, $row['headers']['status-code']);
$this->assertCount(2, $row['body']['$permissions']);
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $row['body']['$permissions']);
}
// remove table
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $moviesId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
return [];
}
/**
* @depends testCreateDatabase
*/
public function testColumnBooleanDefault(array $data): void
{
$databaseId = $data['databaseId'];
/**
* Test for SUCCESS
*/
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Boolean'
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
$true = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'true',
'required' => false,
'default' => true
]);
$this->assertEquals(202, $true['headers']['status-code']);
$false = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'false',
'required' => false,
'default' => false
]);
$this->assertEquals(202, $false['headers']['status-code']);
}
/**
* @depends testCreateDatabase
*/
public function testOneToOneRelationship(array $data): array
{
$databaseId = $data['databaseId'];
$person = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'person',
'name' => 'person',
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
Permission::create(Role::user($this->getUser()['$id'])),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $person['headers']['status-code']);
$library = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'library',
'name' => 'library',
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::create(Role::user($this->getUser()['$id'])),
],
'rowSecurity' => true,
]);
$this->assertEquals(201, $library['headers']['status-code']);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'fullName',
'size' => 255,
'required' => false,
]);
sleep(1); // Wait for worker
$relation = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => 'library',
'type' => Database::RELATION_ONE_TO_ONE,
'key' => 'library',
'twoWay' => true,
'onDelete' => Database::RELATION_MUTATE_CASCADE,
]);
sleep(1); // Wait for worker
$libraryName = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'libraryName',
'size' => 255,
'required' => true,
]);
sleep(1); // Wait for worker
$this->assertEquals(202, $libraryName['headers']['status-code']);
$this->assertEquals(202, $relation['headers']['status-code']);
$this->assertEquals('library', $relation['body']['key']);
$this->assertEquals('relationship', $relation['body']['type']);
$this->assertEquals('processing', $relation['body']['status']);
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $columns['headers']['status-code']);
$this->assertEquals(2, $columns['body']['total']);
$columns = $columns['body']['columns'];
$this->assertEquals('library', $columns[1]['relatedTable']);
$this->assertEquals('oneToOne', $columns[1]['relationType']);
$this->assertEquals(true, $columns[1]['twoWay']);
$this->assertEquals('person', $columns[1]['twoWayKey']);
$this->assertEquals(Database::RELATION_MUTATE_CASCADE, $columns[1]['onDelete']);
$attribute = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$person['body']['$id']}/columns/library", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $attribute['headers']['status-code']);
$this->assertEquals('available', $attribute['body']['status']);
$this->assertEquals('library', $attribute['body']['key']);
$this->assertEquals('relationship', $attribute['body']['type']);
$this->assertEquals(false, $attribute['body']['required']);
$this->assertEquals(false, $attribute['body']['array']);
$this->assertEquals('oneToOne', $attribute['body']['relationType']);
$this->assertEquals(true, $attribute['body']['twoWay']);
$this->assertEquals('person', $attribute['body']['twoWayKey']);
$this->assertEquals(Database::RELATION_MUTATE_CASCADE, $attribute['body']['onDelete']);
$person1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'library' => [
'$id' => 'library1',
'$permissions' => [
Permission::read(Role::any()),
],
'libraryName' => 'Library 1',
],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals('Library 1', $person1['body']['library']['libraryName']);
// Create without nested ID
$person2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'library' => [
'libraryName' => 'Library 2',
],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals('Library 2', $person2['body']['library']['libraryName']);
// Ensure IDs were set and internal IDs removed
$this->assertEquals($databaseId, $person1['body']['$databaseId']);
$this->assertEquals($databaseId, $person1['body']['library']['$databaseId']);
$this->assertEquals($person['body']['$id'], $person1['body']['$tableId']);
$this->assertEquals($library['body']['$id'], $person1['body']['library']['$tableId']);
$this->assertArrayNotHasKey('$table', $person1['body']);
$this->assertArrayNotHasKey('$table', $person1['body']['library']);
$this->assertArrayNotHasKey('$internalId', $person1['body']);
$this->assertArrayNotHasKey('$internalId', $person1['body']['library']);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', 'library.*'])->toString(),
Query::equal('library', ['library1'])->toString(),
],
]);
$this->assertEquals(1, $rows['body']['total']);
$this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']);
$this->assertArrayHasKey('fullName', $rows['body']['rows'][0]);
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['library.*'])->toString(),
Query::equal('library.libraryName', ['Library 1'])->toString(),
],
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertEquals(1, $rows['body']['total']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']);
$this->assertEquals($person1['body']['$id'], $rows['body']['rows'][0]['$id']);
$response = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/library', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
sleep(2);
$this->assertEquals(204, $response['headers']['status-code']);
$attribute = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$person['body']['$id']}/columns/library", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(404, $attribute['headers']['status-code']);
$person1 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/rows/' . $person1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertArrayNotHasKey('library', $person1['body']);
//Test Deletion of related twoKey
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $library['body']['$id'] . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $columns['headers']['status-code']);
$this->assertEquals(1, $columns['body']['total']);
$this->assertEquals('libraryName', $columns['body']['columns'][0]['key']);
return [
'databaseId' => $databaseId,
'personCollection' => $person['body']['$id'],
'libraryCollection' => $library['body']['$id'],
];
}
/**
* @depends testOneToOneRelationship
*/
public function testOneToManyRelationship(array $data): array
{
$databaseId = $data['databaseId'];
$personCollection = $data['personCollection'];
$libraryCollection = $data['libraryCollection'];
// One person can own several libraries
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $personCollection . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => 'library',
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => true,
'key' => 'libraries',
'twoWayKey' => 'person_one_to_many',
]);
sleep(1);
$libraryAttributesResponse = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $libraryCollection . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertIsArray($libraryAttributesResponse['body']['columns']);
$this->assertEquals(2, $libraryAttributesResponse['body']['total']);
$this->assertEquals('person_one_to_many', $libraryAttributesResponse['body']['columns'][1]['key']);
$libraryCollectionResponse = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $libraryCollection, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertIsArray($libraryCollectionResponse['body']['columns']);
$this->assertCount(2, $libraryCollectionResponse['body']['columns']);
$attribute = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$personCollection}/columns/libraries", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $attribute['headers']['status-code']);
$this->assertEquals('available', $attribute['body']['status']);
$this->assertEquals('libraries', $attribute['body']['key']);
$this->assertEquals('relationship', $attribute['body']['type']);
$this->assertEquals(false, $attribute['body']['required']);
$this->assertEquals(false, $attribute['body']['array']);
$this->assertEquals('oneToMany', $attribute['body']['relationType']);
$this->assertEquals(true, $attribute['body']['twoWay']);
$this->assertEquals('person_one_to_many', $attribute['body']['twoWayKey']);
$this->assertEquals('restrict', $attribute['body']['onDelete']);
$person2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $personCollection . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => 'person10',
'data' => [
'fullName' => 'Stevie Wonder',
'libraries' => [
[
'$id' => 'library10',
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'libraryName' => 'Library 10',
],
[
'$id' => 'library11',
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'libraryName' => 'Library 11',
]
],
],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
]
]);
$this->assertEquals(201, $person2['headers']['status-code']);
$this->assertArrayHasKey('libraries', $person2['body']);
$this->assertEquals(2, count($person2['body']['libraries']));
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $personCollection . '/rows/' . $person2['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'libraries.*'])->toString()
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('$table', $response['body']);
$this->assertArrayHasKey('libraries', $response['body']);
$this->assertEquals(2, count($response['body']['libraries']));
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $libraryCollection . '/rows/library11', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['person_one_to_many.$id'])->toString()
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('person_one_to_many', $response['body']);
$this->assertEquals('person10', $response['body']['person_one_to_many']['$id']);
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $personCollection . '/columns/libraries/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'onDelete' => Database::RELATION_MUTATE_CASCADE,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$attribute = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$personCollection}/columns/libraries", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals(200, $attribute['headers']['status-code']);
$this->assertEquals('available', $attribute['body']['status']);
$this->assertEquals('libraries', $attribute['body']['key']);
$this->assertEquals('relationship', $attribute['body']['type']);
$this->assertEquals(false, $attribute['body']['required']);
$this->assertEquals(false, $attribute['body']['array']);
$this->assertEquals('oneToMany', $attribute['body']['relationType']);
$this->assertEquals(true, $attribute['body']['twoWay']);
$this->assertEquals(Database::RELATION_MUTATE_CASCADE, $attribute['body']['onDelete']);
return ['databaseId' => $databaseId, 'personCollection' => $personCollection];
}
/**
* @depends testCreateDatabase
*/
public function testManyToOneRelationship(array $data): array
{
$databaseId = $data['databaseId'];
// Create album table
$albums = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Albums',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
// Create album name column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 255,
'required' => true,
]);
// Create artist table
$artists = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Artists',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
// Create artist name column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $artists['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 255,
'required' => true,
]);
// Create relationship
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $artists['body']['$id'],
'type' => Database::RELATION_MANY_TO_ONE,
'twoWay' => true,
'key' => 'artist',
'twoWayKey' => 'albums',
]);
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertEquals('artist', $response['body']['key']);
$this->assertEquals('relationship', $response['body']['type']);
$this->assertEquals(false, $response['body']['required']);
$this->assertEquals(false, $response['body']['array']);
$this->assertEquals('manyToOne', $response['body']['relationType']);
$this->assertEquals(true, $response['body']['twoWay']);
$this->assertEquals('albums', $response['body']['twoWayKey']);
$this->assertEquals('restrict', $response['body']['onDelete']);
sleep(1); // Wait for worker
$permissions = [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
];
// Create album
$album = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => 'album1',
'permissions' => $permissions,
'data' => [
'name' => 'Album 1',
'artist' => [
'$id' => ID::unique(),
'name' => 'Artist 1',
],
],
]);
$this->assertEquals(201, $album['headers']['status-code']);
$this->assertEquals('album1', $album['body']['$id']);
$this->assertEquals('Album 1', $album['body']['name']);
$this->assertEquals('Artist 1', $album['body']['artist']['name']);
$this->assertEquals($permissions, $album['body']['$permissions']);
$this->assertEquals($permissions, $album['body']['artist']['$permissions']);
$album = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/rows/album1', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'artist.name', 'artist.$permissions'])->toString()
]
]);
$this->assertEquals(200, $album['headers']['status-code']);
$this->assertEquals('album1', $album['body']['$id']);
$this->assertEquals('Album 1', $album['body']['name']);
$this->assertEquals('Artist 1', $album['body']['artist']['name']);
$this->assertEquals($permissions, $album['body']['$permissions']);
$this->assertEquals($permissions, $album['body']['artist']['$permissions']);
$artist = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $artists['body']['$id'] . '/rows/' . $album['body']['artist']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'albums.$id', 'albums.name', 'albums.$permissions'])->toString()
]
]);
$this->assertEquals(200, $artist['headers']['status-code']);
$this->assertEquals('Artist 1', $artist['body']['name']);
$this->assertEquals($permissions, $artist['body']['$permissions']);
$this->assertEquals(1, count($artist['body']['albums']));
$this->assertEquals('album1', $artist['body']['albums'][0]['$id']);
$this->assertEquals('Album 1', $artist['body']['albums'][0]['name']);
$this->assertEquals($permissions, $artist['body']['albums'][0]['$permissions']);
return [
'databaseId' => $databaseId,
'albumsCollection' => $albums['body']['$id'],
'artistsCollection' => $artists['body']['$id'],
];
}
/**
* @depends testCreateDatabase
*/
public function testManyToManyRelationship(array $data): array
{
$databaseId = $data['databaseId'];
// Create sports table
$sports = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Sports',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
// Create sport name column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 255,
'required' => true,
]);
// Create player table
$players = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Players',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
// Create player name column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $players['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 255,
'required' => true,
]);
// Create relationship
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $players['body']['$id'],
'type' => Database::RELATION_MANY_TO_MANY,
'twoWay' => true,
'key' => 'players',
'twoWayKey' => 'sports',
'onDelete' => Database::RELATION_MUTATE_SET_NULL,
]);
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertEquals('players', $response['body']['key']);
$this->assertEquals('relationship', $response['body']['type']);
$this->assertEquals(false, $response['body']['required']);
$this->assertEquals(false, $response['body']['array']);
$this->assertEquals('manyToMany', $response['body']['relationType']);
$this->assertEquals(true, $response['body']['twoWay']);
$this->assertEquals('sports', $response['body']['twoWayKey']);
$this->assertEquals('setNull', $response['body']['onDelete']);
sleep(1); // Wait for worker
$permissions = [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
];
// Create sport
$sport = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => 'sport1',
'permissions' => $permissions,
'data' => [
'name' => 'Sport 1',
'players' => [
[
'$id' => 'player1',
'name' => 'Player 1',
],
[
'$id' => 'player2',
'name' => 'Player 2',
]
],
],
]);
$this->assertEquals(201, $sport['headers']['status-code']);
$this->assertEquals('sport1', $sport['body']['$id']);
$this->assertEquals('Sport 1', $sport['body']['name']);
$this->assertEquals('Player 1', $sport['body']['players'][0]['name']);
$this->assertEquals('Player 2', $sport['body']['players'][1]['name']);
$this->assertEquals($permissions, $sport['body']['$permissions']);
$this->assertEquals($permissions, $sport['body']['players'][0]['$permissions']);
$this->assertEquals($permissions, $sport['body']['players'][1]['$permissions']);
$sport = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/rows/sport1', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'players.name', 'players.$permissions'])->toString()
]
]);
$this->assertEquals(200, $sport['headers']['status-code']);
$this->assertEquals('sport1', $sport['body']['$id']);
$this->assertEquals('Sport 1', $sport['body']['name']);
$this->assertEquals('Player 1', $sport['body']['players'][0]['name']);
$this->assertEquals('Player 2', $sport['body']['players'][1]['name']);
$this->assertEquals($permissions, $sport['body']['$permissions']);
$this->assertEquals($permissions, $sport['body']['players'][0]['$permissions']);
$this->assertEquals($permissions, $sport['body']['players'][1]['$permissions']);
$player = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $players['body']['$id'] . '/rows/' . $sport['body']['players'][0]['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'sports.$id', 'sports.name', 'sports.$permissions'])->toString()
]
]);
$this->assertEquals(200, $player['headers']['status-code']);
$this->assertEquals('Player 1', $player['body']['name']);
$this->assertEquals($permissions, $player['body']['$permissions']);
$this->assertEquals(1, count($player['body']['sports']));
$this->assertEquals('sport1', $player['body']['sports'][0]['$id']);
$this->assertEquals('Sport 1', $player['body']['sports'][0]['name']);
$this->assertEquals($permissions, $player['body']['sports'][0]['$permissions']);
return [
'databaseId' => $databaseId,
'sportsCollection' => $sports['body']['$id'],
'playersCollection' => $players['body']['$id'],
];
}
/**
* @depends testOneToManyRelationship
*/
public function testValidateOperators(array $data): void
{
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::isNotNull('$id')->toString(),
Query::select(['*', 'libraries.*'])->toString(),
Query::startsWith('fullName', 'Stevie')->toString(),
Query::endsWith('fullName', 'Wonder')->toString(),
Query::between('$createdAt', '1975-12-06', '2050-12-01')->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, count($response['body']['rows']));
$this->assertEquals('person10', $response['body']['rows'][0]['$id']);
$this->assertEquals('Stevie Wonder', $response['body']['rows'][0]['fullName']);
$this->assertEquals(2, count($response['body']['rows'][0]['libraries']));
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::isNotNull('$id')->toString(),
Query::isNull('fullName')->toString(),
Query::select(['fullName'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, count($response['body']['rows']));
$this->assertEquals(null, $response['body']['rows'][0]['fullName']);
$this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]);
$this->assertArrayHasKey('$databaseId', $response['body']['rows'][0]);
$this->assertArrayHasKey('$tableId', $response['body']['rows'][0]);
}
/**
* @depends testOneToManyRelationship
*/
public function testSelectQueries(array $data): void
{
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('fullName', ['Stevie Wonder'])->toString(),
Query::select(['fullName'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]);
$this->assertArrayHasKey('$databaseId', $response['body']['rows'][0]);
$this->assertArrayHasKey('$tableId', $response['body']['rows'][0]);
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['libraries.*', '$id'])->toString(),
],
]);
$row = $response['body']['rows'][0];
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('libraries', $row);
$this->assertArrayHasKey('$databaseId', $row);
$this->assertArrayHasKey('$tableId', $row);
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows/' . $row['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['fullName', '$id'])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('fullName', $response['body']);
$this->assertArrayNotHasKey('libraries', $response['body']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testOrQueries(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Or queries'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('Or queries', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$presidents = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'USA Presidents',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $presidents['headers']['status-code']);
$this->assertEquals($presidents['body']['name'], 'USA Presidents');
// Create Attributes
$firstName = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'first_name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $firstName['headers']['status-code']);
$lastName = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'last_name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $lastName['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'first_name' => 'Donald',
'last_name' => 'Trump',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'first_name' => 'George',
'last_name' => 'Bush',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'first_name' => 'Joe',
'last_name' => 'Biden',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $presidents['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::select(['first_name', 'last_name'])->toString(),
Query::or([
Query::equal('first_name', ['Donald']),
Query::equal('last_name', ['Bush'])
])->toString(),
Query::limit(999)->toString(),
Query::offset(0)->toString()
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testNotContains(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'NotContains test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('NotContains test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$movies = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Movies',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $movies['headers']['status-code']);
$this->assertEquals($movies['body']['name'], 'Movies');
// Create Attributes
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$genre = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'genre',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $genre['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Spider-Man: Homecoming',
'genre' => 'Action',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'The Avengers',
'genre' => 'Action',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Romantic Comedy',
'genre' => 'Romance',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test notContains query - should return movies that don't contain "Spider" in title
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $movies['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::select(['title', 'genre'])->toString(),
Query::notContains('title', ['Spider'])->toString(),
Query::limit(999)->toString(),
Query::offset(0)->toString()
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('The Avengers', $rows['body']['rows'][0]['title']);
$this->assertEquals('Romantic Comedy', $rows['body']['rows'][1]['title']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testNotSearch(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'NotSearch test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('NotSearch test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$books = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Books',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $books['headers']['status-code']);
$this->assertEquals($books['body']['name'], 'Books');
// Create Attributes
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$description = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'description',
'size' => 2048,
'required' => true,
]);
$this->assertEquals(202, $description['headers']['status-code']);
\sleep(2);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'fts_description',
'type' => Database::INDEX_FULLTEXT,
'columns' => ['description'],
]);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Science Fiction Adventures',
'description' => 'A thrilling journey through space and time',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Romance Novel',
'description' => 'A love story set in modern times',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Mystery Thriller',
'description' => 'A detective solves complex crimes',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test notSearch query - should return books that don't have "space" in the description
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $books['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::notSearch('description', 'space')->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('Romance Novel', $rows['body']['rows'][0]['title']);
$this->assertEquals('Mystery Thriller', $rows['body']['rows'][1]['title']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testNotBetween(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'NotBetween test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('NotBetween test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$products = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Products',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $products['headers']['status-code']);
$this->assertEquals($products['body']['name'], 'Products');
// Create Attributes
$name = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $name['headers']['status-code']);
$price = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/columns/float', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'price',
'required' => true,
]);
$this->assertEquals(202, $price['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Cheap Product',
'price' => 5.99,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Mid Product',
'price' => 25.00,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Expensive Product',
'price' => 150.00,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test notBetween query - should return products NOT priced between 10 and 50
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::notBetween('price', 10, 50)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('Cheap Product', $rows['body']['rows'][0]['name']);
$this->assertEquals('Expensive Product', $rows['body']['rows'][1]['name']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testNotStartsWith(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'NotStartsWith test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('NotStartsWith test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$employees = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Employees',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $employees['headers']['status-code']);
$this->assertEquals($employees['body']['name'], 'Employees');
// Create Attributes
$name = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $name['headers']['status-code']);
$department = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'department',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $department['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'John Smith',
'department' => 'Engineering',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Jane Doe',
'department' => 'Marketing',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Bob Johnson',
'department' => 'Sales',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test notStartsWith query - should return employees whose names don't start with "John"
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $employees['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::notStartsWith('name', 'John')->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('Jane Doe', $rows['body']['rows'][0]['name']);
$this->assertEquals('Bob Johnson', $rows['body']['rows'][1]['name']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testNotEndsWith(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'NotEndsWith test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('NotEndsWith test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$files = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Files',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $files['headers']['status-code']);
$this->assertEquals($files['body']['name'], 'Files');
// Create Attributes
$filename = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'filename',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $filename['headers']['status-code']);
$type = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'type',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $type['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'filename' => 'row.pdf',
'type' => 'PDF',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'filename' => 'image.jpg',
'type' => 'Image',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'filename' => 'presentation.pptx',
'type' => 'Presentation',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test notEndsWith query - should return files that don't end with ".pdf"
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $files['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::notEndsWith('filename', '.pdf')->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(2, $rows['body']['rows']);
$this->assertEquals('image.jpg', $rows['body']['rows'][0]['filename']);
$this->assertEquals('presentation.pptx', $rows['body']['rows'][1]['filename']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testCreatedBefore(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CreatedBefore test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('CreatedBefore test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$posts = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Posts',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $posts['headers']['status-code']);
$this->assertEquals($posts['body']['name'], 'Posts');
// Create Attributes
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$content = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'content',
'size' => 512,
'required' => true,
]);
$this->assertEquals(202, $content['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Old Post',
'content' => 'This is an old post content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
// Sleep to ensure different creation times
sleep(1);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Recent Post',
'content' => 'This is a recent post content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
// Get the creation time of the second post to use as boundary
$secondPostCreatedAt = $row2['body']['$createdAt'];
// Sleep again
sleep(1);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Newest Post',
'content' => 'This is the newest post content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test createdBefore query - should return posts created before the second post
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $posts['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::createdBefore($secondPostCreatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Old Post', $rows['body']['rows'][0]['title']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testCreatedAfter(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CreatedAfter test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('CreatedAfter test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$events = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Events',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $events['headers']['status-code']);
$this->assertEquals($events['body']['name'], 'Events');
// Create Attributes
$name = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $name['headers']['status-code']);
$description = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'description',
'size' => 512,
'required' => true,
]);
$this->assertEquals(202, $description['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Early Event',
'description' => 'This is an early event',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
// Sleep to ensure different creation times
sleep(1);
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Middle Event',
'description' => 'This is a middle event',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
// Get the creation time of the second event to use as boundary
$secondEventCreatedAt = $row2['body']['$createdAt'];
// Sleep again
sleep(1);
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Latest Event',
'description' => 'This is the latest event',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Test createdAfter query - should return events created after the second event
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $events['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::createdAfter($secondEventCreatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Latest Event', $rows['body']['rows'][0]['name']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testCreatedBetween(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CreatedBetween test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('CreatedBetween test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$articles = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Articles',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $articles['headers']['status-code']);
$this->assertEquals($articles['body']['name'], 'Articles');
// Create Attributes
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$content = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'content',
'size' => 5000,
'required' => true,
]);
$this->assertEquals(202, $content['headers']['status-code']);
// Wait for attributes to be available
sleep(2);
// Create first article
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'First Article',
'content' => 'This is the first article content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$firstArticleCreatedAt = $row1['body']['$createdAt'];
// Sleep to ensure different timestamps
sleep(1);
// Create second article
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Second Article',
'content' => 'This is the second article content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$secondArticleCreatedAt = $row2['body']['$createdAt'];
// Sleep again
sleep(1);
// Create third article
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Third Article',
'content' => 'This is the third article content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$thirdArticleCreatedAt = $row3['body']['$createdAt'];
// Sleep again
sleep(1);
// Create fourth article
$row4 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Fourth Article',
'content' => 'This is the fourth article content',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row4['headers']['status-code']);
// Test createdBetween query - should return articles created between first and third (inclusive)
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::createdBetween($firstArticleCreatedAt, $thirdArticleCreatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(3, $rows['body']['rows']);
// Verify the returned articles are the correct ones
$titles = array_column($rows['body']['rows'], 'title');
$this->assertContains('First Article', $titles);
$this->assertContains('Second Article', $titles);
$this->assertContains('Third Article', $titles);
$this->assertNotContains('Fourth Article', $titles);
// Test createdBetween query - should return only the second article when using its timestamp for both bounds
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $articles['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::createdBetween($secondArticleCreatedAt, $secondArticleCreatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Second Article', $rows['body']['rows'][0]['title']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testUpdatedBefore(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'UpdatedBefore test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('UpdatedBefore test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$tasks = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Tasks',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $tasks['headers']['status-code']);
$this->assertEquals($tasks['body']['name'], 'Tasks');
// Create Attributes
$title = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $title['headers']['status-code']);
$status = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'status',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $status['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Task One',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$taskOneId = $row1['body']['$id'];
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Task Two',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$taskTwoId = $row2['body']['$id'];
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'title' => 'Task Three',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$taskThreeId = $row3['body']['$id'];
// Update first task
sleep(1);
$this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows/' . $taskOneId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'completed',
]
]);
// Update second task and get its updated time
sleep(1);
$updatedTaskTwo = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows/' . $taskTwoId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'in_progress',
]
]);
$secondTaskUpdatedAt = $updatedTaskTwo['body']['$updatedAt'];
// Update third task
sleep(1);
$this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows/' . $taskThreeId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'review',
]
]);
// Test updatedBefore query - should return tasks updated before the second task's update time
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $tasks['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::updatedBefore($secondTaskUpdatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Task One', $rows['body']['rows'][0]['title']);
$this->assertEquals('completed', $rows['body']['rows'][0]['status']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testUpdatedAfter(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'UpdatedAfter test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('UpdatedAfter test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$orders = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Orders',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $orders['headers']['status-code']);
$this->assertEquals($orders['body']['name'], 'Orders');
// Create Attributes
$orderNumber = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'orderNumber',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $orderNumber['headers']['status-code']);
$status = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'status',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $status['headers']['status-code']);
// Wait for worker
sleep(2);
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'orderNumber' => 'ORD-001',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
$orderOneId = $row1['body']['$id'];
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'orderNumber' => 'ORD-002',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
$orderTwoId = $row2['body']['$id'];
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'orderNumber' => 'ORD-003',
'status' => 'pending',
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
$orderThreeId = $row3['body']['$id'];
// Update first order
sleep(1);
$this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows/' . $orderOneId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'processing',
]
]);
// Update second order and get its updated time
sleep(1);
$updatedOrderTwo = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows/' . $orderTwoId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'shipped',
]
]);
$secondOrderUpdatedAt = $updatedOrderTwo['body']['$updatedAt'];
// Update third order
sleep(1);
$this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows/' . $orderThreeId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'status' => 'delivered',
]
]);
// Test updatedAfter query - should return orders updated after the second order's update time
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $orders['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::updatedAfter($secondOrderUpdatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('ORD-003', $rows['body']['rows'][0]['orderNumber']);
$this->assertEquals('delivered', $rows['body']['rows'][0]['status']);
}
/**
* @throws \Utopia\Database\Exception
* @throws \Utopia\Database\Exception\Query
*/
public function testUpdatedBetween(): void
{
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'UpdatedBetween test'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('UpdatedBetween test', $database['body']['name']);
$databaseId = $database['body']['$id'];
// Create Collection
$products = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Products',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $products['headers']['status-code']);
$this->assertEquals($products['body']['name'], 'Products');
// Create Attributes
$name = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $name['headers']['status-code']);
$price = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/columns/float', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'price',
'required' => true,
]);
$this->assertEquals(202, $price['headers']['status-code']);
// Wait for attributes to be available
sleep(2);
// Create first product
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Product A',
'price' => 99.99,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row1['headers']['status-code']);
// Sleep to ensure different timestamps
sleep(1);
// Create second product
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Product B',
'price' => 149.99,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row2['headers']['status-code']);
// Sleep again
sleep(1);
// Create third product
$row3 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Product C',
'price' => 199.99,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row3['headers']['status-code']);
// Sleep again
sleep(1);
// Create fourth product
$row4 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Product D',
'price' => 249.99,
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row4['headers']['status-code']);
// Now update products in sequence to get different updatedAt timestamps
sleep(1);
// Update first product
$update1 = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows/' . $row1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'price' => 89.99,
]
]);
$this->assertEquals(200, $update1['headers']['status-code']);
$firstProductUpdatedAt = $update1['body']['$updatedAt'];
sleep(1);
// Update second product
$update2 = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows/' . $row2['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'price' => 139.99,
]
]);
$this->assertEquals(200, $update2['headers']['status-code']);
$secondProductUpdatedAt = $update2['body']['$updatedAt'];
sleep(1);
// Update third product
$update3 = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows/' . $row3['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'price' => 189.99,
]
]);
$this->assertEquals(200, $update3['headers']['status-code']);
$thirdProductUpdatedAt = $update3['body']['$updatedAt'];
sleep(1);
// Update fourth product
$update4 = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows/' . $row4['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'price' => 239.99,
]
]);
$this->assertEquals(200, $update4['headers']['status-code']);
// Test updatedBetween query - should return products updated between first and third (inclusive)
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::updatedBetween($firstProductUpdatedAt, $thirdProductUpdatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(3, $rows['body']['rows']);
// Verify the returned products are the correct ones
$names = array_column($rows['body']['rows'], 'name');
$this->assertContains('Product A', $names);
$this->assertContains('Product B', $names);
$this->assertContains('Product C', $names);
$this->assertNotContains('Product D', $names);
// Test updatedBetween query - should return only the second product when using its timestamp for both bounds
$rows = $this->client->call(
Client::METHOD_GET,
'/tablesdb/' . $databaseId . '/tables/' . $products['body']['$id'] . '/rows',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),
[
'queries' => [
Query::updatedBetween($secondProductUpdatedAt, $secondProductUpdatedAt)->toString(),
],
]
);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertCount(1, $rows['body']['rows']);
$this->assertEquals('Product B', $rows['body']['rows'][0]['name']);
$this->assertEquals(139.99, $rows['body']['rows'][0]['price']);
}
/**
* @depends testCreateDatabase
* @param array $data
* @return void
* @throws \Exception
*/
public function testUpdateWithExistingRelationships(array $data): void
{
$databaseId = $data['databaseId'];
$table1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Collection1',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$table2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Collection2',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$table1 = $table1['body']['$id'];
$table2 = $table2['body']['$id'];
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $table1 . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => '49',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $table2 . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => '49',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $table1 . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $table2,
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => true,
'key' => 'collection2'
]);
sleep(1);
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $table1 . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Row 1',
'collection2' => [
[
'name' => 'Row 2',
],
],
],
]);
$update = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $table1 . '/rows/' . $row['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'data' => [
'name' => 'Row 1 Updated',
],
]);
$this->assertEquals(200, $update['headers']['status-code']);
}
/**
* @depends testCreateDatabase
*/
public function testInvalidRelationshipDocumentId(array $data): void
{
$databaseId = $data['databaseId'];
// Create parent table
$parentTable = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'ParentTable',
]);
$this->assertEquals(201, $parentTable['headers']['status-code']);
$parentTableId = $parentTable['body']['$id'];
// Create child table
$childTable = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'ChildTable',
]);
$this->assertEquals(201, $childTable['headers']['status-code']);
$childTableId = $childTable['body']['$id'];
// Add string column to parent
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 255,
'required' => false,
]);
// Add string column to child
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $childTableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'title',
'size' => 255,
'required' => false,
]);
// Create one-to-many relationship
$relationship = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/columns/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedTableId' => $childTableId,
'type' => Database::RELATION_ONE_TO_MANY,
'twoWay' => false,
'key' => 'children',
]);
$this->assertEquals(202, $relationship['headers']['status-code']);
// Wait for relationship column to be available
$this->assertEventually(function () use ($databaseId, $parentTableId) {
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/columns', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$columnKeys = array_column($columns['body']['columns'], 'key');
$this->assertContains('children', $columnKeys, "Relationship column 'children' not found in table {$parentTableId} of database {$databaseId}");
}, 2000, 200);
// ID too long (>36 chars) should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 1',
'children' => [
[
'$id' => 'this_id_is_way_too_long_and_should_fail_validation_check',
'title' => 'Child 1',
],
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// ID with invalid characters should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 2',
'children' => [
[
'$id' => 'invalid@id#with$special%chars',
'title' => 'Child 2',
],
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// ID starting with underscore should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 3',
'children' => [
[
'$id' => '_startsWithUnderscore',
'title' => 'Child 3',
],
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Valid ID should succeed
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 4',
'children' => [
[
'$id' => 'valid-id-123',
'title' => 'Child 4',
],
],
],
]);
$this->assertEquals(201, $response['headers']['status-code']);
$parentRowId = $response['body']['$id'];
// Update with invalid relationship ID should fail
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows/' . $parentRowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'data' => [
'children' => [
[
'$id' => 'another@invalid#id',
'title' => 'Child 5',
],
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Invalid string relation ID should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 6',
'children' => [
'invalid@string#id',
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Integer as relation value should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 7',
'children' => [
12345,
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// unique() as $id should succeed
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 8',
'children' => [
[
'$id' => 'unique()',
'title' => 'Child 8',
],
],
],
]);
$this->assertEquals(201, $response['headers']['status-code']);
// Empty string as $id should fail
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 9',
'children' => [
[
'$id' => '',
'title' => 'Child 9',
],
],
],
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Valid ID with allowed special chars (hyphen, period) should succeed
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $parentTableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Parent 10',
'children' => [
[
'$id' => 'valid.id-with_chars',
'title' => 'Child 10',
],
],
],
]);
$this->assertEquals(201, $response['headers']['status-code']);
}
/**
* @depends testCreateDatabase
*/
public function testTimeout(array $data): void
{
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $data['databaseId'] . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Slow Queries',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$data = [
'$id' => $table['body']['$id'],
'databaseId' => $table['body']['databaseId']
];
$longtext = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'longtext',
'size' => 100000000,
'required' => false,
'default' => null,
]);
$this->assertEquals($longtext['headers']['status-code'], 202);
for ($i = 0; $i < 10; $i++) {
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'longtext' => file_get_contents(__DIR__ . '../../../../../resources/longtext.txt'),
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
}
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-timeout' => 1,
], $this->getHeaders()), [
'queries' => [
Query::notEqual('longtext', 'appwrite')->toString(),
],
]);
$this->assertEquals(408, $response['headers']['status-code']);
}
/**
* @throws \Exception
*/
public function testIncrementColumn(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CounterDatabase'
]);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'CounterCollection',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Add integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'count',
'required' => true,
]);
\sleep(3);
// Create row with initial count = 5
$doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'count' => 5
],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $doc['headers']['status-code']);
$rowId = $doc['body']['$id'];
// Increment by default 1
$inc = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/count/increment", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(200, $inc['headers']['status-code']);
$this->assertEquals($tableId, $inc['body']['$tableId']);
$this->assertEquals($databaseId, $inc['body']['$databaseId']);
$this->assertEquals(6, $inc['body']['count']);
// Verify count = 6
$get = $this->client->call(Client::METHOD_GET, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(6, $get['body']['count']);
// Increment by custom value 4
$inc2 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/count/increment", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 4
]);
$this->assertEquals(200, $inc2['headers']['status-code']);
$this->assertEquals(10, $inc2['body']['count']);
$get2 = $this->client->call(Client::METHOD_GET, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(10, $get2['body']['count']);
// Test max limit exceeded
$err = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/count/increment", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['max' => 8]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test column not found
$notFound = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/unknown/increment", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(404, $notFound['headers']['status-code']);
// Test decrement with value 0
$inc3 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/count/increment", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 0
]);
$this->assertEquals(400, $inc3['headers']['status-code']);
}
public function testDecrementColumn(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CounterDatabase'
]);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'CounterCollection',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Add integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'count',
'required' => true,
]);
\sleep(2);
// Create row with initial count = 10
$doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => ['count' => 10],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
],
]);
$rowId = $doc['body']['$id'];
// Decrement by default 1 (count = 10 -> 9)
$dec = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(200, $dec['headers']['status-code']);
$this->assertEquals(9, $dec['body']['count']);
$this->assertEquals($tableId, $dec['body']['$tableId']);
$this->assertEquals($databaseId, $dec['body']['$databaseId']);
$get = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(9, $get['body']['count']);
// Decrement by custom value 3 (count 9 -> 6)
$dec2 = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 3
]);
$this->assertEquals(200, $dec2['headers']['status-code']);
$this->assertEquals(6, $dec2['body']['count']);
$get2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(6, $get2['body']['count']);
// Test min limit exceeded
$err = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['min' => 7]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test type error on non-numeric column
$typeErr = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['value' => 'not-a-number']);
$this->assertEquals(400, $typeErr['headers']['status-code']);
// Test decrement with value 0
$inc3 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/$databaseId/tables/$tableId/rows/$rowId/count/decrement", array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 0
]);
$this->assertEquals(400, $inc3['headers']['status-code']);
}
public function testSpatialPointColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Point Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Point Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create string column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
// Create point column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'location',
'required' => true,
]);
$this->assertEquals(202, $response['headers']['status-code']);
sleep(2);
// Create row with point column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Test Location',
'location' => [40.7128, -74.0060]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([40.7128, -74.0060], $response['body']['location']);
$rowId = $response['body']['$id'];
// Read row with point column
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7128, -74.0060], $response['body']['location']);
// Update row with new point coordinates
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'location' => [40.7589, -73.9851]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7589, -73.9851], $response['body']['location']);
// Upsert row with point column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Upserted Location',
'location' => [34.0522, -80]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([34.0522, -80], $response['body']['location']);
// Create row without permissions (should fail)
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'rowId' => ID::unique(),
'data' => [
'name' => 'Unauthorized Location',
'location' => [0, 0]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialLineColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Line Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Line Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'distance',
'required' => true,
]);
// Create line column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => true,
]);
sleep(2);
// Create row with line column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'distance' => 100,
'route' => [[40.7128, -74.0060], [40.7589, -73.9851]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851]], $response['body']['route']);
$rowId = $response['body']['$id'];
// Read row
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851]], $response['body']['route']);
// Update row
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'route' => [[40.7128, -74.0060], [40.7589, -73.9851], [40.7505, -73.9934]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[40.7128, -74.0060], [40.7589, -73.9851], [40.7505, -73.9934]], $response['body']['route']);
// Upsert row with line column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'distance' => 200,
'route' => [[34.0522, -80], [34.0736, -80]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[34.0522, -80], [34.0736, -80]], $response['body']['route']);
// Delete row
$response = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $response['headers']['status-code']);
// Verify row is deleted
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialPolygonColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Polygon Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Polygon Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create boolean column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/boolean', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'active',
'required' => true,
]);
// Create polygon column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => true,
]);
sleep(2);
// Create row with polygon column
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => true,
'area' => [[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]], $response['body']['area']);
$rowId = $response['body']['$id'];
// Read row
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7128, -74.0060]]], $response['body']['area']);
// Update row with new polygon
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'area' => [[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7505, -73.9934], [40.7128, -74.0060]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[40.7128, -74.0060], [40.7589, -74.0060], [40.7589, -73.9851], [40.7128, -73.9851], [40.7505, -73.9934], [40.7128, -74.0060]]], $response['body']['area']);
// Upsert row with polygon column
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . ID::unique(), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => false,
'area' => [[[34.0522, -80], [34.0736, -80], [34.0736, -80], [34.0522, -80], [34.0522, -80]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([[[34.0522, -80], [34.0736, -80], [34.0736, -80], [34.0522, -80], [34.0522, -80]]], $response['body']['area']);
// Create row missing required polygon (should fail)
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'active' => true
]
]);
$this->assertEquals(400, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialColumnsMixedTable(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Mixed Spatial Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with multiple spatial and non-spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Mixed Spatial Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create multiple columns
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'center',
'required' => true,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'boundary',
'required' => false,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'coverage',
'required' => true,
]);
sleep(3);
// Create row with all spatial columns
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Central Park',
'center' => [40.7829, -73.9654],
'boundary' => [[40.7649, -73.9814], [40.8009, -73.9494]],
'coverage' => [[[40.7649, -73.9814], [40.8009, -73.9814], [40.8009, -73.9494], [40.7649, -73.9494], [40.7649, -73.9814]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([40.7829, -73.9654], $response['body']['center']);
$this->assertEquals([[40.7649, -73.9814], [40.8009, -73.9494]], $response['body']['boundary']);
$this->assertEquals([[[40.7649, -73.9814], [40.8009, -73.9814], [40.8009, -73.9494], [40.7649, -73.9494], [40.7649, -73.9814]]], $response['body']['coverage']);
$rowId = $response['body']['$id'];
// Update row with new spatial data
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'center' => [40.7505, -73.9934],
'boundary' => [[40.7305, -74.0134], [40.7705, -73.9734]],
'coverage' => [[[40.7305, -74.0134], [40.7705, -74.0134], [40.7705, -73.9734], [40.7305, -73.9734], [40.7305, -74.0134]]]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([40.7505, -73.9934], $response['body']['center']);
$this->assertEquals([[40.7305, -74.0134], [40.7705, -73.9734]], $response['body']['boundary']);
$this->assertEquals([[[40.7305, -74.0134], [40.7705, -74.0134], [40.7705, -73.9734], [40.7305, -73.9734], [40.7305, -74.0134]]], $response['body']['coverage']);
// Create row with minimal required columns
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Minimal Location',
'center' => [0, 0],
'coverage' => [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([0, 0], $response['body']['center']);
// Permission validation - create without user context
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'rowId' => ID::unique(),
'data' => [
'name' => 'Unauthorized Location',
'center' => [0, 0],
'coverage' => [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialQuery(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Query Test Database'
]);
$this->assertNotEmpty($database['body']['$id']);
$databaseId = $database['body']['$id'];
// Create table with spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Spatial Query Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::delete(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create string column
$nameColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals(202, $nameColumn['headers']['status-code']);
// Create point column
$pointColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'pointAttr',
'required' => true,
]);
$this->assertEquals(202, $pointColumn['headers']['status-code']);
// Create line column
$lineColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'lineAttr',
'required' => true,
]);
$this->assertEquals(202, $lineColumn['headers']['status-code']);
// Create polygon column
$polygonColumn = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'polyAttr',
'required' => true,
]);
$this->assertEquals(202, $polygonColumn['headers']['status-code']);
// Wait for columns to be created
sleep(2);
// Create test rows with spatial data
$rows = [
[
'$id' => 'row1',
'name' => 'Test Row 1',
'pointAttr' => [6.0, 6.0],
'lineAttr' => [[1.0, 1.0], [1.1,1.1] , [2.0, 2.0]],
'polyAttr' => [[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]]]
],
[
'$id' => 'row2',
'name' => 'Test Row 2',
'pointAttr' => [7.0, 6.0],
'lineAttr' => [[10.0, 10.0], [20.0, 20.0]],
'polyAttr' => [[[20.0, 20.0], [30.0, 20.0], [30.0, 30.0], [20.0, 30.0], [20.0, 20.0]]]
],
[
'$id' => 'row3',
'name' => 'Test Row 3',
'pointAttr' => [25.0, 25.0],
'lineAttr' => [[25.0, 25.0], [35.0, 35.0]],
'polyAttr' => [[[40.0, 40.0], [50.0, 40.0], [50.0, 50.0], [40.0, 50.0], [40.0, 40.0]]]
]
];
foreach ($rows as $r) {
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => $r['$id'],
'data' => [
'name' => $r['name'],
'pointAttr' => $r['pointAttr'],
'lineAttr' => $r['lineAttr'],
'polyAttr' => $r['polyAttr']
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
}
// Equality on non-spatial column (name)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('name', ['Test Row 1'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['rows']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// Polygon column queries
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('polyAttr', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]]]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['rows']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// Not equal queries
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notEqual('pointAttr', [[6.0, 6.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['rows']);
// contains on line (point on line)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::contains('lineAttr', [[1.1, 1.1]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['rows']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// notContains on polygon (point outside all polygons)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notContains('polyAttr', [[15.0, 15.0]])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// intersects on polygon (point inside row1 polygon)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::intersects('polyAttr', [5.0, 5.0])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// notIntersects on polygon (point outside all polygons)
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notIntersects('polyAttr', [60.0, 60.0])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// overlaps on polygon (polygon overlapping row1)
$overlapPoly = [[[5.0, 5.0], [12.0, 5.0], [12.0, 12.0], [5.0, 12.0], [5.0, 5.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::overlaps('polyAttr', $overlapPoly)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// notOverlaps on polygon (polygon that overlaps none)
$noOverlapPoly = [[[60.0, 60.0], [70.0, 60.0], [70.0, 70.0], [60.0, 70.0], [60.0, 60.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notOverlaps('polyAttr', $noOverlapPoly)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// distance (equals) on point
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceEqual('pointAttr', [6.0, 6.0], 1.0)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row2', $response['body']['rows'][0]['$id']);
// notDistance (outside radius) on point
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceNotEqual('pointAttr', [6.0, 6.0], 1.0)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, $response['body']['total']);
// distanceGreaterThan
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceGreaterThan('pointAttr', [6.0, 6.0], 5.0)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
// distanceLessThan
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::distanceLessThan('pointAttr', [6.0, 6.0], 0.5)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
// crosses on line (query line crosses row1 line)
$crossLine = [[1.0, 2.0], [2.0, 1.0]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::crosses('lineAttr', $crossLine)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// notCrosses on line (query line does not cross any stored lines)
$nonCrossLine = [[0.0, 1.0], [0.0, 2.0]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notCrosses('lineAttr', $nonCrossLine)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// touches on polygon (query polygon touches row1 polygon at corner)
$touchPoly = [[[10.0, 10.0], [20.0, 10.0], [20.0, 20.0], [10.0, 20.0], [10.0, 10.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::touches('polyAttr', $touchPoly)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, $response['body']['total']);
$this->assertEquals('row1', $response['body']['rows'][0]['$id']);
// notTouches on polygon (polygon far away should not touch)
$farPoly = [[[60.0, 60.0], [70.0, 60.0], [70.0, 70.0], [60.0, 70.0], [60.0, 60.0]]];
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::notTouches('polyAttr', $farPoly)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(3, $response['body']['total']);
// Select specific columns
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::select(['name', 'pointAttr'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['rows']);
foreach ($response['body']['rows'] as $doc) {
$this->assertArrayHasKey('name', $doc);
$this->assertArrayHasKey('pointAttr', $doc);
$this->assertArrayNotHasKey('lineAttr', $doc);
$this->assertArrayNotHasKey('polyAttr', $doc);
}
// Order by name
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::orderAsc('name')->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(3, $response['body']['rows']);
$this->assertEquals('Test Row 1', $response['body']['rows'][0]['name']);
$this->assertEquals('Test Row 2', $response['body']['rows'][1]['name']);
$this->assertEquals('Test Row 3', $response['body']['rows'][2]['name']);
// Limit results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::limit(2)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['rows']);
// Offset results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::offset(1)->toString(), Query::limit(2)->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['rows']);
// Complex query with multiple conditions
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['name', 'pointAttr'])->toString(),
Query::orderAsc('name')->toString(),
Query::limit(1)->toString()
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['rows']);
$this->assertEquals('Test Row 1', $response['body']['rows'][0]['name']);
// Query with no results
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [Query::equal('name', ['Non-existent Row'])->toString()]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['rows']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialIndex(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Index Test DB'
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'SpatialIdx',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create spatial columns: one required, one optional
$reqPoint = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'pRequired',
'required' => true,
]);
$this->assertEquals(202, $reqPoint['headers']['status-code']);
$optPoint = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'pOptional',
'required' => false,
]);
$this->assertEquals(202, $optPoint['headers']['status-code']);
// Ensure columns are available
sleep(2);
// Create index on required spatial column (should succeed)
$okIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'idx_required_point',
'type' => Database::INDEX_SPATIAL,
'columns' => ['pRequired'],
]);
$this->assertEquals(202, $okIndex['headers']['status-code']);
// Create index on optional spatial column (should fail in case of mariadb)
$badIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'idx_optional_point',
'type' => Database::INDEX_SPATIAL,
'columns' => ['pOptional'],
]);
$this->assertEquals(400, $badIndex['headers']['status-code']);
// making it required to create index on it
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point/'.'pOptional', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'required' => true,
'default' => null
]);
$this->assertEquals(200, $updated['headers']['status-code']);
sleep(2);
$retriedIndex = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'idx_optional_point',
'type' => Database::INDEX_SPATIAL,
'columns' => ['pOptional'],
]);
$this->assertEquals(202, $retriedIndex['headers']['status-code']);
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testUpdateSpatialColumns(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Update Spatial Columns Test Database'
]);
$databaseId = $database['body']['$id'];
// Create table with spatial columns
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => ID::unique(),
'name' => 'Update Spatial Columns Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
],
]);
$tableId = $table['body']['$id'];
// Create string column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
// Create point column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'location',
'required' => true,
]);
// Create line column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => false,
]);
// Create polygon column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => true,
]);
sleep(2);
// Test 1: Update point column - change required status
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point/location', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'required' => false,
'default' => null,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(false, $response['body']['required']);
// Test 2: Update line column - change required status and add default value
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line/route', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'required' => false,
'default' => [[0, 0], [1, 1]],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(false, $response['body']['required']);
$this->assertEquals([[0, 0], [1, 1]], $response['body']['default']);
// Test 3: Update polygon column - change key name
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon/area', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'newKey' => 'coverage',
'default' => null,
'required' => false
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('coverage', $response['body']['key']);
// Test 4: Update point column - add default value
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point/location', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'default' => [0, 0],
'required' => false
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals([0, 0], $response['body']['default']);
// Test 5: Verify column updates by creating a row
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'name' => 'Test Location',
'coverage' => [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals([0, 0], $response['body']['location']); // Should use default value
$this->assertEquals([[0, 0], [1, 1]], $response['body']['route']); // Should use default value
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $tableId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialDistanceInMeter(): void
{
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
];
// Create database
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', $headers, [
'databaseId' => ID::unique(),
'name' => 'Spatial Distance Meters Database'
]);
$databaseId = $database['body']['$id'];
// Create table
$table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", $headers, [
'tableId' => ID::unique(),
'name' => 'Spatial Distance Meters Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$tableId = $table['body']['$id'];
// Create point column
$resp = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/point", $headers, [
'key' => 'loc',
'required' => true,
]);
$this->assertEquals(202, $resp['headers']['status-code']);
sleep(2);
// Create spatial index
$indexResp = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", $headers, [
'key' => 'idx_loc',
'type' => Database::INDEX_SPATIAL,
'columns' => ['loc'],
]);
$this->assertEquals(202, $indexResp['headers']['status-code']);
// Insert two points ~1km apart
$points = [
'p0' => [0.0000, 0.0000],
'p1' => [0.0090, 0.0000]
];
foreach ($points as $id => $loc) {
$rowResp = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", $headers, [
'rowId' => $id,
'data' => ['loc' => $loc]
]);
$this->assertEquals(201, $rowResp['headers']['status-code']);
}
// Queries
$queries = [
'within1_5km' => Query::distanceLessThan('loc', [0.0, 0.0], 1500, true),
'within500m' => Query::distanceLessThan('loc', [0.0, 0.0], 500, true),
'greater500m' => Query::distanceGreaterThan('loc', [0.0, 0.0], 500, true),
'equal0m' => Query::distanceEqual('loc', [0.0, 0.0], 0, true),
'notEqual0m' => Query::distanceNotEqual('loc', [0.0, 0.0], 0, true),
];
// Assertions
$results = [
'within1_5km' => 2,
'within500m' => 1,
'greater500m' => 1,
'equal0m' => 'p0',
'notEqual0m' => 'p1'
];
foreach ($queries as $key => $query) {
$resp = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", $headers, [
'queries' => [$query->toString()]
]);
$this->assertEquals(200, $resp['headers']['status-code']);
if (is_int($results[$key])) {
$this->assertCount($results[$key], $resp['body']['rows']);
} else {
$this->assertEquals($results[$key], $resp['body']['rows'][0]['$id']);
}
}
// Cleanup
$this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}", $headers);
$this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}", $headers);
}
public function testSpatialColCreateOnExistingData(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial Distance Meters Database'
]);
$databaseId = $database['body']['$id'];
$tableId = ID::unique();
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => $tableId,
'name' => 'spatial-test',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$description = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'description',
'size' => 512,
'required' => false,
'default' => '',
]);
$this->assertEquals(202, $description['headers']['status-code']);
sleep(2);
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'description' => 'description'
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row['headers']['status-code']);
$point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'loc',
'required' => true,
]);
$this->assertEquals(400, $point['headers']['status-code']);
$point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'loc',
'required' => false,
'default' => null
]);
$this->assertEquals(202, $point['headers']['status-code']);
$line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => true,
]);
$this->assertEquals(400, $line['headers']['status-code']);
$line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => false,
'default' => null
]);
$this->assertEquals(202, $line['headers']['status-code']);
$poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => true,
]);
$this->assertEquals(400, $poly['headers']['status-code']);
$poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => false,
'default' => null
]);
$this->assertEquals(202, $poly['headers']['status-code']);
}
public function testSpatialColCreateOnExistingDataWithDefaults(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'Spatial With Defaults Database'
]);
$databaseId = $database['body']['$id'];
$tableId = ID::unique();
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => $tableId,
'name' => 'spatial-test-defaults',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$description = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'description',
'size' => 512,
'required' => false,
'default' => '',
]);
$this->assertEquals(202, $description['headers']['status-code']);
sleep(2);
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'description' => 'description'
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $row['headers']['status-code']);
// Test point with default value
$point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'loc',
'required' => false,
'default' => [0.0, 0.0]
]);
$this->assertEquals(202, $point['headers']['status-code']);
// Test line with default value
$line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'route',
'required' => false,
'default' => [[0.0, 0.0], [1.0, 1.0]]
]);
$this->assertEquals(202, $line['headers']['status-code']);
// Test polygon with default value
$poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'area',
'required' => false,
'default' => [[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]]
]);
$this->assertEquals(202, $poly['headers']['status-code']);
// Wait for columns to be available
sleep(2);
// Create a new row without spatial data to test default values
$newRow = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'description' => 'test default values'
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $newRow['headers']['status-code']);
$newRowId = $newRow['body']['$id'];
// Fetch the row to verify default values are applied
$fetchedRow = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $newRowId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $fetchedRow['headers']['status-code']);
// Verify default values are applied
$this->assertEquals([0.0, 0.0], $fetchedRow['body']['loc']);
$this->assertEquals([[0.0, 0.0], [1.0, 1.0]], $fetchedRow['body']['route']);
$this->assertEquals([[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]], $fetchedRow['body']['area']);
}
}