Add spatial column validation during required mode and tests for existing data in databases

This commit is contained in:
ArnabChatterjee20k
2025-09-17 18:01:29 +05:30
parent 14062fc7c2
commit 4815780476
3 changed files with 272 additions and 5 deletions
@@ -366,13 +366,20 @@ abstract class Action extends UtopiaAction
'filters' => $filters,
'options' => $options,
]);
if (!$dbForProject->getAdapter()->getSupportForSpatialIndexNull() && in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) && $attribute->getAttribute('required')) {
$existingDataPresent = $dbForProject->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence());
if (count($existingDataPresent)) {
throw new StructureException('Failed to add required spatial column: existing rows present. Make the column optional.');
}
}
$dbForProject->checkAttribute($collection, $attribute);
$attribute = $dbForProject->createDocument('attributes', $attribute);
} catch (DuplicateException) {
throw new Exception($this->getDuplicateException());
} catch (LimitException) {
throw new Exception($this->getLimitException());
} catch (StructureException $e) {
throw new Exception($this->getInvalidStructureException(), $e->getMessage());
} catch (Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId);
$dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence());
@@ -7798,4 +7798,134 @@ trait DatabasesBase
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
public function testSpatialColCreateOnExistingData(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'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'];
$colId = ID::unique();
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => $colId,
'name' => 'spatial-test',
'documentSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $collection['headers']['status-code']);
$description = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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);
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 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, $document['headers']['status-code']);
$point = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/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']);
}
}
@@ -3062,7 +3062,7 @@ trait DatabasesBase
public function testInvalidRowStructure(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
$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']
@@ -3735,7 +3735,7 @@ trait DatabasesBase
public function testEnforceTableAndRowPermissions(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
$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']
@@ -3928,7 +3928,7 @@ trait DatabasesBase
public function testEnforceTablePermissions(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
$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']
@@ -4284,7 +4284,7 @@ trait DatabasesBase
public function testUpdatePermissionsWithEmptyPayload(): array
{
// Create Database
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
$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']
@@ -8828,4 +8828,134 @@ trait DatabasesBase
$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']);
}
}