mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 17:08:45 +00:00
Merge branch '1.8.x' into feat-update-preview-url-in-vcs-controller
This commit is contained in:
commit
14b278a9a6
60 changed files with 6217 additions and 85 deletions
|
|
@ -842,6 +842,12 @@ return [
|
|||
'code' => 400,
|
||||
],
|
||||
|
||||
Exception::ATTRIBUTE_TYPE_NOT_SUPPORTED => [
|
||||
'name' => Exception::ATTRIBUTE_TYPE_NOT_SUPPORTED,
|
||||
'description' => 'Attribute type is not supported.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Exists for both Attributes & Columns */
|
||||
Exception::RELATIONSHIP_VALUE_INVALID => [
|
||||
'name' => Exception::RELATIONSHIP_VALUE_INVALID,
|
||||
|
|
@ -900,6 +906,11 @@ return [
|
|||
'description' => "Existing data is too large for new size, truncate your existing data then try again.",
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::COLUMN_TYPE_NOT_SUPPORTED => [
|
||||
'name' => Exception::COLUMN_TYPE_NOT_SUPPORTED,
|
||||
'description' => 'Column type is not supported.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Indexes */
|
||||
Exception::INDEX_NOT_FOUND => [
|
||||
|
|
|
|||
|
|
@ -562,15 +562,18 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
logging: $resource->getAttribute('logging', true),
|
||||
requestTimeout: 30
|
||||
requestTimeout: 30,
|
||||
responseFormat: Executor::RESPONSE_FORMAT_ARRAY_HEADERS
|
||||
);
|
||||
|
||||
$headerOverrides = [];
|
||||
|
||||
// Branded 404 override
|
||||
$isResponseBranded = false;
|
||||
if ($executionResponse['statusCode'] === 404 && $deployment->getAttribute('adapter', '') === 'static') {
|
||||
$layout = new View(__DIR__ . '/../views/general/404.phtml');
|
||||
$executionResponse['body'] = $layout->render();
|
||||
$executionResponse['headers']['content-length'] = \strlen($executionResponse['body']);
|
||||
$headerOverrides['content-length'] = \strlen($executionResponse['body']);
|
||||
$isResponseBranded = true;
|
||||
}
|
||||
|
||||
|
|
@ -580,15 +583,16 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$transformation = new Transformation();
|
||||
$transformation->addAdapter(new Preview());
|
||||
$transformation->setInput($executionResponse['body']);
|
||||
$transformation->setTraits($executionResponse['headers']);
|
||||
|
||||
$simpleHeaders = [];
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
$simpleHeaders[$key] = \is_array($value) ? \implode(', ', $value) : $value;
|
||||
}
|
||||
|
||||
$transformation->setTraits($simpleHeaders);
|
||||
if ($isPreview && $transformation->transform()) {
|
||||
$executionResponse['body'] = $transformation->getOutput();
|
||||
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
}
|
||||
}
|
||||
$headerOverrides['content-length'] = \strlen($executionResponse['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -602,25 +606,33 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
->setParam('code', $executionResponse['statusCode']);
|
||||
|
||||
$executionResponse['body'] = $layout->render();
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
} elseif (\strtolower($key) === 'content-type') {
|
||||
$executionResponse['headers'][$key] = 'text/html';
|
||||
}
|
||||
}
|
||||
|
||||
$headerOverrides['content-length'] = \strlen($executionResponse['body']);
|
||||
$headerOverrides['content-type'] = 'text/html';
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('resourceType') === 'functions') {
|
||||
$executionResponse['headers']['x-appwrite-execution-id'] = $execution->getId();
|
||||
$headerOverrides['x-appwrite-execution-id'] = $execution->getId();
|
||||
} elseif ($deployment->getAttribute('resourceType') === 'sites') {
|
||||
$executionResponse['headers']['x-appwrite-log-id'] = $execution->getId();
|
||||
$headerOverrides['x-appwrite-log-id'] = $execution->getId();
|
||||
}
|
||||
|
||||
foreach ($headerOverrides as $key => $value) {
|
||||
if (\array_key_exists($key, $executionResponse['headers'])) {
|
||||
if (\is_array($executionResponse['headers'][$key])) {
|
||||
$executionResponse['headers'][$key][] = $value;
|
||||
} else {
|
||||
$executionResponse['headers'][$key] = [$executionResponse['headers'][$key], $value];
|
||||
}
|
||||
} else {
|
||||
$executionResponse['headers'][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$headersFiltered = [];
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) {
|
||||
$headersFiltered[] = ['name' => $key, 'value' => $value];
|
||||
$headersFiltered[] = ['name' => $key, 'value' => \is_array($value) ? \implode(', ', $value) : $value];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -687,7 +699,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
|
||||
$headers = [];
|
||||
foreach (($executionResponse['headers'] ?? []) as $key => $value) {
|
||||
$headers[] = ['name' => $key, 'value' => $value];
|
||||
$headers[] = ['name' => $key, 'value' => \is_array($value) ? \implode(', ', $value) : $value];
|
||||
}
|
||||
|
||||
$execution->setAttribute('responseBody', $executionResponse['body'] ?? '');
|
||||
|
|
@ -696,16 +708,17 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$body = $execution['responseBody'] ?? '';
|
||||
|
||||
$contentType = 'text/plain';
|
||||
foreach ($execution['responseHeaders'] as $header) {
|
||||
if (\strtolower($header['name']) === 'content-type') {
|
||||
$contentType = $header['value'];
|
||||
}
|
||||
|
||||
if (\strtolower($header['name']) === 'transfer-encoding') {
|
||||
foreach ($executionResponse['headers'] as $name => $values) {
|
||||
if (\strtolower($name) === 'content-type') {
|
||||
$contentType = \is_array($values) ? $values[0] : $values;
|
||||
continue;
|
||||
}
|
||||
|
||||
$response->addHeader(\strtolower($header['name']), $header['value']);
|
||||
if (\strtolower($name) === 'transfer-encoding') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$response->setHeader($name, $values);
|
||||
}
|
||||
|
||||
$response
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
|
|||
const APP_DATABASE_ATTRIBUTE_URL = 'url';
|
||||
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||
const APP_DATABASE_ATTRIBUTE_POINT = 'point';
|
||||
const APP_DATABASE_ATTRIBUTE_LINE = 'line';
|
||||
const APP_DATABASE_ATTRIBUTE_POLYGON = 'polygon';
|
||||
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
|
||||
|
|
|
|||
169
composer.lock
generated
169
composer.lock
generated
|
|
@ -2599,16 +2599,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "1c064a0c67749923483216b081066642751cc2c7"
|
||||
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7",
|
||||
"reference": "1c064a0c67749923483216b081066642751cc2c7",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
|
||||
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2616,6 +2616,7 @@
|
|||
"psr/log": "^1|^2|^3",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
|
||||
"symfony/polyfill-php83": "^1.29",
|
||||
"symfony/service-contracts": "^2.5|^3"
|
||||
},
|
||||
"conflict": {
|
||||
|
|
@ -2674,7 +2675,7 @@
|
|||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2694,7 +2695,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T11:36:08+00:00"
|
||||
"time": "2025-08-27T07:45:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
|
|
@ -2939,6 +2940,86 @@
|
|||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php83",
|
||||
"version": "v1.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php83.git",
|
||||
"reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5",
|
||||
"reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php83\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-08T02:45:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.6.0",
|
||||
|
|
@ -3557,16 +3638,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "8a536fead840d9da6ee819fe6b80e0f047997f69"
|
||||
"reference": "06ffa2b1c977f5451200a1ee82a500be1390a789"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/8a536fead840d9da6ee819fe6b80e0f047997f69",
|
||||
"reference": "8a536fead840d9da6ee819fe6b80e0f047997f69",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/06ffa2b1c977f5451200a1ee82a500be1390a789",
|
||||
"reference": "06ffa2b1c977f5451200a1ee82a500be1390a789",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3607,9 +3688,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/1.2.3"
|
||||
"source": "https://github.com/utopia-php/database/tree/1.3.0"
|
||||
},
|
||||
"time": "2025-08-27T11:47:04+00:00"
|
||||
"time": "2025-09-02T16:20:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
|
|
@ -4109,16 +4190,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "0e4499d9dd2c90c2be188cc5fb7a32d9a892b569"
|
||||
"reference": "38171023efd3abe650d2abc5ac65f5df52311da6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/0e4499d9dd2c90c2be188cc5fb7a32d9a892b569",
|
||||
"reference": "0e4499d9dd2c90c2be188cc5fb7a32d9a892b569",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/38171023efd3abe650d2abc5ac65f5df52311da6",
|
||||
"reference": "38171023efd3abe650d2abc5ac65f5df52311da6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4159,9 +4240,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.0.0"
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.0.1"
|
||||
},
|
||||
"time": "2025-08-13T09:15:53+00:00"
|
||||
"time": "2025-08-28T13:41:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
|
|
@ -7410,16 +7491,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
|
||||
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7484,7 +7565,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7504,7 +7585,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-30T17:13:41+00:00"
|
||||
"time": "2025-08-25T06:35:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
|
|
@ -7646,16 +7727,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
|
||||
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
|
||||
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7693,7 +7774,7 @@
|
|||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7713,7 +7794,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T11:36:08+00:00"
|
||||
"time": "2025-08-05T10:16:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
|
@ -8047,16 +8128,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
|
||||
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8088,7 +8169,7 @@
|
|||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/process/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8099,25 +8180,29 @@
|
|||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-17T09:11:12+00:00"
|
||||
"time": "2025-08-18T09:42:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
|
||||
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8175,7 +8260,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8195,7 +8280,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
"time": "2025-08-25T06:35:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "textalk/websocket",
|
||||
|
|
|
|||
|
|
@ -966,7 +966,7 @@ services:
|
|||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.8.1
|
||||
image: openruntimes/executor:0.11.0
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
|
|
|||
1
docs/references/databases/create-line-attribute.md
Normal file
1
docs/references/databases/create-line-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric line attribute.
|
||||
1
docs/references/databases/create-point-attribute.md
Normal file
1
docs/references/databases/create-point-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric 2d point attribute.
|
||||
1
docs/references/databases/create-polygon-attribute.md
Normal file
1
docs/references/databases/create-polygon-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric polygon attribute.
|
||||
1
docs/references/databases/update-line-attribute.md
Normal file
1
docs/references/databases/update-line-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a line attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-point-attribute.md
Normal file
1
docs/references/databases/update-point-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a point attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-polygon-attribute.md
Normal file
1
docs/references/databases/update-polygon-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a polygon attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/tablesdb/create-line-column.md
Normal file
1
docs/references/tablesdb/create-line-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric line attribute.
|
||||
1
docs/references/tablesdb/create-point-column.md
Normal file
1
docs/references/tablesdb/create-point-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric point attribute.
|
||||
1
docs/references/tablesdb/create-polygon-column.md
Normal file
1
docs/references/tablesdb/create-polygon-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a geometric polygon attribute.
|
||||
1
docs/references/tablesdb/update-line-column.md
Normal file
1
docs/references/tablesdb/update-line-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a line column. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/tablesdb/update-point-column.md
Normal file
1
docs/references/tablesdb/update-point-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a point column. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/tablesdb/update-polygon-column.md
Normal file
1
docs/references/tablesdb/update-polygon-column.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a polygon column. Changing the `default` value will not update already existing documents.
|
||||
|
|
@ -235,6 +235,8 @@ class Exception extends \Exception
|
|||
public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid';
|
||||
public const ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize';
|
||||
|
||||
public const ATTRIBUTE_TYPE_NOT_SUPPORTED = 'ATTRIBUTE_TYPE_NOT_SUPPORTED';
|
||||
|
||||
/** Columns */
|
||||
public const COLUMN_NOT_FOUND = 'column_not_found';
|
||||
public const COLUMN_UNKNOWN = 'column_unknown';
|
||||
|
|
@ -247,6 +249,8 @@ class Exception extends \Exception
|
|||
public const COLUMN_TYPE_INVALID = 'column_type_invalid';
|
||||
public const COLUMN_INVALID_RESIZE = 'column_invalid_resize';
|
||||
|
||||
public const COLUMN_TYPE_NOT_SUPPORTED = 'COLUMN_TYPE_NOT_SUPPORTED';
|
||||
|
||||
/** Relationship */
|
||||
public const RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid';
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class Mapper
|
|||
'json' => Types::json(),
|
||||
'none' => Types::json(),
|
||||
'any' => Types::json(),
|
||||
'spatial' => Types::json(),
|
||||
];
|
||||
|
||||
foreach ($defaults as $type => $default) {
|
||||
|
|
@ -456,6 +457,9 @@ class Mapper
|
|||
'boolean' => static::model("{$prefix}Boolean"),
|
||||
'datetime' => static::model("{$prefix}Datetime"),
|
||||
'relationship' => static::model("{$prefix}Relationship"),
|
||||
'point' => static::model("{$prefix}Point"),
|
||||
'linestring' => static::model("{$prefix}Line"),
|
||||
'polygon' => static::model("{$prefix}Polygon"),
|
||||
default => throw new Exception('Unknown ' . strtolower($prefix) . ' implementation'),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,6 +209,14 @@ abstract class Action extends UtopiaAction
|
|||
: Exception::COLUMN_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the exception for spatial type attribute not supported by the database adapter
|
||||
*/
|
||||
protected function getSpatialTypeNotSupportedException(): string
|
||||
{
|
||||
return $this->isCollectionsAPI() ? Exception::ATTRIBUTE_TYPE_NOT_SUPPORTED : Exception::COLUMN_TYPE_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct collections context for Events queue.
|
||||
*/
|
||||
|
|
@ -245,6 +253,18 @@ abstract class Action extends UtopiaAction
|
|||
? UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP
|
||||
: UtopiaResponse::MODEL_COLUMN_RELATIONSHIP,
|
||||
|
||||
Database::VAR_POINT => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_POINT
|
||||
: UtopiaResponse::MODEL_COLUMN_POINT,
|
||||
|
||||
Database::VAR_LINESTRING => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_LINE
|
||||
: UtopiaResponse::MODEL_COLUMN_LINE,
|
||||
|
||||
Database::VAR_POLYGON => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_POLYGON
|
||||
: UtopiaResponse::MODEL_COLUMN_POLYGON,
|
||||
|
||||
Database::VAR_STRING => match ($format) {
|
||||
APP_DATABASE_ATTRIBUTE_EMAIL => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_EMAIL
|
||||
|
|
@ -286,6 +306,10 @@ abstract class Action extends UtopiaAction
|
|||
$default = $attribute->getAttribute('default');
|
||||
$options = $attribute->getAttribute('options', []);
|
||||
|
||||
if (in_array($type, Database::SPATIAL_TYPES) && !$dbForProject->getAdapter()->getSupportForSpatialAttributes()) {
|
||||
throw new Exception($this->getSpatialTypeNotSupportedException());
|
||||
}
|
||||
|
||||
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($db->isEmpty()) {
|
||||
|
|
@ -434,7 +458,7 @@ abstract class Action extends UtopiaAction
|
|||
return $attribute;
|
||||
}
|
||||
|
||||
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
|
||||
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
|
||||
{
|
||||
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line;
|
||||
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createLineAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_LINE;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/line')
|
||||
->desc('Create line attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/create-line-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.createLineColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
|
||||
'key' => $key,
|
||||
'type' => Database::VAR_LINESTRING,
|
||||
'required' => $required,
|
||||
'default' => $default
|
||||
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updateLineAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_LINE;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/line/:key')
|
||||
->desc('Update line attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/update-line-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.updateLineColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
key: $key,
|
||||
dbForProject: $dbForProject,
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_LINESTRING,
|
||||
default: $default,
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point;
|
||||
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createPointAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_POINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/point')
|
||||
->desc('Create point attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/create-point-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.createPointColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
|
||||
'key' => $key,
|
||||
'type' => Database::VAR_POINT,
|
||||
'required' => $required,
|
||||
'default' => $default,
|
||||
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updatePointAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_POINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/point/:key')
|
||||
->desc('Update point attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/update-point-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.updatePointColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
key: $key,
|
||||
dbForProject: $dbForProject,
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_POINT,
|
||||
default: $default,
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon;
|
||||
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createPolygonAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_POLYGON;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/polygon')
|
||||
->desc('Create polygon attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/create-polygon-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.createPolygonColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
|
||||
'key' => $key,
|
||||
'type' => Database::VAR_POLYGON,
|
||||
'required' => $required,
|
||||
'default' => $default,
|
||||
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updatePolygonAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_POLYGON;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/polygon/:key')
|
||||
->desc('Update polygon attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/update-polygon-attribute.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.updatePolygonColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for attribute when not provided, as JSON string. Cannot be set when attribute is required.', true)
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
|
||||
{
|
||||
$default = \is_string($default) ? \json_decode($default, true) : $default;
|
||||
|
||||
$attribute = $this->updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
key: $key,
|
||||
dbForProject: $dbForProject,
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_POLYGON,
|
||||
default: $default,
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||
|
|
@ -74,11 +75,15 @@ class Upsert extends Action
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void
|
||||
public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void
|
||||
{
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
if ($database->isEmpty()) {
|
||||
|
|
@ -141,5 +146,16 @@ class Upsert extends Action
|
|||
'total' => $modified,
|
||||
$this->getSdkGroup() => $upserted
|
||||
]), $this->getResponseModel());
|
||||
|
||||
$this->triggerBulk(
|
||||
'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert',
|
||||
$database,
|
||||
$collection,
|
||||
$upserted,
|
||||
$queueForEvents,
|
||||
$queueForRealtime,
|
||||
$queueForFunctions,
|
||||
$queueForWebhooks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class Create extends Action
|
|||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', null, new Key(), 'Index Key.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.')
|
||||
->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.')
|
||||
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true)
|
||||
->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional: true)
|
||||
|
|
@ -190,11 +190,21 @@ class Create extends Action
|
|||
'orders' => $orders,
|
||||
]);
|
||||
|
||||
$maxIndexLength = $dbForProject->getAdapter()->getMaxIndexLength();
|
||||
$internalIndexesKeys = $dbForProject->getAdapter()->getInternalIndexesKeys();
|
||||
$supportForIndexArray = $dbForProject->getAdapter()->getSupportForIndexArray();
|
||||
$supportForSpatialAttributes = $dbForProject->getAdapter()->getSupportForSpatialAttributes();
|
||||
$supportForSpatialIndexNull = $dbForProject->getAdapter()->getSupportForSpatialIndexNull();
|
||||
$supportForSpatialIndexOrder = $dbForProject->getAdapter()->getSupportForSpatialIndexOrder();
|
||||
|
||||
$validator = new IndexValidator(
|
||||
$collection->getAttribute('attributes'),
|
||||
$dbForProject->getAdapter()->getMaxIndexLength(),
|
||||
$dbForProject->getAdapter()->getInternalIndexesKeys(),
|
||||
$dbForProject->getAdapter()->getSupportForIndexArray()
|
||||
$maxIndexLength,
|
||||
$internalIndexesKeys,
|
||||
$supportForIndexArray,
|
||||
$supportForSpatialAttributes,
|
||||
$supportForSpatialIndexNull,
|
||||
$supportForSpatialIndexOrder
|
||||
);
|
||||
|
||||
if (!$validator->isValid($index)) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Line;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line\Create as LineCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends LineCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createLineColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_LINE;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/line')
|
||||
->desc('Create line column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'column.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/create-line-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Line;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line\Update as LineUpdate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends LineUpdate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updateLineColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_LINE;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/line/:key')
|
||||
->desc('Update line column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
|
||||
->label('audits.event', 'column.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/update-line-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->param('newKey', null, new Key(), 'New Column Key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Point;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point\Create as PointCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends PointCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createPointColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_POINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/point')
|
||||
->desc('Create point column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'column.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/create-point-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Point;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point\Update as PointUpdate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends PointUpdate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updatePointColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_POINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/point/:key')
|
||||
->desc('Update point column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
|
||||
->label('audits.event', 'column.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/update-point-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->param('newKey', null, new Key(), 'New Column Key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Polygon;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon\Create as PolygonCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends PolygonCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createPolygonColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_POLYGON;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/polygon')
|
||||
->desc('Create polygon column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'column.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/create-polygon-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Polygon;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon\Update as PolygonUpdate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Spatial;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends PolygonUpdate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updatePolygonColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_POLYGON;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/polygon/:key')
|
||||
->desc('Update polygon column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', ['tables.write', 'collections.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
|
||||
->label('audits.event', 'column.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/tablesdb/update-polygon-column.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', '', new Key(), 'Column Key.')
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for column when not provided, as JSON string. Cannot be set when column is required.', true)
|
||||
->param('newKey', null, new Key(), 'New Column Key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ class Create extends IndexCreate
|
|||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).')
|
||||
->param('key', null, new Key(), 'Index Key.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.')
|
||||
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.')
|
||||
->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.')
|
||||
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true)
|
||||
->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional: true)
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ class Upsert extends DocumentsUpsert
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\In
|
|||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Integer\Update as UpdateIntegerAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Create as CreateIPAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\IP\Update as UpdateIPAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line\Create as CreateLineAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Line\Update as UpdateLineAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point\Create as CreatePointAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Point\Update as UpdatePointAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon\Create as CreatePolygonAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Polygon\Update as UpdatePolygonAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Create as CreateRelationshipAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Relationship\Update as UpdateRelationshipAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\String\Create as CreateStringAttribute;
|
||||
|
|
@ -132,6 +138,18 @@ class Collections extends Base
|
|||
$service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute());
|
||||
$service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute());
|
||||
|
||||
// Attribute: Line
|
||||
$service->addAction(CreateLineAttribute::getName(), new CreateLineAttribute());
|
||||
$service->addAction(UpdateLineAttribute::getName(), new UpdateLineAttribute());
|
||||
|
||||
// Attribute: Point
|
||||
$service->addAction(CreatePointAttribute::getName(), new CreatePointAttribute());
|
||||
$service->addAction(UpdatePointAttribute::getName(), new UpdatePointAttribute());
|
||||
|
||||
// Attribute: Polygon
|
||||
$service->addAction(CreatePolygonAttribute::getName(), new CreatePolygonAttribute());
|
||||
$service->addAction(UpdatePolygonAttribute::getName(), new UpdatePolygonAttribute());
|
||||
|
||||
// Attribute: Relationship
|
||||
$service->addAction(CreateRelationshipAttribute::getName(), new CreateRelationshipAttribute());
|
||||
$service->addAction(UpdateRelationshipAttribute::getName(), new UpdateRelationshipAttribute());
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Integer\Cre
|
|||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Integer\Update as UpdateInteger;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\IP\Create as CreateIP;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\IP\Update as UpdateIP;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Line\Create as CreateLine;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Line\Update as UpdateLine;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Point\Create as CreatePoint;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Point\Update as UpdatePoint;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Polygon\Create as CreatePolygon;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Polygon\Update as UpdatePolygon;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Relationship\Create as CreateRelationship;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Relationship\Update as UpdateRelationship;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\String\Create as CreateString;
|
||||
|
|
@ -134,6 +140,18 @@ class Tables extends Base
|
|||
$service->addAction(CreateIP::getName(), new CreateIP());
|
||||
$service->addAction(UpdateIP::getName(), new UpdateIP());
|
||||
|
||||
// Column: Line
|
||||
$service->addAction(CreateLine::getName(), new CreateLine());
|
||||
$service->addAction(UpdateLine::getName(), new UpdateLine());
|
||||
|
||||
// Column: Point
|
||||
$service->addAction(CreatePoint::getName(), new CreatePoint());
|
||||
$service->addAction(UpdatePoint::getName(), new UpdatePoint());
|
||||
|
||||
// Column: Polygon
|
||||
$service->addAction(CreatePolygon::getName(), new CreatePolygon());
|
||||
$service->addAction(UpdatePolygon::getName(), new UpdatePolygon());
|
||||
|
||||
// Column: Relationship
|
||||
$service->addAction(CreateRelationship::getName(), new CreateRelationship());
|
||||
$service->addAction(UpdateRelationship::getName(), new UpdateRelationship());
|
||||
|
|
|
|||
42
src/Appwrite/Utopia/Database/Validator/Spatial.php
Normal file
42
src/Appwrite/Utopia/Database/Validator/Spatial.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Utopia\Database\Validator\Spatial as SpatialValidator;
|
||||
use Utopia\Validator\JSON;
|
||||
|
||||
class Spatial extends JSON
|
||||
{
|
||||
private string $spatialAttributeType;
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Value must be a valid spatial type JSON string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $spatialAttributeType
|
||||
*/
|
||||
public function __construct(string $spatialAttributeType)
|
||||
{
|
||||
$this->spatialAttributeType = $spatialAttributeType;
|
||||
}
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if valid or false if not.
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!parent::isValid($value)) {
|
||||
return false;
|
||||
}
|
||||
$value = \json_decode($value, true);
|
||||
$validator = new SpatialValidator($this->spatialAttributeType);
|
||||
return $validator->isValid($value);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,10 @@ use Appwrite\Utopia\Response\Model\AttributeEnum;
|
|||
use Appwrite\Utopia\Response\Model\AttributeFloat;
|
||||
use Appwrite\Utopia\Response\Model\AttributeInteger;
|
||||
use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||
use Appwrite\Utopia\Response\Model\AttributeLine;
|
||||
use Appwrite\Utopia\Response\Model\AttributeList;
|
||||
use Appwrite\Utopia\Response\Model\AttributePoint;
|
||||
use Appwrite\Utopia\Response\Model\AttributePolygon;
|
||||
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
||||
use Appwrite\Utopia\Response\Model\AttributeString;
|
||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||
|
|
@ -41,7 +44,10 @@ use Appwrite\Utopia\Response\Model\ColumnFloat;
|
|||
use Appwrite\Utopia\Response\Model\ColumnIndex;
|
||||
use Appwrite\Utopia\Response\Model\ColumnInteger;
|
||||
use Appwrite\Utopia\Response\Model\ColumnIP;
|
||||
use Appwrite\Utopia\Response\Model\ColumnLine;
|
||||
use Appwrite\Utopia\Response\Model\ColumnList;
|
||||
use Appwrite\Utopia\Response\Model\ColumnPoint;
|
||||
use Appwrite\Utopia\Response\Model\ColumnPolygon;
|
||||
use Appwrite\Utopia\Response\Model\ColumnRelationship;
|
||||
use Appwrite\Utopia\Response\Model\ColumnString;
|
||||
use Appwrite\Utopia\Response\Model\ColumnURL;
|
||||
|
|
@ -203,6 +209,9 @@ class Response extends SwooleResponse
|
|||
public const MODEL_ATTRIBUTE_URL = 'attributeUrl';
|
||||
public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime';
|
||||
public const MODEL_ATTRIBUTE_RELATIONSHIP = 'attributeRelationship';
|
||||
public const MODEL_ATTRIBUTE_POINT = 'attributePoint';
|
||||
public const MODEL_ATTRIBUTE_LINE = 'attributeLine';
|
||||
public const MODEL_ATTRIBUTE_POLYGON = 'attributePolygon';
|
||||
|
||||
// Database Columns
|
||||
public const MODEL_COLUMN = 'column';
|
||||
|
|
@ -217,6 +226,9 @@ class Response extends SwooleResponse
|
|||
public const MODEL_COLUMN_URL = 'columnUrl';
|
||||
public const MODEL_COLUMN_DATETIME = 'columnDatetime';
|
||||
public const MODEL_COLUMN_RELATIONSHIP = 'columnRelationship';
|
||||
public const MODEL_COLUMN_POINT = 'columnPoint';
|
||||
public const MODEL_COLUMN_LINE = 'columnLine';
|
||||
public const MODEL_COLUMN_POLYGON = 'columnPolygon';
|
||||
|
||||
// Users
|
||||
public const MODEL_ACCOUNT = 'account';
|
||||
|
|
@ -399,6 +411,8 @@ class Response extends SwooleResponse
|
|||
*/
|
||||
protected static bool $showSensitive = false;
|
||||
|
||||
protected SwooleHTTPResponse $swoole;
|
||||
|
||||
/**
|
||||
* Response constructor.
|
||||
*
|
||||
|
|
@ -406,6 +420,8 @@ class Response extends SwooleResponse
|
|||
*/
|
||||
public function __construct(SwooleHTTPResponse $response)
|
||||
{
|
||||
$this->swoole = $response;
|
||||
|
||||
$this
|
||||
// General
|
||||
->setModel(new None())
|
||||
|
|
@ -482,6 +498,9 @@ class Response extends SwooleResponse
|
|||
->setModel(new AttributeURL())
|
||||
->setModel(new AttributeDatetime())
|
||||
->setModel(new AttributeRelationship())
|
||||
->setModel(new AttributePoint())
|
||||
->setModel(new AttributeLine())
|
||||
->setModel(new AttributePolygon())
|
||||
// Table API Models
|
||||
->setModel(new Table())
|
||||
->setModel(new Column())
|
||||
|
|
@ -496,6 +515,9 @@ class Response extends SwooleResponse
|
|||
->setModel(new ColumnURL())
|
||||
->setModel(new ColumnDatetime())
|
||||
->setModel(new ColumnRelationship())
|
||||
->setModel(new ColumnPoint())
|
||||
->setModel(new ColumnLine())
|
||||
->setModel(new ColumnPolygon())
|
||||
->setModel(new Index())
|
||||
->setModel(new ColumnIndex())
|
||||
->setModel(new Row())
|
||||
|
|
@ -937,12 +959,18 @@ class Response extends SwooleResponse
|
|||
* Set Header
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param string|array<string> $value
|
||||
* @return void
|
||||
*/
|
||||
public function setHeader(string $key, string $value): void
|
||||
public function setHeader(string $key, mixed $value): void
|
||||
{
|
||||
$this->sendHeader($key, $value);
|
||||
if (is_array($value)) {
|
||||
// Temporary solution to support proxying Set-cookie (2 cookies, 1 name)
|
||||
// Ideally this would live in http library, supporting array of values in all adapters
|
||||
$this->swoole->header($key, $value);
|
||||
} else {
|
||||
$this->sendHeader($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ abstract class Model
|
|||
public const TYPE_DATETIME_EXAMPLE = '2020-10-15T06:38:00.000+00:00';
|
||||
public const TYPE_RELATIONSHIP = 'relationship';
|
||||
public const TYPE_PAYLOAD = 'payload';
|
||||
public const TYPE_SPATIAL = 'spatial';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
|
|||
43
src/Appwrite/Utopia/Response/Model/AttributeLine.php
Normal file
43
src/Appwrite/Utopia/Response/Model/AttributeLine.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class AttributeLine extends Attribute
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[[0, 0], [1, 1]]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributeLine';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_LINE;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,9 @@ class AttributeList extends Model
|
|||
Response::MODEL_ATTRIBUTE_IP,
|
||||
Response::MODEL_ATTRIBUTE_DATETIME,
|
||||
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
|
||||
Response::MODEL_ATTRIBUTE_POINT,
|
||||
Response::MODEL_ATTRIBUTE_LINE,
|
||||
Response::MODEL_ATTRIBUTE_POLYGON,
|
||||
Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'List of attributes.',
|
||||
|
|
|
|||
43
src/Appwrite/Utopia/Response/Model/AttributePoint.php
Normal file
43
src/Appwrite/Utopia/Response/Model/AttributePoint.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class AttributePoint extends Attribute
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[0, 0]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributePoint';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_POINT;
|
||||
}
|
||||
}
|
||||
43
src/Appwrite/Utopia/Response/Model/AttributePolygon.php
Normal file
43
src/Appwrite/Utopia/Response/Model/AttributePolygon.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class AttributePolygon extends Attribute
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[[[0, 0], [0, 10]], [[10, 10], [0, 0]]]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributePolygon';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_POLYGON;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,6 +70,9 @@ class Collection extends Model
|
|||
Response::MODEL_ATTRIBUTE_IP,
|
||||
Response::MODEL_ATTRIBUTE_DATETIME,
|
||||
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
|
||||
Response::MODEL_ATTRIBUTE_POINT,
|
||||
Response::MODEL_ATTRIBUTE_LINE,
|
||||
Response::MODEL_ATTRIBUTE_POLYGON,
|
||||
Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'Collection attributes.',
|
||||
|
|
|
|||
43
src/Appwrite/Utopia/Response/Model/ColumnLine.php
Normal file
43
src/Appwrite/Utopia/Response/Model/ColumnLine.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class ColumnLine extends Column
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for column when not provided. Cannot be set when column is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[[0, 0], [1, 1]]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'ColumnLine';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_COLUMN_LINE;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,9 @@ class ColumnList extends Model
|
|||
Response::MODEL_COLUMN_IP,
|
||||
Response::MODEL_COLUMN_DATETIME,
|
||||
Response::MODEL_COLUMN_RELATIONSHIP,
|
||||
Response::MODEL_COLUMN_POINT,
|
||||
Response::MODEL_COLUMN_LINE,
|
||||
Response::MODEL_COLUMN_POLYGON,
|
||||
Response::MODEL_COLUMN_STRING // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'List of columns.',
|
||||
|
|
|
|||
43
src/Appwrite/Utopia/Response/Model/ColumnPoint.php
Normal file
43
src/Appwrite/Utopia/Response/Model/ColumnPoint.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class ColumnPoint extends Column
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for column when not provided. Cannot be set when column is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[0, 0]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'ColumnPoint';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_COLUMN_POINT;
|
||||
}
|
||||
}
|
||||
43
src/Appwrite/Utopia/Response/Model/ColumnPolygon.php
Normal file
43
src/Appwrite/Utopia/Response/Model/ColumnPolygon.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class ColumnPolygon extends Column
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_SPATIAL,
|
||||
'description' => 'Default value for column when not provided. Cannot be set when column is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => '[[[0, 0], [0, 10]], [[10, 10], [0, 0]]]'
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'ColumnPolygon';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_COLUMN_POLYGON;
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,9 @@ class Table extends Model
|
|||
Response::MODEL_COLUMN_IP,
|
||||
Response::MODEL_COLUMN_DATETIME,
|
||||
Response::MODEL_COLUMN_RELATIONSHIP,
|
||||
Response::MODEL_COLUMN_POINT,
|
||||
Response::MODEL_COLUMN_LINE,
|
||||
Response::MODEL_COLUMN_POLYGON,
|
||||
Response::MODEL_COLUMN_STRING, // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'Table columns.',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ use Utopia\System\System;
|
|||
|
||||
class Executor
|
||||
{
|
||||
// 0.8.6 is last version with object-based headers
|
||||
public const RESPONSE_FORMAT_OBJECT_HEADERS = '0.10.0';
|
||||
|
||||
// 0.9.0 is first version with array-based headers
|
||||
public const RESPONSE_FORMAT_ARRAY_HEADERS = '0.11.0';
|
||||
|
||||
public const METHOD_GET = 'GET';
|
||||
public const METHOD_POST = 'POST';
|
||||
public const METHOD_PUT = 'PUT';
|
||||
|
|
@ -170,6 +176,7 @@ class Executor
|
|||
* @param string $entrypoint
|
||||
* @param string $runtimeEntrypoint
|
||||
* @param bool $logging
|
||||
* @param string $responseFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -190,7 +197,8 @@ class Executor
|
|||
int $memory,
|
||||
bool $logging,
|
||||
string $runtimeEntrypoint = '',
|
||||
?int $requestTimeout = null
|
||||
?int $requestTimeout = null,
|
||||
string $responseFormat = self::RESPONSE_FORMAT_OBJECT_HEADERS
|
||||
) {
|
||||
if (empty($headers['host'])) {
|
||||
$headers['host'] = System::getEnv('_APP_DOMAIN', '');
|
||||
|
|
@ -232,7 +240,7 @@ class Executor
|
|||
$requestTimeout = $timeout + 15;
|
||||
}
|
||||
|
||||
$response = $this->call($this->endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout);
|
||||
$response = $this->call($this->endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data', 'x-executor-response-format' => $responseFormat ], $params, true, $requestTimeout);
|
||||
|
||||
$status = $response['headers']['status-code'];
|
||||
if ($status >= 400) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6172,4 +6172,561 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
}
|
||||
|
||||
public function testSpatialBulkOperations(): void
|
||||
{
|
||||
// Create database
|
||||
$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 Bulk Operations Test Database'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($database['body']['$id']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection with spatial attributes
|
||||
$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' => ID::unique(),
|
||||
'name' => 'Spatial Bulk Operations Collection',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create string attribute
|
||||
$nameAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, $nameAttribute['headers']['status-code']);
|
||||
|
||||
// Create point attribute
|
||||
$pointAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, $pointAttribute['headers']['status-code']);
|
||||
|
||||
// Create polygon attribute
|
||||
$polygonAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/polygon', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'area',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $polygonAttribute['headers']['status-code']);
|
||||
|
||||
// Wait for attributes to be created
|
||||
sleep(2);
|
||||
|
||||
// Test 1: Bulk create with spatial data
|
||||
$spatialDocuments = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$spatialDocuments[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Location ' . $i,
|
||||
'location' => [10.0 + $i, 20.0 + $i], // POINT
|
||||
'area' => [
|
||||
[10.0 + $i, 20.0 + $i],
|
||||
[11.0 + $i, 20.0 + $i],
|
||||
[11.0 + $i, 21.0 + $i],
|
||||
[10.0 + $i, 21.0 + $i],
|
||||
[10.0 + $i, 20.0 + $i]
|
||||
] // POLYGON
|
||||
];
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => $spatialDocuments,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertCount(5, $response['body']['documents']);
|
||||
|
||||
// Verify created documents have proper spatial data
|
||||
foreach ($response['body']['documents'] as $index => $document) {
|
||||
$this->assertNotEmpty($document['$id']);
|
||||
$this->assertNotEmpty($document['name']);
|
||||
$this->assertIsArray($document['location']);
|
||||
$this->assertIsArray($document['area']);
|
||||
$this->assertCount(2, $document['location']); // POINT has 2 coordinates
|
||||
|
||||
// Check polygon structure - it might be stored as an array of arrays
|
||||
if (is_array($document['area'][0])) {
|
||||
$this->assertGreaterThan(1, count($document['area'][0])); // POLYGON has multiple points
|
||||
} else {
|
||||
$this->assertGreaterThan(1, count($document['area'])); // POLYGON has multiple points
|
||||
}
|
||||
|
||||
$this->assertEquals('Location ' . $index, $document['name']);
|
||||
$this->assertEquals([10.0 + $index, 20.0 + $index], $document['location']);
|
||||
}
|
||||
|
||||
// Test 2: Bulk update with spatial data
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'name' => 'Updated Location',
|
||||
'location' => [15.0, 25.0], // New POINT
|
||||
'area' => [
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
] // New POLYGON
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(5, $response['body']['documents']);
|
||||
|
||||
// Verify updated documents
|
||||
foreach ($response['body']['documents'] as $document) {
|
||||
$this->assertEquals('Updated Location', $document['name']);
|
||||
$this->assertEquals([15.0, 25.0], $document['location']);
|
||||
// The area might be stored as an array of arrays, so check the first element
|
||||
$this->assertIsArray($document['area']);
|
||||
if (is_array($document['area'][0])) {
|
||||
// If it's an array of arrays, check the first polygon
|
||||
$this->assertEquals([
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
], $document['area'][0]);
|
||||
} else {
|
||||
// If it's a direct array, check the whole thing
|
||||
$this->assertEquals([
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
], $document['area']);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: Bulk upsert with spatial data
|
||||
$upsertDocuments = [
|
||||
[
|
||||
'$id' => 'upsert1',
|
||||
'name' => 'Upsert Location 1',
|
||||
'location' => [30.0, 40.0],
|
||||
'area' => [
|
||||
[30.0, 40.0],
|
||||
[31.0, 40.0],
|
||||
[31.0, 41.0],
|
||||
[30.0, 41.0],
|
||||
[30.0, 40.0]
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => 'upsert2',
|
||||
'name' => 'Upsert Location 2',
|
||||
'location' => [35.0, 45.0],
|
||||
'area' => [
|
||||
[35.0, 45.0],
|
||||
[36.0, 45.0],
|
||||
[36.0, 46.0],
|
||||
[35.0, 46.0],
|
||||
[35.0, 45.0]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => $upsertDocuments,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(2, $response['body']['documents']);
|
||||
|
||||
// Verify upserted documents
|
||||
foreach ($response['body']['documents'] as $document) {
|
||||
$this->assertNotEmpty($document['$id']);
|
||||
$this->assertIsArray($document['location']);
|
||||
$this->assertIsArray($document['area']);
|
||||
|
||||
// Verify the spatial data structure
|
||||
$this->assertCount(2, $document['location']); // POINT has 2 coordinates
|
||||
if (is_array($document['area'][0])) {
|
||||
$this->assertGreaterThan(1, count($document['area'][0])); // POLYGON has multiple points
|
||||
} else {
|
||||
$this->assertGreaterThan(1, count($document['area'])); // POLYGON has multiple points
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Edge cases for spatial bulk operations
|
||||
|
||||
// Test 4a: Invalid point coordinates (should fail)
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Point',
|
||||
'location' => [1000.0, 2000.0],
|
||||
'area' => [10.0 , 10.0]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// invalid polygon
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Polygon',
|
||||
'location' => [10.0, 20.0],
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Missing Location',
|
||||
// Missing required 'location' attribute
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to missing required attribute
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4e: Mixed valid and invalid documents in bulk (should fail)
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Valid Document',
|
||||
'location' => [10.0, 20.0],
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Document',
|
||||
// Missing required 'location' attribute
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to mixed valid/invalid documents
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4f: Very large spatial data (stress test)
|
||||
$largePolygon = [];
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$largePolygon[] = [$i * 0.001, $i * 0.001];
|
||||
}
|
||||
$largePolygon[] = [0, 0]; // Close the polygon
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Large Polygon Test',
|
||||
'location' => [0.0, 0.0],
|
||||
'area' => $largePolygon
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Test 4g: Null values in spatial attributes (should fail)
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Null Values Test',
|
||||
'location' => null, // Null point
|
||||
'area' => null // Null polygon
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to null values
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4h: Bulk operations with spatial data exceeding limits
|
||||
$largeBulkDocuments = [];
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$largeBulkDocuments[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Bulk Test ' . $i,
|
||||
'location' => [$i * 0.1, $i * 0.1],
|
||||
'area' => [
|
||||
[$i * 0.1, $i * 0.1],
|
||||
[($i + 1) * 0.1, $i * 0.1],
|
||||
[($i + 1) * 0.1, ($i + 1) * 0.1],
|
||||
[$i * 0.1, ($i + 1) * 0.1],
|
||||
[$i * 0.1, $i * 0.1]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => $largeBulkDocuments,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
}
|
||||
|
||||
public function testSpatialBulkOperationsWithLineStrings(): void
|
||||
{
|
||||
// Create database
|
||||
$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 LineString Bulk Operations Test Database'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($database['body']['$id']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection with line string attributes
|
||||
$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' => ID::unique(),
|
||||
'name' => 'Spatial LineString Bulk Operations Collection',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create string attribute
|
||||
$nameAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, $nameAttribute['headers']['status-code']);
|
||||
|
||||
// Create line string attribute
|
||||
$lineAttribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/line', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'path',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
// Handle both 201 (created) and 202 (accepted) status codes
|
||||
$this->assertEquals(202, $lineAttribute['headers']['status-code']);
|
||||
|
||||
// Wait for attributes to be created
|
||||
sleep(2);
|
||||
|
||||
// Test bulk create with line string data
|
||||
$lineStringDocuments = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$lineStringDocuments[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Path ' . $i,
|
||||
'path' => [
|
||||
[$i * 10, $i * 10],
|
||||
[($i + 1) * 10, ($i + 1) * 10],
|
||||
[($i + 2) * 10, ($i + 2) * 10]
|
||||
] // LINE STRING with 3 points
|
||||
];
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => $lineStringDocuments,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertCount(3, $response['body']['documents']);
|
||||
|
||||
// Verify created documents have proper line string data
|
||||
foreach ($response['body']['documents'] as $index => $document) {
|
||||
$this->assertNotEmpty($document['$id']);
|
||||
$this->assertNotEmpty($document['name']);
|
||||
$this->assertIsArray($document['path']);
|
||||
$this->assertGreaterThan(1, count($document['path'])); // LINE STRING has multiple points
|
||||
$this->assertEquals('Path ' . $index, $document['name']);
|
||||
$this->assertCount(3, $document['path']); // Each line string has 3 points
|
||||
}
|
||||
|
||||
// Test bulk update with line string data
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'name' => 'Updated Path',
|
||||
'path' => [
|
||||
[0, 0],
|
||||
[50, 50],
|
||||
[100, 100]
|
||||
] // New LINE STRING
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(3, $response['body']['documents']);
|
||||
|
||||
// Verify updated documents
|
||||
foreach ($response['body']['documents'] as $document) {
|
||||
$this->assertEquals('Updated Path', $document['name']);
|
||||
$this->assertEquals([
|
||||
[0, 0],
|
||||
[50, 50],
|
||||
[100, 100]
|
||||
], $document['path']);
|
||||
}
|
||||
|
||||
// Test: Invalid line string (single point - should fail)
|
||||
$response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Line String',
|
||||
'path' => [[10, 20]] // Single point - invalid line string
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6132,4 +6132,569 @@ class DatabasesCustomServerTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
}
|
||||
|
||||
public function testSpatialBulkOperations(): 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' => 'Spatial Bulk Operations 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 Bulk Operations 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' => 'location',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $pointColumn['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' => 'area',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $polygonColumn['headers']['status-code']);
|
||||
|
||||
// Wait for columns to be created
|
||||
sleep(2);
|
||||
|
||||
// Test 1: Bulk create with spatial data
|
||||
$spatialRows = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$spatialRows[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Location ' . $i,
|
||||
'location' => [10.0 + $i, 20.0 + $i], // POINT
|
||||
'area' => [
|
||||
[10.0 + $i, 20.0 + $i],
|
||||
[11.0 + $i, 20.0 + $i],
|
||||
[11.0 + $i, 21.0 + $i],
|
||||
[10.0 + $i, 21.0 + $i],
|
||||
[10.0 + $i, 20.0 + $i]
|
||||
] // POLYGON
|
||||
];
|
||||
}
|
||||
|
||||
$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()), [
|
||||
'rows' => $spatialRows,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertCount(5, $response['body']['rows']);
|
||||
|
||||
// Verify created rows have proper spatial data
|
||||
foreach ($response['body']['rows'] as $index => $row) {
|
||||
$this->assertNotEmpty($row['$id']);
|
||||
$this->assertNotEmpty($row['name']);
|
||||
$this->assertIsArray($row['location']);
|
||||
$this->assertIsArray($row['area']);
|
||||
$this->assertCount(2, $row['location']); // POINT has 2 coordinates
|
||||
|
||||
// Check polygon structure - it might be stored as an array of arrays
|
||||
if (is_array($row['area'][0])) {
|
||||
$this->assertGreaterThan(1, count($row['area'][0])); // POLYGON has multiple points
|
||||
} else {
|
||||
$this->assertGreaterThan(1, count($row['area'])); // POLYGON has multiple points
|
||||
}
|
||||
|
||||
$this->assertEquals('Location ' . $index, $row['name']);
|
||||
$this->assertEquals([10.0 + $index, 20.0 + $index], $row['location']);
|
||||
}
|
||||
|
||||
// Test 2: Bulk update with spatial data
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'name' => 'Updated Location',
|
||||
'location' => [15.0, 25.0], // New POINT
|
||||
'area' => [
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
] // New POLYGON
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(5, $response['body']['rows']);
|
||||
|
||||
// Verify updated rows
|
||||
foreach ($response['body']['rows'] as $row) {
|
||||
$this->assertEquals('Updated Location', $row['name']);
|
||||
$this->assertEquals([15.0, 25.0], $row['location']);
|
||||
// The area might be stored as an array of arrays, so check the first element
|
||||
$this->assertIsArray($row['area']);
|
||||
if (is_array($row['area'][0])) {
|
||||
// If it's an array of arrays, check the first polygon
|
||||
$this->assertEquals([
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
], $row['area'][0]);
|
||||
} else {
|
||||
// If it's a direct array, check the whole thing
|
||||
$this->assertEquals([
|
||||
[15.0, 25.0],
|
||||
[16.0, 25.0],
|
||||
[16.0, 26.0],
|
||||
[15.0, 26.0],
|
||||
[15.0, 25.0]
|
||||
], $row['area']);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: Bulk upsert with spatial data
|
||||
$upsertRows = [
|
||||
[
|
||||
'$id' => 'upsert1',
|
||||
'name' => 'Upsert Location 1',
|
||||
'location' => [30.0, 40.0],
|
||||
'area' => [
|
||||
[30.0, 40.0],
|
||||
[31.0, 40.0],
|
||||
[31.0, 41.0],
|
||||
[30.0, 41.0],
|
||||
[30.0, 40.0]
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => 'upsert2',
|
||||
'name' => 'Upsert Location 2',
|
||||
'location' => [35.0, 45.0],
|
||||
'area' => [
|
||||
[35.0, 45.0],
|
||||
[36.0, 45.0],
|
||||
[36.0, 46.0],
|
||||
[35.0, 46.0],
|
||||
[35.0, 45.0]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rows' => $upsertRows,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(2, $response['body']['rows']);
|
||||
|
||||
// Verify upserted rows
|
||||
foreach ($response['body']['rows'] as $row) {
|
||||
$this->assertNotEmpty($row['$id']);
|
||||
$this->assertIsArray($row['location']);
|
||||
$this->assertIsArray($row['area']);
|
||||
|
||||
// Verify the spatial data structure
|
||||
$this->assertCount(2, $row['location']); // POINT has 2 coordinates
|
||||
if (is_array($row['area'][0])) {
|
||||
$this->assertGreaterThan(1, count($row['area'][0])); // POLYGON has multiple points
|
||||
} else {
|
||||
$this->assertGreaterThan(1, count($row['area'])); // POLYGON has multiple points
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Edge cases for spatial bulk operations
|
||||
|
||||
// Test 4a: Invalid point coordinates (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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Point',
|
||||
'location' => [1000.0, 2000.0], // Invalid coordinates
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// Coordinates are not validated strictly; creation should succeed
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Test 4b: Invalid polygon (insufficient points - 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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Polygon',
|
||||
'location' => [10.0, 20.0],
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4c: Missing required location (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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Missing Location',
|
||||
// Missing required 'location' attribute
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to missing required attribute
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4d: Mixed valid and invalid rows in bulk (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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Valid Row',
|
||||
'location' => [10.0, 20.0],
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Row',
|
||||
// Missing required 'location' attribute
|
||||
'area' => [
|
||||
[10.0, 20.0],
|
||||
[11.0, 20.0],
|
||||
[11.0, 21.0],
|
||||
[10.0, 21.0],
|
||||
[10.0, 20.0]
|
||||
]
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to mixed valid/invalid rows
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4e: Very large spatial data (stress test)
|
||||
$largePolygon = [];
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$largePolygon[] = [$i * 0.001, $i * 0.001];
|
||||
}
|
||||
$largePolygon[] = [0, 0]; // Close the polygon
|
||||
|
||||
$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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Large Polygon Test',
|
||||
'location' => [0.0, 0.0],
|
||||
'area' => $largePolygon
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Test 4f: Null values in spatial attributes (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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Null Values Test',
|
||||
'location' => null, // Null point
|
||||
'area' => null // Null polygon
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
// This should fail due to null values
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test 4g: Bulk operations with spatial data exceeding limits
|
||||
$largeBulkRows = [];
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$largeBulkRows[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Bulk Test ' . $i,
|
||||
'location' => [$i * 0.1, $i * 0.1],
|
||||
'area' => [
|
||||
[$i * 0.1, $i * 0.1],
|
||||
[($i + 1) * 0.1, $i * 0.1],
|
||||
[($i + 1) * 0.1, ($i + 1) * 0.1],
|
||||
[$i * 0.1, ($i + 1) * 0.1],
|
||||
[$i * 0.1, $i * 0.1]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$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()), [
|
||||
'rows' => $largeBulkRows,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $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 testSpatialBulkOperationsWithLineStrings(): 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' => 'Spatial LineString Bulk Operations Test Database'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($database['body']['$id']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table with line string 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 LineString Bulk Operations 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 line string 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' => 'path',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
// Handle both 201 (created) and 202 (accepted) status codes
|
||||
$this->assertEquals(202, $lineColumn['headers']['status-code']);
|
||||
|
||||
// Wait for columns to be created
|
||||
sleep(2);
|
||||
|
||||
// Test bulk create with line string data
|
||||
$lineStringRows = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$lineStringRows[] = [
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Path ' . $i,
|
||||
'path' => [
|
||||
[$i * 10, $i * 10],
|
||||
[($i + 1) * 10, ($i + 1) * 10],
|
||||
[($i + 2) * 10, ($i + 2) * 10]
|
||||
] // LINE STRING with 3 points
|
||||
];
|
||||
}
|
||||
|
||||
$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()), [
|
||||
'rows' => $lineStringRows,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertCount(3, $response['body']['rows']);
|
||||
|
||||
// Verify created rows have proper line string data
|
||||
foreach ($response['body']['rows'] as $index => $row) {
|
||||
$this->assertNotEmpty($row['$id']);
|
||||
$this->assertNotEmpty($row['name']);
|
||||
$this->assertIsArray($row['path']);
|
||||
$this->assertGreaterThan(1, count($row['path'])); // LINE STRING has multiple points
|
||||
$this->assertEquals('Path ' . $index, $row['name']);
|
||||
$this->assertCount(3, $row['path']); // Each line string has 3 points
|
||||
}
|
||||
|
||||
// Test bulk update with line string data
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'name' => 'Updated Path',
|
||||
'path' => [
|
||||
[0, 0],
|
||||
[50, 50],
|
||||
[100, 100]
|
||||
] // New LINE STRING
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(3, $response['body']['rows']);
|
||||
|
||||
// Verify updated rows
|
||||
foreach ($response['body']['rows'] as $row) {
|
||||
$this->assertEquals('Updated Path', $row['name']);
|
||||
$this->assertEquals([
|
||||
[0, 0],
|
||||
[50, 50],
|
||||
[100, 100]
|
||||
], $row['path']);
|
||||
}
|
||||
|
||||
// Test: Invalid line string (single point - 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()), [
|
||||
'rows' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Invalid Line String',
|
||||
'path' => [[10, 20]] // Single point - invalid line string
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$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']
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ trait Base
|
|||
public const string CREATE_IP_ATTRIBUTE = 'create_ip_attribute';
|
||||
public const string CREATE_ENUM_ATTRIBUTE = 'create_enum_attribute';
|
||||
public const string CREATE_DATETIME_ATTRIBUTE = 'create_datetime_attribute';
|
||||
public const string CREATE_POINT_ATTRIBUTE = 'create_point_attribute';
|
||||
public const string CREATE_LINE_ATTRIBUTE = 'create_line_attribute';
|
||||
public const string CREATE_POLYGON_ATTRIBUTE = 'create_polygon_attribute';
|
||||
|
||||
public const string CREATE_RELATIONSHIP_ATTRIBUTE = 'create_relationship_attribute';
|
||||
public const string UPDATE_STRING_ATTRIBUTE = 'update_string_attribute';
|
||||
|
|
@ -56,6 +59,9 @@ trait Base
|
|||
public const string UPDATE_IP_ATTRIBUTE = 'update_ip_attribute';
|
||||
public const string UPDATE_ENUM_ATTRIBUTE = 'update_enum_attribute';
|
||||
public const string UPDATE_DATETIME_ATTRIBUTE = 'update_datetime_attribute';
|
||||
public const string UPDATE_POINT_ATTRIBUTE = 'update_point_attribute';
|
||||
public const string UPDATE_LINE_ATTRIBUTE = 'update_line_attribute';
|
||||
public const string UPDATE_POLYGON_ATTRIBUTE = 'update_polygon_attribute';
|
||||
|
||||
public const string UPDATE_RELATIONSHIP_ATTRIBUTE = 'update_relationship_attribute';
|
||||
public const string GET_ATTRIBUTES = 'get_attributes';
|
||||
|
|
@ -72,6 +78,9 @@ trait Base
|
|||
public const string CREATE_IP_COLUMN = 'create_ip_column';
|
||||
public const string CREATE_ENUM_COLUMN = 'create_enum_column';
|
||||
public const string CREATE_DATETIME_COLUMN = 'create_datetime_column';
|
||||
public const string CREATE_POINT_COLUMN = 'create_point_column';
|
||||
public const string CREATE_LINE_COLUMN = 'create_line_column';
|
||||
public const string CREATE_POLYGON_COLUMN = 'create_polygon_column';
|
||||
|
||||
public const string CREATE_RELATIONSHIP_COLUMN = 'create_relationship_column';
|
||||
public const string UPDATE_STRING_COLUMN = 'update_string_column';
|
||||
|
|
@ -83,6 +92,9 @@ trait Base
|
|||
public const string UPDATE_IP_COLUMN = 'update_ip_column';
|
||||
public const string UPDATE_ENUM_COLUMN = 'update_enum_column';
|
||||
public const string UPDATE_DATETIME_COLUMN = 'update_datetime_column';
|
||||
public const string UPDATE_POINT_COLUMN = 'update_point_column';
|
||||
public const string UPDATE_LINE_COLUMN = 'update_line_column';
|
||||
public const string UPDATE_POLYGON_COLUMN = 'update_polygon_column';
|
||||
|
||||
public const string UPDATE_RELATIONSHIP_COLUMN = 'update_relationship_column';
|
||||
public const string GET_COLUMNS = 'get_columns';
|
||||
|
|
@ -930,6 +942,35 @@ trait Base
|
|||
array
|
||||
}
|
||||
}';
|
||||
case self::CREATE_POINT_COLUMN:
|
||||
return 'mutation createPointColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $array: Boolean){
|
||||
tablesDBCreatePointColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, array: $array) {
|
||||
key
|
||||
required
|
||||
array
|
||||
status
|
||||
}
|
||||
}';
|
||||
|
||||
case self::CREATE_LINE_COLUMN:
|
||||
return 'mutation createLineColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $array: Boolean){
|
||||
tablesDBCreateLineColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, array: $array) {
|
||||
key
|
||||
required
|
||||
array
|
||||
status
|
||||
}
|
||||
}';
|
||||
|
||||
case self::CREATE_POLYGON_COLUMN:
|
||||
return 'mutation createPolygonColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!, $array: Boolean){
|
||||
tablesDBCreatePolygonColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required, array: $array) {
|
||||
key
|
||||
required
|
||||
array
|
||||
status
|
||||
}
|
||||
}';
|
||||
case self::CREATE_RELATIONSHIP_COLUMN:
|
||||
return 'mutation createRelationshipColumn($databaseId: String!, $tableId: String!, $relatedTableId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){
|
||||
tablesDBCreateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, relatedTableId: $relatedTableId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) {
|
||||
|
|
@ -1009,6 +1050,27 @@ trait Base
|
|||
default
|
||||
}
|
||||
}';
|
||||
case self::UPDATE_POINT_COLUMN:
|
||||
return 'mutation updatePointColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!){
|
||||
tablesDBUpdatePointColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required) {
|
||||
required
|
||||
}
|
||||
}';
|
||||
|
||||
case self::UPDATE_LINE_COLUMN:
|
||||
return 'mutation updateLineColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!){
|
||||
tablesDBUpdateLineColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required) {
|
||||
required
|
||||
}
|
||||
}';
|
||||
|
||||
case self::UPDATE_POLYGON_COLUMN:
|
||||
return 'mutation updatePolygonColumn($databaseId: String!, $tableId: String!, $key: String!, $required: Boolean!){
|
||||
tablesDBUpdatePolygonColumn(databaseId: $databaseId, tableId: $tableId, key: $key, required: $required) {
|
||||
required
|
||||
}
|
||||
}';
|
||||
|
||||
case self::UPDATE_RELATIONSHIP_COLUMN:
|
||||
return 'mutation updateRelationshipColumn($databaseId: String!, $tableId: String!, $key: String!, $onDelete: String){
|
||||
tablesDBUpdateRelationshipColumn(databaseId: $databaseId, tableId: $tableId, key: $key, onDelete: $onDelete) {
|
||||
|
|
|
|||
|
|
@ -1188,6 +1188,55 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||
|
||||
// bulk upsert
|
||||
$this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$actorsId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Robert Downey Jr.',
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(6, $response['data']['channels']);
|
||||
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*", $response['data']['events']);
|
||||
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']);
|
||||
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||
|
||||
$client->close();
|
||||
}
|
||||
|
||||
|
|
@ -1644,6 +1693,107 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertIsArray($response2['data']['payload']['$permissions']);
|
||||
}
|
||||
|
||||
// bulk upsert
|
||||
$this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$actorsId}/documents", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documents' => [
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Robert Downey Jr.',
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user1Id)),
|
||||
],
|
||||
],
|
||||
[
|
||||
'$id' => ID::unique(),
|
||||
'name' => 'Thor',
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user2Id)),
|
||||
],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$response = json_decode($client1->receive(), true);
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(6, $response['data']['channels']);
|
||||
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*", $response['data']['events']);
|
||||
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']);
|
||||
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||
|
||||
// client1 shouldnot receive more than 1 event
|
||||
try {
|
||||
json_decode(json_decode($client1->receive(), true));
|
||||
$this->fail('Expected TimeoutException was not thrown.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf(TimeoutException::class, $e);
|
||||
}
|
||||
|
||||
$response = json_decode($client2->receive(), true);
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(6, $response['data']['channels']);
|
||||
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||
$this->assertContains("databases.*", $response['data']['events']);
|
||||
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']);
|
||||
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||
|
||||
// client2 shouldnot receive more than 1 event
|
||||
try {
|
||||
json_decode(json_decode($client2->receive(), true));
|
||||
$this->fail('Expected TimeoutException was not thrown.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf(TimeoutException::class, $e);
|
||||
}
|
||||
|
||||
|
||||
$client1->close();
|
||||
$client2->close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2775,11 +2775,13 @@ class SitesCustomServerTest extends Scope
|
|||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/cookies', [
|
||||
'cookie' => 'custom-session-id=abcd123'
|
||||
'cookie' => 'custom-session-id=abcd123; custom-user-id=efgh456'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("abcd123", $response['body']);
|
||||
$this->assertEquals("abcd123;efgh456", $response['body']);
|
||||
$this->assertEquals("value-one", $response['cookies']['my-cookie-one']);
|
||||
$this->assertEquals("value-two", $response['cookies']['my-cookie-two']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
export async function GET(context) {
|
||||
const sessionId = context.cookies.get("custom-session-id")?.value ?? 'Custom session ID missing';
|
||||
return new Response(sessionId);
|
||||
const userId = context.cookies.get("custom-user-id")?.value ?? 'Custom user ID missing';
|
||||
|
||||
context.cookies.set('my-cookie-one', 'value-one');
|
||||
context.cookies.set('my-cookie-two', 'value-two');
|
||||
|
||||
return new Response(sessionId + ";" + userId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue