mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.9.x' into fix-oauth-oidc-params
This commit is contained in:
@@ -1523,6 +1523,13 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_team_confirm'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['teamInternalId', 'confirm'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -404,6 +404,13 @@ $platformCollections = [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_teamInternalId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['teamInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -635,6 +642,13 @@ $platformCollections = [
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1007,7 +1021,14 @@ $platformCollections = [
|
||||
'attributes' => ['projectInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -298,6 +298,16 @@ return [
|
||||
'category' => 'Messaging',
|
||||
],
|
||||
|
||||
// Proxy
|
||||
'rules.read' => [
|
||||
'description' => 'Access to read proxy rules.',
|
||||
'category' => 'Proxy',
|
||||
],
|
||||
'rules.write' => [
|
||||
'description' => 'Access to create, update, and delete proxy rules.',
|
||||
'category' => 'Proxy',
|
||||
],
|
||||
|
||||
// Other
|
||||
"webhooks.read" => [
|
||||
"description" =>
|
||||
@@ -351,12 +361,4 @@ return [
|
||||
'description' => 'Access to create, update, and delete resources under VCS service.',
|
||||
'category' => 'Other',
|
||||
],
|
||||
'rules.read' => [
|
||||
'description' => 'Access to read proxy rules.',
|
||||
'category' => 'Other',
|
||||
],
|
||||
'rules.write' => [
|
||||
'description' => 'Access to create, update, and delete proxy rules.',
|
||||
'category' => 'Other',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -52,6 +52,7 @@ const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
|
||||
const APP_DATABASE_ATTRIBUTE_URL = 'url';
|
||||
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||
const APP_DATABASE_ATTRIBUTE_BIGINT_RANGE = 'bigintRange';
|
||||
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||
const APP_DATABASE_ATTRIBUTE_POINT = 'point';
|
||||
const APP_DATABASE_ATTRIBUTE_LINE = 'line';
|
||||
|
||||
@@ -36,6 +36,13 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
|
||||
return new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}, Database::VAR_INTEGER);
|
||||
|
||||
// BigInt uses a dedicated bigintRange format name to avoid clobbering `intRange`.
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_BIGINT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}, Database::VAR_BIGINT);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
|
||||
@@ -11,6 +11,7 @@ use Appwrite\Utopia\Response\Model\AlgoScryptModified;
|
||||
use Appwrite\Utopia\Response\Model\AlgoSha;
|
||||
use Appwrite\Utopia\Response\Model\Any;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
use Appwrite\Utopia\Response\Model\AttributeBigInt;
|
||||
use Appwrite\Utopia\Response\Model\AttributeBoolean;
|
||||
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
||||
use Appwrite\Utopia\Response\Model\AttributeEmail;
|
||||
@@ -37,6 +38,7 @@ use Appwrite\Utopia\Response\Model\Branch;
|
||||
use Appwrite\Utopia\Response\Model\Bucket;
|
||||
use Appwrite\Utopia\Response\Model\Collection;
|
||||
use Appwrite\Utopia\Response\Model\Column;
|
||||
use Appwrite\Utopia\Response\Model\ColumnBigInt;
|
||||
use Appwrite\Utopia\Response\Model\ColumnBoolean;
|
||||
use Appwrite\Utopia\Response\Model\ColumnDatetime;
|
||||
use Appwrite\Utopia\Response\Model\ColumnEmail;
|
||||
@@ -297,6 +299,7 @@ Response::setModel(new Attribute());
|
||||
Response::setModel(new AttributeList());
|
||||
Response::setModel(new AttributeString());
|
||||
Response::setModel(new AttributeInteger());
|
||||
Response::setModel(new AttributeBigInt());
|
||||
Response::setModel(new AttributeFloat());
|
||||
Response::setModel(new AttributeBoolean());
|
||||
Response::setModel(new AttributeEmail());
|
||||
@@ -330,6 +333,7 @@ Response::setModel(new Column());
|
||||
Response::setModel(new ColumnList());
|
||||
Response::setModel(new ColumnString());
|
||||
Response::setModel(new ColumnInteger());
|
||||
Response::setModel(new ColumnBigInt());
|
||||
Response::setModel(new ColumnFloat());
|
||||
Response::setModel(new ColumnBoolean());
|
||||
Response::setModel(new ColumnEmail());
|
||||
|
||||
+3
-3
@@ -74,13 +74,13 @@
|
||||
"utopia-php/locale": "0.8.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.22.*",
|
||||
"utopia-php/migration": "1.9.*",
|
||||
"utopia-php/migration": "1.*",
|
||||
"utopia-php/platform": "0.13.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/span": "1.1.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.17.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/queue": "0.18.*",
|
||||
"utopia-php/servers": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "2.*",
|
||||
"utopia-php/system": "0.10.*",
|
||||
|
||||
Generated
+88
-88
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2af4d953af2a624be8bf2f89ea27336c",
|
||||
"content-hash": "ec2ad489c60f0102f0dfab223b6d1fe4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -2708,16 +2708,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.4.8",
|
||||
"version": "v7.4.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "01933e626c3de76bea1e22641e205e78f6a34342"
|
||||
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/01933e626c3de76bea1e22641e205e78f6a34342",
|
||||
"reference": "01933e626c3de76bea1e22641e205e78f6a34342",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
|
||||
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2785,7 +2785,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.4.8"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.4.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2805,7 +2805,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T12:55:43+00:00"
|
||||
"time": "2026-04-29T13:25:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -3658,21 +3658,21 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cli",
|
||||
"version": "0.23.2",
|
||||
"version": "0.23.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cli.git",
|
||||
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab"
|
||||
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
|
||||
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
|
||||
"url": "https://api.github.com/repos/utopia-php/cli/zipball/3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
|
||||
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"utopia-php/servers": "0.3.*"
|
||||
"utopia-php/servers": "0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
@@ -3703,9 +3703,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cli/issues",
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.23.2"
|
||||
"source": "https://github.com/utopia-php/cli/tree/0.23.3"
|
||||
},
|
||||
"time": "2026-04-27T09:19:04+00:00"
|
||||
"time": "2026-05-05T04:38:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/compression",
|
||||
@@ -3850,22 +3850,23 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "5.4.2",
|
||||
"version": "5.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "a1bb3e2a4fba13032ea625b21a21039c43cffeda"
|
||||
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/a1bb3e2a4fba13032ea625b21a21039c43cffeda",
|
||||
"reference": "a1bb3e2a4fba13032ea625b21a21039c43cffeda",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
|
||||
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"ext-mongodb": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.4",
|
||||
"utopia-php/cache": "1.*",
|
||||
"utopia-php/console": "0.1.*",
|
||||
@@ -3903,9 +3904,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/5.4.2"
|
||||
"source": "https://github.com/utopia-php/database/tree/5.7.0"
|
||||
},
|
||||
"time": "2026-04-30T09:59:57+00:00"
|
||||
"time": "2026-05-06T01:04:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -4271,23 +4272,23 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/http",
|
||||
"version": "0.34.24",
|
||||
"version": "0.34.25",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/http.git",
|
||||
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33"
|
||||
"reference": "76be330d4197bae680eb4ccc29c573456fe91904"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/d1eced0627c5a9fceddf53992ed97d664b810d33",
|
||||
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/76be330d4197bae680eb4ccc29c573456fe91904",
|
||||
"reference": "76be330d4197bae680eb4ccc29c573456fe91904",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/servers": "0.4.0",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
@@ -4321,9 +4322,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/http/issues",
|
||||
"source": "https://github.com/utopia-php/http/tree/0.34.24"
|
||||
"source": "https://github.com/utopia-php/http/tree/0.34.25"
|
||||
},
|
||||
"time": "2026-04-24T12:16:53+00:00"
|
||||
"time": "2026-05-05T04:39:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
@@ -4530,16 +4531,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "1.9.6",
|
||||
"version": "1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "b164631404ec759f8c368fe2321f44c22bc258ab"
|
||||
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/b164631404ec759f8c368fe2321f44c22bc258ab",
|
||||
"reference": "b164631404ec759f8c368fe2321f44c22bc258ab",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/55f4863d690e775f44fec3cae4bd1f4491fed5ea",
|
||||
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4579,9 +4580,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.9.6"
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.10.0"
|
||||
},
|
||||
"time": "2026-04-30T08:11:07+00:00"
|
||||
"time": "2026-05-06T04:35:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -4646,26 +4647,26 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/platform",
|
||||
"version": "0.13.0",
|
||||
"version": "0.13.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/platform.git",
|
||||
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e"
|
||||
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/d23af5349a7ea9ee11f9920a13626226f985522e",
|
||||
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
|
||||
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.1",
|
||||
"utopia-php/cli": "0.23.*",
|
||||
"utopia-php/http": "0.34.*",
|
||||
"utopia-php/queue": "0.17.*",
|
||||
"utopia-php/servers": "0.3.*"
|
||||
"php": ">=8.3",
|
||||
"utopia-php/cli": "0.23.3",
|
||||
"utopia-php/http": "0.34.25",
|
||||
"utopia-php/queue": "0.18.2",
|
||||
"utopia-php/servers": "0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
@@ -4691,9 +4692,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/platform/issues",
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.13.0"
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.13.2"
|
||||
},
|
||||
"time": "2026-04-17T09:57:18+00:00"
|
||||
"time": "2026-05-05T06:00:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/pools",
|
||||
@@ -4803,25 +4804,24 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.17.0",
|
||||
"version": "0.18.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/queue.git",
|
||||
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f"
|
||||
"reference": "f85ca003c99ff475708c05466643d067403c0c22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/0fbc7d7312f5cf76ec112513fb93317000901f5f",
|
||||
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/f85ca003c99ff475708c05466643d067403c0c22",
|
||||
"reference": "f85ca003c99ff475708c05466643d067403c0c22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"php-amqplib/php-amqplib": "^3.7",
|
||||
"utopia-php/di": "0.3.*",
|
||||
"utopia-php/fetch": "0.5.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/servers": "0.3.*",
|
||||
"utopia-php/servers": "0.4.0",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
@@ -4864,9 +4864,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/queue/issues",
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.17.0"
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.18.2"
|
||||
},
|
||||
"time": "2026-03-23T16:21:31+00:00"
|
||||
"time": "2026-05-05T04:38:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/registry",
|
||||
@@ -4922,16 +4922,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/servers",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/servers.git",
|
||||
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52"
|
||||
"reference": "7db346ef377503efe0acafe0791085270cd9ed70"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/servers/zipball/235be31200df9437fc96a1c270ffef4c64fafe52",
|
||||
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52",
|
||||
"url": "https://api.github.com/repos/utopia-php/servers/zipball/7db346ef377503efe0acafe0791085270cd9ed70",
|
||||
"reference": "7db346ef377503efe0acafe0791085270cd9ed70",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4970,9 +4970,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/servers/issues",
|
||||
"source": "https://github.com/utopia-php/servers/tree/0.3.0"
|
||||
"source": "https://github.com/utopia-php/servers/tree/0.4.0"
|
||||
},
|
||||
"time": "2026-03-13T11:31:42+00:00"
|
||||
"time": "2026-05-05T04:08:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/span",
|
||||
@@ -5020,16 +5020,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "8a2e3a86fd01aaed675884146665308c2122264e"
|
||||
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/8a2e3a86fd01aaed675884146665308c2122264e",
|
||||
"reference": "8a2e3a86fd01aaed675884146665308c2122264e",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/64e132a3768e22243eda36fe4262da22fd204f3c",
|
||||
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5066,22 +5066,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/2.0.1"
|
||||
"source": "https://github.com/utopia-php/storage/tree/2.0.2"
|
||||
},
|
||||
"time": "2026-04-29T09:05:48+00:00"
|
||||
"time": "2026-05-01T15:06:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
"version": "0.10.1",
|
||||
"version": "0.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/system.git",
|
||||
"reference": "7c1669533bb9c285de19191270c8c1439161a78a"
|
||||
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/system/zipball/7c1669533bb9c285de19191270c8c1439161a78a",
|
||||
"reference": "7c1669533bb9c285de19191270c8c1439161a78a",
|
||||
"url": "https://api.github.com/repos/utopia-php/system/zipball/04229a822b147c1abaf1a92fb42c2d7aad4625df",
|
||||
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5122,9 +5122,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/system/issues",
|
||||
"source": "https://github.com/utopia-php/system/tree/0.10.1"
|
||||
"source": "https://github.com/utopia-php/system/tree/0.10.2"
|
||||
},
|
||||
"time": "2026-03-15T21:07:41+00:00"
|
||||
"time": "2026-05-05T14:33:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/telemetry",
|
||||
@@ -5466,16 +5466,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.25.1",
|
||||
"version": "1.27.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "f21a556b9acdbf75bbdcdc90a078af641646eade"
|
||||
"reference": "9faa38b48d422f3da764a719712905c83b3922cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/f21a556b9acdbf75bbdcdc90a078af641646eade",
|
||||
"reference": "f21a556b9acdbf75bbdcdc90a078af641646eade",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb",
|
||||
"reference": "9faa38b48d422f3da764a719712905c83b3922cb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5511,9 +5511,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.25.1"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.27.5"
|
||||
},
|
||||
"time": "2026-04-28T11:12:22+00:00"
|
||||
"time": "2026-05-05T12:09:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
@@ -6620,16 +6620,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "12.5.23",
|
||||
"version": "12.5.24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969"
|
||||
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
|
||||
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046",
|
||||
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6698,7 +6698,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.23"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6706,7 +6706,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-18T06:12:49+00:00"
|
||||
"time": "2026-05-01T04:21:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@@ -7691,16 +7691,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
|
||||
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d",
|
||||
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7757,7 +7757,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7777,7 +7777,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-04-29T15:02:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Create a bigint attribute. Optionally, minimum and maximum values can be provided.
|
||||
@@ -0,0 +1 @@
|
||||
Update a bigint attribute. Changing the `default` value will not update already existing documents.
|
||||
@@ -0,0 +1 @@
|
||||
Create a bigint column. Optionally, minimum and maximum values can be provided.
|
||||
@@ -0,0 +1 @@
|
||||
Update a bigint column. Changing the `default` value will not update already existing rows.
|
||||
+9
-3
@@ -241,6 +241,10 @@ abstract class Action extends UtopiaAction
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_INTEGER
|
||||
: UtopiaResponse::MODEL_COLUMN_INTEGER,
|
||||
|
||||
Database::VAR_BIGINT => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_BIGINT
|
||||
: UtopiaResponse::MODEL_COLUMN_BIGINT,
|
||||
|
||||
Database::VAR_FLOAT => $isCollections
|
||||
? UtopiaResponse::MODEL_ATTRIBUTE_FLOAT
|
||||
: UtopiaResponse::MODEL_COLUMN_FLOAT,
|
||||
@@ -540,6 +544,7 @@ abstract class Action extends UtopiaAction
|
||||
|
||||
switch ($attribute->getAttribute('format')) {
|
||||
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
|
||||
case APP_DATABASE_ATTRIBUTE_BIGINT_RANGE:
|
||||
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
|
||||
$min ??= $attribute->getAttribute('formatOptions')['min'];
|
||||
$max ??= $attribute->getAttribute('formatOptions')['max'];
|
||||
@@ -548,14 +553,15 @@ abstract class Action extends UtopiaAction
|
||||
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
|
||||
}
|
||||
|
||||
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) {
|
||||
$validator = new Range($min, $max, Database::VAR_INTEGER);
|
||||
} else {
|
||||
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_FLOAT_RANGE) {
|
||||
$validator = new Range($min, $max, Database::VAR_FLOAT);
|
||||
|
||||
if (!is_null($default)) {
|
||||
$default = \floatval($default);
|
||||
}
|
||||
} else {
|
||||
// intRange and bigintRange share the same integer range semantics
|
||||
$validator = new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}
|
||||
|
||||
if (!is_null($default) && !$validator->isValid($default)) {
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
|
||||
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
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\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Range;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createBigIntAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint')
|
||||
->desc('Create bigint attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->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-bigint-attribute.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
deprecated: new Deprecated(
|
||||
since: '1.8.0',
|
||||
replaceWith: 'tablesDB.createBigIntColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
|
||||
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
|
||||
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
|
||||
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.', true)
|
||||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
|
||||
{
|
||||
$min ??= \PHP_INT_MIN;
|
||||
$max ??= \PHP_INT_MAX;
|
||||
|
||||
if ($min > $max) {
|
||||
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
|
||||
}
|
||||
|
||||
$validator = new Range($min, $max, Range::TYPE_INTEGER);
|
||||
if (!\is_null($default) && !$validator->isValid($default)) {
|
||||
throw new Exception($this->getInvalidValueException(), $validator->getDescription());
|
||||
}
|
||||
|
||||
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
|
||||
'key' => $key,
|
||||
'type' => Database::VAR_BIGINT,
|
||||
'size' => 8,
|
||||
'required' => $required,
|
||||
'default' => $default,
|
||||
'array' => $array,
|
||||
'format' => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
|
||||
'formatOptions' => ['min' => $min, 'max' => $max],
|
||||
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
if (!empty($formatOptions)) {
|
||||
$attribute->setAttribute('min', \intval($formatOptions['min']));
|
||||
$attribute->setAttribute('max', \intval($formatOptions['max']));
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
|
||||
|
||||
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\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updateBigIntAttribute';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint/:key')
|
||||
->desc('Update bigint 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-bigint-attribute.md',
|
||||
auth: [AuthType::ADMIN, 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.updateBigIntColumn',
|
||||
),
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
|
||||
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
|
||||
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
|
||||
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
|
||||
{
|
||||
$attribute = $this->updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
key: $key,
|
||||
dbForProject: $dbForProject,
|
||||
queueForEvents: $queueForEvents,
|
||||
authorization: $authorization,
|
||||
type: Database::VAR_BIGINT,
|
||||
default: $default,
|
||||
required: $required,
|
||||
min: $min,
|
||||
max: $max,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
if (!empty($formatOptions)) {
|
||||
$attribute->setAttribute('min', \intval($formatOptions['min']));
|
||||
$attribute->setAttribute('max', \intval($formatOptions['max']));
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
|
||||
->dynamic($attribute, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
@@ -290,13 +290,15 @@ class Create extends Action
|
||||
}
|
||||
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
$format = $type === Database::VAR_INTEGER
|
||||
? APP_DATABASE_ATTRIBUTE_INT_RANGE
|
||||
: APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
|
||||
$format = match($type) {
|
||||
Database::VAR_INTEGER => APP_DATABASE_ATTRIBUTE_INT_RANGE,
|
||||
Database::VAR_BIGINT => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
|
||||
default => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
|
||||
};
|
||||
|
||||
$formatOptions = [
|
||||
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
|
||||
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
|
||||
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
|
||||
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as BigIntCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Create extends BigIntCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createBigIntColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_BIGINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint')
|
||||
->desc('Create bigint column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
|
||||
->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-bigint-column.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_ACCEPTED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
|
||||
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
|
||||
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
|
||||
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.', true)
|
||||
->param('array', false, new Boolean(), 'Is column an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as BigIntUpdate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Update extends BigIntUpdate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updateBigIntColumn';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string|array
|
||||
{
|
||||
return UtopiaResponse::MODEL_COLUMN_BIGINT;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint/:key')
|
||||
->desc('Update bigint column')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.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-bigint-column.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
|
||||
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
|
||||
->param('required', null, new Boolean(), 'Is column required?')
|
||||
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
|
||||
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
|
||||
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.')
|
||||
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Column Key.', true, ['dbForProject'])
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Services\Registry;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as CreateBigIntAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as UpdateBigIntAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as CreateBooleanAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as UpdateBooleanAttribute;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as CreateDatetimeAttribute;
|
||||
@@ -171,6 +173,10 @@ class Legacy extends Base
|
||||
$service->addAction(CreateIntegerAttribute::getName(), new CreateIntegerAttribute());
|
||||
$service->addAction(UpdateIntegerAttribute::getName(), new UpdateIntegerAttribute());
|
||||
|
||||
// Attribute: BigInt
|
||||
$service->addAction(CreateBigIntAttribute::getName(), new CreateBigIntAttribute());
|
||||
$service->addAction(UpdateBigIntAttribute::getName(), new UpdateBigIntAttribute());
|
||||
|
||||
// Attribute: IP
|
||||
$service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute());
|
||||
$service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute());
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Appwrite\Platform\Modules\Databases\Services\Registry;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Create as CreateTablesDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Delete as DeleteTablesDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Get as GetTablesDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Create as CreateBigInt;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Update as UpdateBigInt;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Create as CreateBoolean;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Update as UpdateBoolean;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Datetime\Create as CreateDatetime;
|
||||
@@ -151,6 +153,10 @@ class TablesDB extends Base
|
||||
$service->addAction(CreateInteger::getName(), new CreateInteger());
|
||||
$service->addAction(UpdateInteger::getName(), new UpdateInteger());
|
||||
|
||||
// Column: BigInt
|
||||
$service->addAction(CreateBigInt::getName(), new CreateBigInt());
|
||||
$service->addAction(UpdateBigInt::getName(), new UpdateBigInt());
|
||||
|
||||
// Column: IP
|
||||
$service->addAction(CreateIP::getName(), new CreateIP());
|
||||
$service->addAction(UpdateIP::getName(), new UpdateIP());
|
||||
|
||||
@@ -59,7 +59,7 @@ class Create extends Base
|
||||
],
|
||||
))
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', optional: false)
|
||||
->param('duration', null, new Range(1, 3600), 'Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds.', optional: false)
|
||||
->param('duration', null, new Range(1, 3600), 'Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds.', optional: false, example: 600)
|
||||
->inject('response')
|
||||
->inject('queueForEvents')
|
||||
->inject('project')
|
||||
|
||||
@@ -11,7 +11,7 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
@@ -86,28 +86,28 @@ class Get extends Action
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('provider', '', new Text(128), 'OAuth2 provider key. For example: github, google, apple.')
|
||||
->param('providerId', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders', [])), true), 'OAuth2 provider key. For example: github, google, apple.', aliases: ['provider'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $provider,
|
||||
string $providerId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
): void {
|
||||
$providers = Config::getParam('oAuthProviders', []);
|
||||
if (!\array_key_exists($provider, $providers) || !($providers[$provider]['enabled'] ?? false)) {
|
||||
if (!\array_key_exists($providerId, $providers) || !($providers[$providerId]['enabled'] ?? false)) {
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$actions = Base::getProviderActions();
|
||||
if (!isset($actions[$provider])) {
|
||||
if (!isset($actions[$providerId])) {
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$updateClass = $actions[$provider];
|
||||
$updateClass = $actions[$providerId];
|
||||
$action = new $updateClass();
|
||||
|
||||
$response->dynamic($action->buildReadResponse($project), $updateClass::getResponseModel());
|
||||
|
||||
@@ -2,14 +2,21 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Project\Http\Project\OAuth2;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
@@ -43,15 +50,28 @@ class XList extends Action
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $queries
|
||||
*/
|
||||
public function action(
|
||||
array $queries,
|
||||
bool $includeTotal,
|
||||
Response $response,
|
||||
Document $project,
|
||||
): void {
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$providers = Config::getParam('oAuthProviders', []);
|
||||
$actions = Base::getProviderActions();
|
||||
|
||||
@@ -66,8 +86,16 @@ class XList extends Action
|
||||
$documents[] = $action->buildReadResponse($project);
|
||||
}
|
||||
|
||||
$total = $includeTotal ? \count($documents) : 0;
|
||||
|
||||
$grouped = Query::groupByType($queries);
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
$limit = $grouped['limit'] ?? null;
|
||||
|
||||
$documents = \array_slice($documents, $offset, $limit);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => \count($documents),
|
||||
'total' => $total,
|
||||
'providers' => $documents,
|
||||
]), Response::MODEL_OAUTH2_PROVIDER_LIST);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
@@ -43,12 +44,14 @@ class Create extends Action
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'createAPIRule',
|
||||
description: <<<EOT
|
||||
Create a new proxy rule for serving Appwrite's API on custom domain.
|
||||
|
||||
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
@@ -67,11 +70,21 @@ class Create extends Action
|
||||
->inject('dbForPlatform')
|
||||
->inject('platform')
|
||||
->inject('log')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $domain, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, array $platform, Log $log)
|
||||
{
|
||||
public function action(
|
||||
string $domain,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Certificate $publisherForCertificates,
|
||||
Event $queueForEvents,
|
||||
Database $dbForPlatform,
|
||||
array $platform,
|
||||
Log $log,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
$this->validateDomainRestrictions($domain, $platform);
|
||||
|
||||
// TODO: (@Meldiron) Remove after 1.7.x migration
|
||||
@@ -108,7 +121,7 @@ class Create extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$rule = $dbForPlatform->createDocument('rules', $rule);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
|
||||
} catch (Duplicate $e) {
|
||||
throw new Exception(Exception::RULE_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -126,6 +139,13 @@ class Create extends Action
|
||||
|
||||
$queueForEvents->setParam('ruleId', $rule->getId());
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
|
||||
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -38,12 +39,12 @@ class Delete extends Action
|
||||
->label('audits.resource', 'rule/{request.ruleId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'deleteRule',
|
||||
description: <<<EOT
|
||||
Delete a proxy rule by its unique ID.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
@@ -58,6 +59,7 @@ class Delete extends Action
|
||||
->inject('dbForPlatform')
|
||||
->inject('queueForDeletes')
|
||||
->inject('queueForEvents')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
@@ -67,15 +69,16 @@ class Delete extends Action
|
||||
Document $project,
|
||||
Database $dbForPlatform,
|
||||
DeleteEvent $queueForDeletes,
|
||||
Event $queueForEvents
|
||||
Event $queueForEvents,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForPlatform->deleteDocument('rules', $rule->getId());
|
||||
$authorization->skip(fn () => $dbForPlatform->deleteDocument('rules', $rule->getId()));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
|
||||
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -45,12 +46,14 @@ class Create extends Action
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'createFunctionRule',
|
||||
description: <<<EOT
|
||||
Create a new proxy rule for executing Appwrite Function on custom domain.
|
||||
|
||||
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
@@ -72,11 +75,25 @@ class Create extends Action
|
||||
->inject('dbForProject')
|
||||
->inject('platform')
|
||||
->inject('log')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $domain, string $functionId, string $branch, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
|
||||
{
|
||||
public function action(
|
||||
string $domain,
|
||||
string $functionId,
|
||||
string $branch,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Certificate $publisherForCertificates,
|
||||
Event $queueForEvents,
|
||||
Database $dbForPlatform,
|
||||
Database $dbForProject,
|
||||
array $platform,
|
||||
Log $log,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
|
||||
$this->validateDomainRestrictions($domain, $platform);
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
@@ -126,7 +143,7 @@ class Create extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$rule = $dbForPlatform->createDocument('rules', $rule);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
|
||||
} catch (Duplicate $e) {
|
||||
throw new Exception(Exception::RULE_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -144,6 +161,13 @@ class Create extends Action
|
||||
|
||||
$queueForEvents->setParam('ruleId', $rule->getId());
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
@@ -34,12 +35,12 @@ class Get extends Action
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'getRule',
|
||||
description: <<<EOT
|
||||
Get a proxy rule by its unique ID.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -51,6 +52,7 @@ class Get extends Action
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
@@ -58,15 +60,16 @@ class Get extends Action
|
||||
string $ruleId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
Database $dbForPlatform,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
|
||||
$certificate = $authorization->skip(fn () => $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')));
|
||||
|
||||
// Give priority to certificate generation logs if present
|
||||
if (!empty($certificate->getAttribute('logs', ''))) {
|
||||
@@ -75,6 +78,13 @@ class Get extends Action
|
||||
|
||||
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
|
||||
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -46,12 +47,14 @@ class Create extends Action
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'createRedirectRule',
|
||||
description: <<<EOT
|
||||
Create a new proxy rule for to redirect from custom domain to another domain.
|
||||
|
||||
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
@@ -75,11 +78,27 @@ class Create extends Action
|
||||
->inject('dbForProject')
|
||||
->inject('platform')
|
||||
->inject('log')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
|
||||
{
|
||||
public function action(
|
||||
string $domain,
|
||||
string $url,
|
||||
int $statusCode,
|
||||
string $resourceId,
|
||||
string $resourceType,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Certificate $publisherForCertificates,
|
||||
Event $queueForEvents,
|
||||
Database $dbForPlatform,
|
||||
Database $dbForProject,
|
||||
array $platform,
|
||||
Log $log,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
|
||||
$this->validateDomainRestrictions($domain, $platform);
|
||||
|
||||
$collection = match ($resourceType) {
|
||||
@@ -131,7 +150,7 @@ class Create extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$rule = $dbForPlatform->createDocument('rules', $rule);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
|
||||
} catch (Duplicate $e) {
|
||||
throw new Exception(Exception::RULE_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -149,6 +168,13 @@ class Create extends Action
|
||||
|
||||
$queueForEvents->setParam('ruleId', $rule->getId());
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
|
||||
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -45,12 +46,14 @@ class Create extends Action
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'createSiteRule',
|
||||
description: <<<EOT
|
||||
Create a new proxy rule for serving Appwrite Site on custom domain.
|
||||
|
||||
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
@@ -72,11 +75,25 @@ class Create extends Action
|
||||
->inject('dbForProject')
|
||||
->inject('platform')
|
||||
->inject('log')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $domain, string $siteId, ?string $branch, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
|
||||
{
|
||||
public function action(
|
||||
string $domain,
|
||||
string $siteId,
|
||||
?string $branch,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Certificate $publisherForCertificates,
|
||||
Event $queueForEvents,
|
||||
Database $dbForPlatform,
|
||||
Database $dbForProject,
|
||||
array $platform,
|
||||
Log $log,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
|
||||
$this->validateDomainRestrictions($domain, $platform);
|
||||
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
@@ -126,7 +143,7 @@ class Create extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
$rule = $dbForPlatform->createDocument('rules', $rule);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
|
||||
} catch (Duplicate $e) {
|
||||
throw new Exception(Exception::RULE_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -144,6 +161,13 @@ class Create extends Action
|
||||
|
||||
$queueForEvents->setParam('ruleId', $rule->getId());
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
|
||||
+19
-15
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Verification;
|
||||
namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Status;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Publisher\Certificate;
|
||||
@@ -13,6 +13,7 @@ use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -32,8 +33,9 @@ class Update extends Action
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/proxy/rules/:ruleId/verification')
|
||||
->desc('Update rule verification status')
|
||||
->setHttpPath('/v1/proxy/rules/:ruleId/status')
|
||||
->httpAlias('/v1/proxy/rules/:ruleId/verification')
|
||||
->desc('Update rule status')
|
||||
->groups(['api', 'proxy'])
|
||||
->label('scope', 'rules.write')
|
||||
->label('event', 'rules.[ruleId].update')
|
||||
@@ -41,12 +43,12 @@ class Update extends Action
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'updateRuleVerification',
|
||||
group: 'rules',
|
||||
name: 'updateRuleStatus',
|
||||
description: <<<EOT
|
||||
Retry getting verification process of a proxy rule. This endpoint triggers domain verification by checking DNS records (CNAME) against the configured target domain. If verification is successful, a TLS certificate will be automatically provisioned for the domain.
|
||||
If not succeeded yet, retry verification process of a proxy rule domain. This endpoint triggers domain verification by checking DNS records. If verification is successful, a TLS certificate will be automatically provisioned for the domain asynchronously in the background.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -61,6 +63,7 @@ class Update extends Action
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->inject('log')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
@@ -71,9 +74,10 @@ class Update extends Action
|
||||
Event $queueForEvents,
|
||||
Document $project,
|
||||
Database $dbForPlatform,
|
||||
Log $log
|
||||
Log $log,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
@@ -90,22 +94,22 @@ class Update extends Action
|
||||
try {
|
||||
$this->verifyRule($rule, $log);
|
||||
// Reset logs and status for the rule
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
$rule = $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'logs' => '',
|
||||
'status' => RULE_STATUS_CERTIFICATE_GENERATING,
|
||||
]));
|
||||
])));
|
||||
|
||||
$certificateId = $rule->getAttribute('certificateId', '');
|
||||
// Reset logs for the associated certificate.
|
||||
if (!empty($certificateId)) {
|
||||
$certificate = $dbForPlatform->updateDocument('certificates', $certificateId, new Document([
|
||||
$certificate = $authorization->skip(fn () => $dbForPlatform->updateDocument('certificates', $certificateId, new Document([
|
||||
'logs' => '',
|
||||
]));
|
||||
])));
|
||||
}
|
||||
} catch (Exception $err) {
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'$updatedAt' => DateTime::now(),
|
||||
]));
|
||||
])));
|
||||
throw $err;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
@@ -39,12 +40,12 @@ class XList extends Action
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
group: 'rules',
|
||||
name: 'listRules',
|
||||
description: <<<EOT
|
||||
Get a list of all the proxy rules. You can use the query params to filter your results.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -53,21 +54,23 @@ class XList extends Action
|
||||
]
|
||||
))
|
||||
->param('queries', [], new Rules(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Rules::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true, deprecated: true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->inject('authorization')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
array $queries,
|
||||
bool $total,
|
||||
string $search,
|
||||
bool $includeTotal,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
Database $dbForPlatform,
|
||||
Authorization $authorization,
|
||||
) {
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
@@ -91,7 +94,7 @@ class XList extends Action
|
||||
}
|
||||
|
||||
$ruleId = $cursor->getValue();
|
||||
$cursorDocument = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
$cursorDocument = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Rule '{$ruleId}' for the 'cursor' value not found.");
|
||||
@@ -102,9 +105,9 @@ class XList extends Action
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$rules = $dbForPlatform->find('rules', $queries);
|
||||
$rules = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
|
||||
foreach ($rules as $rule) {
|
||||
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
|
||||
$certificate = $authorization->skip(fn () => $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')));
|
||||
|
||||
// Give priority to certificate generation logs if present
|
||||
if (!empty($certificate->getAttribute('logs', ''))) {
|
||||
@@ -112,11 +115,18 @@ class XList extends Action
|
||||
}
|
||||
|
||||
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
|
||||
|
||||
// Rename 'created' status to 'unverified' for consistency.
|
||||
// 'verifying' and 'verified' statuses stay as is.
|
||||
// 'unverified' in the meaning of failed certificate generation stays as is.
|
||||
if ($rule->getAttribute('status') === 'created') {
|
||||
$rule->setAttribute('status', 'unverified');
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'rules' => $rules,
|
||||
'total' => $includeTotal ? $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT) : 0,
|
||||
'total' => $total ? $authorization->skip(fn () => $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT)) : 0,
|
||||
]), Response::MODEL_PROXY_RULE_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Appwrite\Platform\Modules\Proxy\Http\Rules\Function\Create as CreateFunction
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\Get as GetRule;
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\Redirect\Create as CreateRedirectRule;
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\Site\Create as CreateSiteRule;
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\Verification\Update as UpdateRuleVerification;
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\Status\Update as UpdateRuleStatus;
|
||||
use Appwrite\Platform\Modules\Proxy\Http\Rules\XList as ListRules;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
@@ -26,6 +26,6 @@ class Http extends Service
|
||||
$this->addAction(GetRule::getName(), new GetRule());
|
||||
$this->addAction(ListRules::getName(), new ListRules());
|
||||
$this->addAction(DeleteRule::getName(), new DeleteRule());
|
||||
$this->addAction(UpdateRuleVerification::getName(), new UpdateRuleVerification());
|
||||
$this->addAction(UpdateRuleStatus::getName(), new UpdateRuleStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +437,15 @@ class OpenAPI3 extends Format
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
|
||||
break;
|
||||
case \Utopia\Database\Validator\BigInt::class:
|
||||
// BigInt validator reports Database::VAR_BIGINT, but OpenAPI expects scalar types.
|
||||
// We expose it as int64 to keep schema consistent with Column/Attribute models.
|
||||
$node['schema']['type'] = 'integer';
|
||||
$node['schema']['format'] = 'int64';
|
||||
if (!empty($param['example'])) {
|
||||
$node['schema']['x-example'] = $param['example'];
|
||||
}
|
||||
break;
|
||||
case \Utopia\Validator\Boolean::class:
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['x-example'] = ($param['example'] ?? '') ?: false;
|
||||
|
||||
@@ -23,6 +23,7 @@ class Attributes extends Validator
|
||||
protected array $supportedTypes = [
|
||||
Database::VAR_STRING,
|
||||
Database::VAR_INTEGER,
|
||||
Database::VAR_BIGINT,
|
||||
Database::VAR_FLOAT,
|
||||
Database::VAR_BOOLEAN,
|
||||
Database::VAR_DATETIME,
|
||||
@@ -181,9 +182,9 @@ class Attributes extends Validator
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate signed only for integer/float types
|
||||
if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer or float types";
|
||||
// Validate signed only for integer/bigint/float types
|
||||
if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_BIGINT, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer, bigint or float types";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -199,10 +200,10 @@ class Attributes extends Validator
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate min/max range for integer/float
|
||||
// Validate min/max range for integer/bigint/float
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer or float types";
|
||||
if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_BIGINT, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer, bigint or float types";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -264,7 +265,7 @@ class Attributes extends Validator
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
$min = $attribute['min'] ?? \PHP_INT_MIN;
|
||||
$max = $attribute['max'] ?? \PHP_INT_MAX;
|
||||
$rangeValidator = new Range($min, $max, Database::VAR_INTEGER);
|
||||
$rangeValidator = new Range($min, $max, Range::TYPE_INTEGER);
|
||||
if (!$rangeValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be between $min and $max";
|
||||
return false;
|
||||
@@ -272,6 +273,23 @@ class Attributes extends Validator
|
||||
}
|
||||
break;
|
||||
|
||||
case Database::VAR_BIGINT:
|
||||
if (!is_int($attribute['default'])) {
|
||||
$this->message = "Default value for bigint attribute '" . $attribute['key'] . "' must be an integer";
|
||||
return false;
|
||||
}
|
||||
// Validate within range if min/max specified
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
$min = $attribute['min'] ?? \PHP_INT_MIN;
|
||||
$max = $attribute['max'] ?? \PHP_INT_MAX;
|
||||
$rangeValidator = new Range($min, $max, Range::TYPE_INTEGER);
|
||||
if (!$rangeValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for bigint attribute '" . $attribute['key'] . "' must be between $min and $max";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Database::VAR_FLOAT:
|
||||
if (!is_float($attribute['default']) && !is_int($attribute['default'])) {
|
||||
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be a number";
|
||||
@@ -281,7 +299,7 @@ class Attributes extends Validator
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
$min = $attribute['min'] ?? -\PHP_FLOAT_MAX;
|
||||
$max = $attribute['max'] ?? \PHP_FLOAT_MAX;
|
||||
$rangeValidator = new Range($min, $max, Database::VAR_FLOAT);
|
||||
$rangeValidator = new Range($min, $max, Range::TYPE_FLOAT);
|
||||
if (!$rangeValidator->isValid((float)$attribute['default'])) {
|
||||
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be between $min and $max";
|
||||
return false;
|
||||
|
||||
@@ -72,6 +72,7 @@ class Response extends SwooleResponse
|
||||
public const MODEL_ATTRIBUTE_LIST = 'attributeList';
|
||||
public const MODEL_ATTRIBUTE_STRING = 'attributeString';
|
||||
public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger';
|
||||
public const MODEL_ATTRIBUTE_BIGINT = 'attributeBigint';
|
||||
public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat';
|
||||
public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean';
|
||||
public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail';
|
||||
@@ -95,6 +96,7 @@ class Response extends SwooleResponse
|
||||
public const MODEL_COLUMN_LIST = 'columnList';
|
||||
public const MODEL_COLUMN_STRING = 'columnString';
|
||||
public const MODEL_COLUMN_INTEGER = 'columnInteger';
|
||||
public const MODEL_COLUMN_BIGINT = 'columnBigint';
|
||||
public const MODEL_COLUMN_FLOAT = 'columnFloat';
|
||||
public const MODEL_COLUMN_BOOLEAN = 'columnBoolean';
|
||||
public const MODEL_COLUMN_EMAIL = 'columnEmail';
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class AttributeBigInt extends Attribute
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('key', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute Key.',
|
||||
'default' => '',
|
||||
'example' => 'count',
|
||||
])
|
||||
->addRule('type', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Attribute type.',
|
||||
'default' => '',
|
||||
'example' => 'bigint',
|
||||
])
|
||||
->addRule('min', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Minimum value to enforce for new documents.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 1,
|
||||
])
|
||||
->addRule('max', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Maximum value to enforce for new documents.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 10,
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 10,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public array $conditions = [
|
||||
'type' => 'bigint'
|
||||
];
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributeBigInt';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_BIGINT;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ class AttributeList extends Model
|
||||
->addRule('attributes', [
|
||||
'type' => [
|
||||
Response::MODEL_ATTRIBUTE_BOOLEAN,
|
||||
// BigInt must come before Integer: response model dispatch is "first match wins",
|
||||
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
|
||||
Response::MODEL_ATTRIBUTE_BIGINT,
|
||||
Response::MODEL_ATTRIBUTE_INTEGER,
|
||||
Response::MODEL_ATTRIBUTE_FLOAT,
|
||||
Response::MODEL_ATTRIBUTE_EMAIL,
|
||||
|
||||
@@ -62,6 +62,9 @@ class Collection extends Model
|
||||
->addRule('attributes', [
|
||||
'type' => [
|
||||
Response::MODEL_ATTRIBUTE_BOOLEAN,
|
||||
// BigInt must come before Integer: response model dispatch is "first match wins",
|
||||
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
|
||||
Response::MODEL_ATTRIBUTE_BIGINT,
|
||||
Response::MODEL_ATTRIBUTE_INTEGER,
|
||||
Response::MODEL_ATTRIBUTE_FLOAT,
|
||||
Response::MODEL_ATTRIBUTE_EMAIL,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class ColumnBigInt extends Column
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('key', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Column Key.',
|
||||
'default' => '',
|
||||
'example' => 'count',
|
||||
])
|
||||
->addRule('type', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Column type.',
|
||||
'default' => '',
|
||||
'example' => 'bigint',
|
||||
])
|
||||
->addRule('min', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Minimum value to enforce for new documents.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 1,
|
||||
])
|
||||
->addRule('max', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Maximum value to enforce for new documents.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 10,
|
||||
])
|
||||
->addRule('default', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'format' => 'int64',
|
||||
'description' => 'Default value for column when not provided. Cannot be set when column is required.',
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'example' => 10,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public array $conditions = [
|
||||
'type' => 'bigint'
|
||||
];
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'ColumnBigInt';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_COLUMN_BIGINT;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ class ColumnList extends Model
|
||||
->addRule('columns', [
|
||||
'type' => [
|
||||
Response::MODEL_COLUMN_BOOLEAN,
|
||||
// BigInt must come before Integer: response model dispatch is "first match wins",
|
||||
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
|
||||
Response::MODEL_COLUMN_BIGINT,
|
||||
Response::MODEL_COLUMN_INTEGER,
|
||||
Response::MODEL_COLUMN_FLOAT,
|
||||
Response::MODEL_COLUMN_EMAIL,
|
||||
|
||||
@@ -74,7 +74,7 @@ class Rule extends Model
|
||||
])
|
||||
->addRule('deploymentResourceId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'ID deployment\'s resource. Used if type is "deployment"',
|
||||
'description' => 'ID of deployment\'s resource (site or function ID). Used if type is "deployment"',
|
||||
'default' => '',
|
||||
'example' => 'n3u9feiwmf',
|
||||
])
|
||||
@@ -86,10 +86,10 @@ class Rule extends Model
|
||||
])
|
||||
->addRule('status', [
|
||||
'type' => self::TYPE_ENUM,
|
||||
'description' => 'Domain verification status. Possible values are "created", "verifying", "verified" and "unverified"',
|
||||
'default' => 'created',
|
||||
'description' => 'Domain verification status. Possible values are "unverified", "verifying", "verified"',
|
||||
'default' => 'unverified',
|
||||
'example' => 'verified',
|
||||
'enum' => ['created', 'verifying', 'verified', 'unverified'],
|
||||
'enum' => ['unverified', 'verifying', 'verified'],
|
||||
])
|
||||
->addRule('logs', [
|
||||
'type' => self::TYPE_STRING,
|
||||
|
||||
@@ -63,6 +63,9 @@ class Table extends Model
|
||||
->addRule('columns', [
|
||||
'type' => [
|
||||
Response::MODEL_COLUMN_BOOLEAN,
|
||||
// BigInt must come before Integer: response model dispatch is "first match wins",
|
||||
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
|
||||
Response::MODEL_COLUMN_BIGINT,
|
||||
Response::MODEL_COLUMN_INTEGER,
|
||||
Response::MODEL_COLUMN_FLOAT,
|
||||
Response::MODEL_COLUMN_EMAIL,
|
||||
|
||||
@@ -1257,7 +1257,6 @@ trait MigrationsBase
|
||||
'max' => 65,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $response['headers']['status-code']);
|
||||
$this->assertEquals($response['body']['key'], 'age');
|
||||
$this->assertEquals($response['body']['type'], 'integer');
|
||||
@@ -1574,6 +1573,19 @@ trait MigrationsBase
|
||||
|
||||
$this->assertEquals(202, $varchar['headers']['status-code']);
|
||||
|
||||
$bigint = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/bigint', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'bigint',
|
||||
'min' => 2147483648,
|
||||
'max' => 9223372036854775807,
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $bigint['headers']['status-code']);
|
||||
|
||||
$mediumtext = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/mediumtext', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -1624,6 +1636,7 @@ trait MigrationsBase
|
||||
'mediumtext' => 'mediumText',
|
||||
'longtext' => 'longText',
|
||||
'varchar' => 'varchar',
|
||||
'bigint' => 2147483648 + $i,
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -1712,6 +1725,8 @@ trait MigrationsBase
|
||||
$this->assertStringContainsString('mediumText', $csvData, 'CSV should contain the medium column header');
|
||||
$this->assertStringContainsString('longText', $csvData, 'CSV should contain the long text column header');
|
||||
$this->assertStringContainsString('varchar', $csvData, 'CSV should contain the varchar column header');
|
||||
$this->assertStringContainsString('bigint', $csvData, 'CSV should contain the bigint column header');
|
||||
$this->assertStringContainsString('2147483649', $csvData, 'CSV should contain bigint test data');
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Tests\E2E\Services\Project;
|
||||
use PHPUnit\Framework\Attributes\Before;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
trait OAuth2Base
|
||||
{
|
||||
@@ -172,6 +173,40 @@ trait OAuth2Base
|
||||
$this->assertNotContains('mock-unverified', $ids);
|
||||
}
|
||||
|
||||
public function testListOAuth2ProvidersTotalFalse(): void
|
||||
{
|
||||
$response = $this->listOAuth2Providers(total: false);
|
||||
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertSame(0, $response['body']['total']);
|
||||
$this->assertGreaterThan(0, \count($response['body']['providers']));
|
||||
}
|
||||
|
||||
public function testListOAuth2ProvidersWithLimit(): void
|
||||
{
|
||||
$response = $this->listOAuth2Providers([
|
||||
Query::limit(1)->toString(),
|
||||
]);
|
||||
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertCount(1, $response['body']['providers']);
|
||||
$this->assertGreaterThan(1, $response['body']['total']);
|
||||
}
|
||||
|
||||
public function testListOAuth2ProvidersWithOffset(): void
|
||||
{
|
||||
$listAll = $this->listOAuth2Providers();
|
||||
$this->assertSame(200, $listAll['headers']['status-code']);
|
||||
|
||||
$listOffset = $this->listOAuth2Providers([
|
||||
Query::offset(1)->toString(),
|
||||
]);
|
||||
|
||||
$this->assertSame(200, $listOffset['headers']['status-code']);
|
||||
$this->assertCount(\count($listAll['body']['providers']) - 1, $listOffset['body']['providers']);
|
||||
$this->assertSame($listAll['body']['total'], $listOffset['body']['total']);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Get OAuth2 provider
|
||||
// =========================================================================
|
||||
@@ -188,6 +223,28 @@ trait OAuth2Base
|
||||
$this->assertSame('', $response['body']['clientSecret']);
|
||||
}
|
||||
|
||||
public function testGetOAuth2ProviderWithAlias(): void
|
||||
{
|
||||
// The action declares the canonical param name as `providerId` and
|
||||
// registers `provider` as an alias so that older SDK versions that
|
||||
// send the provider in the query string continue to work.
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
];
|
||||
$headers = \array_merge($headers, $this->getHeaders());
|
||||
|
||||
// Call with `provider` in query string (legacy behaviour)
|
||||
$response = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/project/oauth2/github?provider=github',
|
||||
$headers,
|
||||
);
|
||||
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertSame('github', $response['body']['$id']);
|
||||
}
|
||||
|
||||
public function testGetOAuth2ProviderClientSecretWriteOnly(): void
|
||||
{
|
||||
$this->updateOAuth2('amazon', [
|
||||
@@ -221,19 +278,23 @@ trait OAuth2Base
|
||||
|
||||
public function testGetOAuth2ProviderUnsupported(): void
|
||||
{
|
||||
// The `providerId` param is validated by a WhiteList of registered
|
||||
// OAuth2 provider keys, so an unknown value is rejected at validation
|
||||
// time — before the action runs — and surfaces as a generic argument
|
||||
// error rather than `project_provider_unsupported`.
|
||||
$response = $this->getOAuth2Provider('not-a-real-provider');
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
$this->assertSame('project_provider_unsupported', $response['body']['type']);
|
||||
$this->assertSame('general_argument_invalid', $response['body']['type']);
|
||||
}
|
||||
|
||||
public function testGetOAuth2ProviderRegisteredInConfigButNoUpdateClass(): void
|
||||
{
|
||||
// `mock` is present in oAuthProviders config (enabled: true) but is NOT
|
||||
// registered in Base::getProviderActions(). Get::action has two
|
||||
// separate `unsupported` throw branches — testGetOAuth2ProviderUnsupported
|
||||
// covers the first (provider missing from config); this covers the
|
||||
// second (provider in config but missing from the action registry).
|
||||
// `mock` is present in oAuthProviders config (enabled: true) but is
|
||||
// NOT registered in Base::getProviderActions(). It passes the
|
||||
// WhiteList validator (which only checks config membership) and
|
||||
// reaches the action body, where the action-registry check throws
|
||||
// `project_provider_unsupported`.
|
||||
$response = $this->getOAuth2Provider('mock');
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
@@ -2573,7 +2634,7 @@ trait OAuth2Base
|
||||
);
|
||||
}
|
||||
|
||||
protected function getOAuth2Provider(string $provider, bool $authenticated = true): mixed
|
||||
protected function getOAuth2Provider(string $providerId, bool $authenticated = true): mixed
|
||||
{
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
@@ -2586,13 +2647,23 @@ trait OAuth2Base
|
||||
|
||||
return $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/project/oauth2/' . $provider,
|
||||
'/project/oauth2/' . $providerId,
|
||||
$headers,
|
||||
);
|
||||
}
|
||||
|
||||
protected function listOAuth2Providers(bool $authenticated = true): mixed
|
||||
protected function listOAuth2Providers(?array $queries = null, ?bool $total = null, bool $authenticated = true): mixed
|
||||
{
|
||||
$params = [];
|
||||
|
||||
if ($queries !== null) {
|
||||
$params['queries'] = $queries;
|
||||
}
|
||||
|
||||
if ($total !== null) {
|
||||
$params['total'] = $total;
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -2606,6 +2677,7 @@ trait OAuth2Base
|
||||
Client::METHOD_GET,
|
||||
'/project/oauth2',
|
||||
$headers,
|
||||
$params,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,298 +2,753 @@
|
||||
|
||||
namespace Tests\E2E\Services\Proxy;
|
||||
|
||||
use Appwrite\ID;
|
||||
use Appwrite\Tests\Async;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\System\System;
|
||||
|
||||
trait ProxyBase
|
||||
{
|
||||
use Async;
|
||||
use ProxyHelpers;
|
||||
|
||||
protected function listRules(array $params = []): mixed
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createAPIRule(string $domain): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
// Cleanup for testRuleVerification test
|
||||
// Required as it uses static domain name
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::endsWith('domain', 'webapp.com')->toString(),
|
||||
Query::limit(1000)->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$ruleId = $rule['$id'];
|
||||
$response = $this->deleteRule($ruleId);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
return $rule;
|
||||
if ($rules['body']['total'] > 0) {
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::endsWith('domain', 'webapp.com')->toString(),
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, count($rules['body']['rules']));
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateRuleVerification(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_PATCH, '/proxy/rules/' . $ruleId . '/verification', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createSiteRule(string $domain, string $siteId, string $branch = ''): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'siteId' => $siteId,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function getRule(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/redirect', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'url' => $url,
|
||||
'statusCode' => $statusCode,
|
||||
'resourceType' => $resourceType,
|
||||
'resourceId' => $resourceId,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createFunctionRule(string $domain, string $functionId, string $branch = ''): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/function', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'functionId' => $functionId,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function deleteRule(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_DELETE, '/proxy/rules/' . $ruleId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function setupAPIRule(string $domain): string
|
||||
public function testCreateRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-api.myapp.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals($domain, $rule['body']['domain']);
|
||||
$this->assertEquals('manual', $rule['body']['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rule['body']);
|
||||
$this->assertArrayHasKey('domain', $rule['body']);
|
||||
$this->assertArrayHasKey('type', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectUrl', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rule['body']);
|
||||
$this->assertArrayHasKey('logs', $rule['body']);
|
||||
$this->assertArrayHasKey('renewAt', $rule['body']);
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
protected function setupRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): string
|
||||
{
|
||||
$rule = $this->createRedirectRule($domain, $url, $statusCode, $resourceType, $resourceId);
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(409, $rule['headers']['status-code']);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function setupFunctionRule(string $domain, string $functionId, string $branch = ''): string
|
||||
{
|
||||
$rule = $this->createFunctionRule($domain, $functionId, $branch);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function setupSiteRule(string $domain, string $siteId, string $branch = ''): string
|
||||
{
|
||||
$rule = $this->createSiteRule($domain, $siteId, $branch);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function cleanupRule(string $ruleId): void
|
||||
{
|
||||
$rule = $this->deleteRule($ruleId);
|
||||
$this->assertEquals(204, $rule['headers']['status-code'], 'Failed to cleanup rule: ' . \json_encode($rule));
|
||||
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
protected function cleanupSite(string $siteId): void
|
||||
public function testCreateRuleSetup(): void
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(204, $site['headers']['status-code'], 'Failed to cleanup site: ' . \json_encode($site));
|
||||
$ruleId = $this->setupAPIRule(\uniqid() . '-api2.myapp.com');
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
protected function cleanupFunction(string $functionId): void
|
||||
public function testCreateRuleApex(): void
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(204, $function['headers']['status-code'], 'Failed to cleanup function: ' . \json_encode($function));
|
||||
$domain = \uniqid() . '.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
}
|
||||
|
||||
protected function setupSite(): mixed
|
||||
public function testCreateRuleVcs(): void
|
||||
{
|
||||
// Site
|
||||
$site = $this->client->call(Client::METHOD_POST, '/sites', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Proxy site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
$domain = \uniqid() . '-vcs.myapp.com';
|
||||
|
||||
$this->assertEquals($site['headers']['status-code'], 201, 'Setup site failed with status code: ' . $site['headers']['status-code'] . ' and response: ' . json_encode($site['body'], JSON_PRETTY_PRINT));
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$siteId = $site['body']['$id'];
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
// Deployment
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'code' => $this->packageSite('static'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
$rule = $this->createSiteRule('commit-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
$rule = $this->createSiteRule('branch-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$site = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]));
|
||||
$this->assertEquals($deploymentId, $site['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($site['body'], JSON_PRETTY_PRINT));
|
||||
}, 120000, 500);
|
||||
$rule = $this->createSiteRule('anything-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
return ['siteId' => $siteId, 'deploymentId' => $deploymentId];
|
||||
$sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0];
|
||||
$domain = \uniqid() . '-vcs.' . $sitesDomain;
|
||||
|
||||
$rule = $this->createSiteRule('commit-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('branch-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('subdomain.anything-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('anything-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
}
|
||||
|
||||
protected function setupFunction(): mixed
|
||||
public function testCreateAPIRule(): void
|
||||
{
|
||||
// Function
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'name' => 'Proxy Function',
|
||||
'entrypoint' => 'index.js',
|
||||
'commands' => '',
|
||||
'execute' => ['any']
|
||||
$domain = \uniqid() . '-api.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/versions');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$ruleId = $this->setupAPIRule($domain);
|
||||
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/versions');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(APP_VERSION_STABLE, $response['body']['server']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$rule = $this->createAPIRule('http://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule('https://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule('wss://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule($domain . '/some-path');
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateRedirectRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-redirect.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/todos/1');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$siteId = $this->setupSite()['siteId'];
|
||||
|
||||
$ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 301, 'site', $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/todos/1');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['id']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['id']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', followRedirects: false);
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']);
|
||||
|
||||
$domain = \uniqid() . '-redirect-307.custom.localhost';
|
||||
$ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 307, 'site', $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', followRedirects: false);
|
||||
$this->assertEquals(307, $response['headers']['status-code']);
|
||||
$this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('type', ['redirect'])->toString(),
|
||||
Query::equal('trigger', ['manual'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
|
||||
$this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'], JSON_PRETTY_PRINT));
|
||||
$this->cleanupSite($siteId);
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
$functionId = $function['body']['$id'];
|
||||
public function testCreateFunctionRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-function.custom.localhost';
|
||||
|
||||
// Deployment
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/ping');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$setup = $this->setupFunction();
|
||||
$functionId = $setup['functionId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($functionId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupFunctionRule($domain, $functionId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/ping');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($functionId, $response['body']['APPWRITE_FUNCTION_ID']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]));
|
||||
$this->assertEquals($deploymentId, $function['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($function['body'], JSON_PRETTY_PRINT));
|
||||
}, 100000, 500);
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['function'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$functionId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
return ['functionId' => $functionId, 'deploymentId' => $deploymentId];
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentId', [$deploymentId])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
});
|
||||
}
|
||||
|
||||
private function packageSite(string $site): CURLFile
|
||||
public function testCreateSiteRule(): void
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$domain = \uniqid() . '-site.custom.localhost';
|
||||
|
||||
$folderPath = realpath(__DIR__ . '/../../../resources/sites') . "/$site";
|
||||
$tarPath = "$folderPath/code.tar.gz";
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz --exclude node_modules -czf code.tar.gz .", '', $stdout, $stderr);
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/contact');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupSiteRule($domain, $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertSame(200, $rule['headers']['status-code']);
|
||||
$this->assertSame('unverified', $rule['body']['status']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/contact');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('Contact page', $response['body']);
|
||||
|
||||
// Wildcard domains automatically get verified status
|
||||
$domains = [
|
||||
\uniqid() . '.sites.localhost',
|
||||
\uniqid() . '.rebranded.localhost',
|
||||
];
|
||||
foreach ($domains as $domain) {
|
||||
$wildcardRuleId = $this->setupSiteRule($domain, $siteId);
|
||||
$this->assertNotEmpty($wildcardRuleId);
|
||||
$rule = $this->getRule($wildcardRuleId);
|
||||
$this->assertSame(200, $rule['headers']['status-code']);
|
||||
$this->assertSame('verified', $rule['body']['status']);
|
||||
$this->cleanupRule($wildcardRuleId);
|
||||
}
|
||||
|
||||
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('trigger', ['deployment'])->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $rules['body']['total']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentId', [$deploymentId])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
});
|
||||
}
|
||||
|
||||
private function packageFunction(string $function): CURLFile
|
||||
public function testCreateSiteBranchRule(): void
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$domain = \uniqid() . '-site-branch.custom.localhost';
|
||||
|
||||
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
||||
$tarPath = "$folderPath/code.tar.gz";
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz --exclude node_modules -czf code.tar.gz .", '', $stdout, $stderr);
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||
$ruleId = $this->setupSiteRule($domain, $siteId, 'dev');
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionBranchRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-function-branch.custom.localhost';
|
||||
|
||||
$setup = $this->setupFunction();
|
||||
$functionId = $setup['functionId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($functionId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupFunctionRule($domain, $functionId, 'dev');
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testUpdateRule(): void
|
||||
{
|
||||
// Create function appwrite-network domain
|
||||
$functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0];
|
||||
$domain = \uniqid() . '-cname-api.' . $functionsDomain;
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// Create site appwrite-network domain
|
||||
$sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0];
|
||||
$domain = \uniqid() . '-cname-api.' . $sitesDomain;
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// Create + update
|
||||
$domain = \uniqid() . '-cname-api.custom.com';
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testGetRule()
|
||||
{
|
||||
$domain = \uniqid() . '-get.custom.localhost';
|
||||
$ruleId = $this->setupAPIRule($domain);
|
||||
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals($domain, $rule['body']['domain']);
|
||||
$this->assertEquals('manual', $rule['body']['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rule['body']);
|
||||
$this->assertArrayHasKey('domain', $rule['body']);
|
||||
$this->assertArrayHasKey('type', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectUrl', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rule['body']);
|
||||
$this->assertArrayHasKey('logs', $rule['body']);
|
||||
$this->assertArrayHasKey('renewAt', $rule['body']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testListRules()
|
||||
{
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$rule = $this->deleteRule($rule['$id']);
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
$rule1Domain = \uniqid() . '-list1.custom.localhost';
|
||||
$rule1Id = $this->setupAPIRule($rule1Domain);
|
||||
$this->assertNotEmpty($rule1Id);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(1, $rules['body']['total']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule1Domain, $rules['body']['rules'][0]['domain']);
|
||||
|
||||
$this->assertEquals('manual', $rules['body']['rules'][0]['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('domain', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('type', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('redirectUrl', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentId', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('logs', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('renewAt', $rules['body']['rules'][0]);
|
||||
|
||||
$rule2Domain = \uniqid() . '-list1.custom.localhost';
|
||||
$rule2Id = $this->setupAPIRule($rule2Domain);
|
||||
$this->assertNotEmpty($rule2Id);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
$this->assertCount(2, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('$id', [$rule1Id])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule1Domain, $rules['body']['rules'][0]['domain']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::orderDesc('$id')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(2, $rules['body']['rules']);
|
||||
$this->assertEquals($rule2Id, $rules['body']['rules'][0]['$id']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('domain', [$rule2Domain])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule2Id, $rules['body']['rules'][0]['$id']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule1Domain,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleIds = \array_column($rules['body']['rules'], '$id');
|
||||
$this->assertContains($rule1Id, $ruleIds);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule2Domain,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleIds = \array_column($rules['body']['rules'], '$id');
|
||||
$this->assertContains($rule2Id, $ruleIds);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule1Id,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleDomains = \array_column($rules['body']['rules'], 'domain');
|
||||
$this->assertContains($rule1Domain, $ruleDomains);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule2Id,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleDomains = \array_column($rules['body']['rules'], 'domain');
|
||||
$this->assertContains($rule2Domain, $ruleDomains);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$rule = $this->deleteRule($rule['$id']);
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
}
|
||||
|
||||
public function testRuleVerification(): void
|
||||
{
|
||||
|
||||
// 1. Site rule can verify
|
||||
$site = $this->setupSite();
|
||||
$siteId = $site['siteId'];
|
||||
|
||||
$rule = $this->createSiteRule('stage-site.webapp.com', $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
$this->assertNotEmpty($rule['body']['$id']);
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals($ruleId, $rule['body']['$id']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
// 2. Function rule can verify
|
||||
$function = $this->setupFunction();
|
||||
$functionId = $function['functionId'];
|
||||
|
||||
$rule = $this->createFunctionRule('stage-function.webapp.com', $functionId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$rule = $this->createAPIRule('stage-site.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['logs']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
||||
// 3. Wrong A record fails to verify
|
||||
$rule = $this->createAPIRule('wrong-a-webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 4. Correct A record can verify
|
||||
$rule = $this->createAPIRule('webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// 5. Correct CNAME record can verify (no CAA record)
|
||||
$rule = $this->createAPIRule('stage.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// 6. Missing CNAME record fails to verify
|
||||
$rule = $this->createAPIRule('stage-missing-cname.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 7. Wrong CNAME record fails to verify
|
||||
$rule = $this->createAPIRule('stage-wrong-cname.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 8. Wrong CAA record fails to verify
|
||||
$rule = $this->createAPIRule('stage-wrong-caa.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleStatus($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 9. Correct CAA record can verify
|
||||
$rule = $this->createAPIRule('stage-correct-caa.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
}
|
||||
|
||||
public function testUpdateRuleVerificationWithSameDataUpdatesTimestamp(): void
|
||||
{
|
||||
$domain = \uniqid() . '-timestamp-test.webapp.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $rule['body']['status']);
|
||||
$this->assertNotEmpty($rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$initialUpdatedAt = $rule['body']['$updatedAt'];
|
||||
$initiallogs = $rule['body']['logs'];
|
||||
|
||||
sleep(1);
|
||||
|
||||
$updatedRule = $this->updateRuleStatus($ruleId);
|
||||
|
||||
$this->assertEquals(400, $updatedRule['headers']['status-code']);
|
||||
$this->assertStringContainsString($initiallogs, $updatedRule['body']['message']);
|
||||
|
||||
$ruleAfterUpdate = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $ruleAfterUpdate['headers']['status-code']);
|
||||
$this->assertEquals('unverified', $ruleAfterUpdate['body']['status']);
|
||||
$this->assertEquals($initiallogs, $ruleAfterUpdate['body']['logs']);
|
||||
$this->assertNotEquals($initialUpdatedAt, $ruleAfterUpdate['body']['$updatedAt']);
|
||||
|
||||
$initialTime = new \DateTime($initialUpdatedAt);
|
||||
$updatedTime = new \DateTime($ruleAfterUpdate['body']['$updatedAt']);
|
||||
$this->assertGreaterThan($initialTime, $updatedTime);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Proxy;
|
||||
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideConsole;
|
||||
|
||||
class ProxyConsoleClientTest extends Scope
|
||||
{
|
||||
use ProxyBase;
|
||||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
}
|
||||
@@ -2,758 +2,13 @@
|
||||
|
||||
namespace Tests\E2E\Services\Proxy;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\System\System;
|
||||
|
||||
class ProxyCustomServerTest extends Scope
|
||||
{
|
||||
use ProxyBase;
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// Cleanup for testRuleVerification test
|
||||
// Required as it uses static domain name
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::endsWith('domain', 'webapp.com')->toString(),
|
||||
Query::limit(1000)->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$ruleId = $rule['$id'];
|
||||
$response = $this->deleteRule($ruleId);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
if ($rules['body']['total'] > 0) {
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::endsWith('domain', 'webapp.com')->toString(),
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, count($rules['body']['rules']));
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreateRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-api.myapp.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals($domain, $rule['body']['domain']);
|
||||
$this->assertEquals('manual', $rule['body']['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rule['body']);
|
||||
$this->assertArrayHasKey('domain', $rule['body']);
|
||||
$this->assertArrayHasKey('type', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectUrl', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rule['body']);
|
||||
$this->assertArrayHasKey('logs', $rule['body']);
|
||||
$this->assertArrayHasKey('renewAt', $rule['body']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(409, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->deleteRule($ruleId);
|
||||
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateRuleSetup(): void
|
||||
{
|
||||
$ruleId = $this->setupAPIRule(\uniqid() . '-api2.myapp.com');
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testCreateRuleApex(): void
|
||||
{
|
||||
$domain = \uniqid() . '.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
}
|
||||
|
||||
public function testCreateRuleVcs(): void
|
||||
{
|
||||
$domain = \uniqid() . '-vcs.myapp.com';
|
||||
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$rule = $this->createSiteRule('commit-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$rule = $this->createSiteRule('branch-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$rule = $this->createSiteRule('anything-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0];
|
||||
$domain = \uniqid() . '-vcs.' . $sitesDomain;
|
||||
|
||||
$rule = $this->createSiteRule('commit-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('branch-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('subdomain.anything-' . $domain, $siteId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createSiteRule('anything-' . $domain, $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
}
|
||||
|
||||
public function testCreateAPIRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-api.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/versions');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$ruleId = $this->setupAPIRule($domain);
|
||||
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/versions');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(APP_VERSION_STABLE, $response['body']['server']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$rule = $this->createAPIRule('http://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule('https://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule('wss://' . $domain);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$rule = $this->createAPIRule($domain . '/some-path');
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateRedirectRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-redirect.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/todos/1');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$siteId = $this->setupSite()['siteId'];
|
||||
|
||||
$ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 301, 'site', $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/todos/1');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['id']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['id']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', followRedirects: false);
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']);
|
||||
|
||||
$domain = \uniqid() . '-redirect-307.custom.localhost';
|
||||
$ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 307, 'site', $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', followRedirects: false);
|
||||
$this->assertEquals(307, $response['headers']['status-code']);
|
||||
$this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('type', ['redirect'])->toString(),
|
||||
Query::equal('trigger', ['manual'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-function.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/ping');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$setup = $this->setupFunction();
|
||||
$functionId = $setup['functionId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($functionId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupFunctionRule($domain, $functionId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/ping');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($functionId, $response['body']['APPWRITE_FUNCTION_ID']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['function'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$functionId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentId', [$deploymentId])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
});
|
||||
}
|
||||
|
||||
public function testCreateSiteRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-site.custom.localhost';
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://appwrite.test');
|
||||
$proxyClient->addHeader('x-appwrite-hostname', $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/contact');
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupSiteRule($domain, $siteId);
|
||||
$this->assertNotEmpty($ruleId);
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertSame(200, $rule['headers']['status-code']);
|
||||
$this->assertSame('created', $rule['body']['status']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/contact');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('Contact page', $response['body']);
|
||||
|
||||
// Wildcard domains automatically get verified status
|
||||
$domains = [
|
||||
\uniqid() . '.sites.localhost',
|
||||
\uniqid() . '.rebranded.localhost',
|
||||
];
|
||||
foreach ($domains as $domain) {
|
||||
$wildcardRuleId = $this->setupSiteRule($domain, $siteId);
|
||||
$this->assertNotEmpty($wildcardRuleId);
|
||||
$rule = $this->getRule($wildcardRuleId);
|
||||
$this->assertSame(200, $rule['headers']['status-code']);
|
||||
$this->assertSame('verified', $rule['body']['status']);
|
||||
$this->cleanupRule($wildcardRuleId);
|
||||
}
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('trigger', ['deployment'])->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $rules['body']['total']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentResourceType', ['site'])->toString(),
|
||||
Query::equal('deploymentResourceId', [$siteId])->toString(),
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString(),
|
||||
Query::equal('type', ['deployment'])->toString(),
|
||||
Query::equal('deploymentId', [$deploymentId])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
});
|
||||
}
|
||||
|
||||
public function testCreateSiteBranchRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-site-branch.custom.localhost';
|
||||
|
||||
$setup = $this->setupSite();
|
||||
$siteId = $setup['siteId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupSiteRule($domain, $siteId, 'dev');
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionBranchRule(): void
|
||||
{
|
||||
$domain = \uniqid() . '-function-branch.custom.localhost';
|
||||
|
||||
$setup = $this->setupFunction();
|
||||
$functionId = $setup['functionId'];
|
||||
$deploymentId = $setup['deploymentId'];
|
||||
|
||||
$this->assertNotEmpty($functionId);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$ruleId = $this->setupFunctionRule($domain, $functionId, 'dev');
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testUpdateRule(): void
|
||||
{
|
||||
// Create function appwrite-network domain
|
||||
$functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0];
|
||||
$domain = \uniqid() . '-cname-api.' . $functionsDomain;
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// Create site appwrite-network domain
|
||||
$sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0];
|
||||
$domain = \uniqid() . '-cname-api.' . $sitesDomain;
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verified', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// Create + update
|
||||
$domain = \uniqid() . '-cname-api.custom.com';
|
||||
|
||||
$rule = $this->createAPIRule($domain);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testGetRule()
|
||||
{
|
||||
$domain = \uniqid() . '-get.custom.localhost';
|
||||
$ruleId = $this->setupAPIRule($domain);
|
||||
|
||||
$this->assertNotEmpty($ruleId);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals($domain, $rule['body']['domain']);
|
||||
$this->assertEquals('manual', $rule['body']['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rule['body']);
|
||||
$this->assertArrayHasKey('domain', $rule['body']);
|
||||
$this->assertArrayHasKey('type', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectUrl', $rule['body']);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rule['body']);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rule['body']);
|
||||
$this->assertArrayHasKey('logs', $rule['body']);
|
||||
$this->assertArrayHasKey('renewAt', $rule['body']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
public function testListRules()
|
||||
{
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$rule = $this->deleteRule($rule['$id']);
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
|
||||
$rule1Domain = \uniqid() . '-list1.custom.localhost';
|
||||
$rule1Id = $this->setupAPIRule($rule1Domain);
|
||||
$this->assertNotEmpty($rule1Id);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(1, $rules['body']['total']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule1Domain, $rules['body']['rules'][0]['domain']);
|
||||
|
||||
$this->assertEquals('manual', $rules['body']['rules'][0]['trigger']);
|
||||
$this->assertArrayHasKey('$id', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('domain', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('type', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('redirectUrl', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('redirectStatusCode', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentResourceType', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentId', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentResourceId', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('deploymentVcsProviderBranch', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('logs', $rules['body']['rules'][0]);
|
||||
$this->assertArrayHasKey('renewAt', $rules['body']['rules'][0]);
|
||||
|
||||
$rule2Domain = \uniqid() . '-list1.custom.localhost';
|
||||
$rule2Id = $this->setupAPIRule($rule2Domain);
|
||||
$this->assertNotEmpty($rule2Id);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
$this->assertCount(2, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(2, $rules['body']['total']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('$id', [$rule1Id])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule1Domain, $rules['body']['rules'][0]['domain']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::orderDesc('$id')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(2, $rules['body']['rules']);
|
||||
$this->assertEquals($rule2Id, $rules['body']['rules'][0]['$id']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'queries' => [
|
||||
Query::equal('domain', [$rule2Domain])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertCount(1, $rules['body']['rules']);
|
||||
$this->assertEquals($rule2Id, $rules['body']['rules'][0]['$id']);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule1Domain,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleIds = \array_column($rules['body']['rules'], '$id');
|
||||
$this->assertContains($rule1Id, $ruleIds);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule2Domain,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleIds = \array_column($rules['body']['rules'], '$id');
|
||||
$this->assertContains($rule2Id, $ruleIds);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule1Id,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleDomains = \array_column($rules['body']['rules'], 'domain');
|
||||
$this->assertContains($rule1Domain, $ruleDomains);
|
||||
|
||||
$rules = $this->listRules([
|
||||
'search' => $rule2Id,
|
||||
'queries' => [ Query::orderDesc('$createdAt')->toString() ]
|
||||
]);
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$ruleDomains = \array_column($rules['body']['rules'], 'domain');
|
||||
$this->assertContains($rule2Domain, $ruleDomains);
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
foreach ($rules['body']['rules'] as $rule) {
|
||||
$rule = $this->deleteRule($rule['$id']);
|
||||
$this->assertEquals(204, $rule['headers']['status-code']);
|
||||
}
|
||||
|
||||
$rules = $this->listRules();
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
$this->assertCount(0, $rules['body']['rules']);
|
||||
}
|
||||
|
||||
public function testRuleVerification(): void
|
||||
{
|
||||
|
||||
// 1. Site rule can verify
|
||||
$site = $this->setupSite();
|
||||
$siteId = $site['siteId'];
|
||||
|
||||
$rule = $this->createSiteRule('stage-site.webapp.com', $siteId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
$this->assertNotEmpty($rule['body']['$id']);
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals($ruleId, $rule['body']['$id']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
// 2. Function rule can verify
|
||||
$function = $this->setupFunction();
|
||||
$functionId = $function['functionId'];
|
||||
|
||||
$rule = $this->createFunctionRule('stage-function.webapp.com', $functionId);
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$rule = $this->createAPIRule('stage-site.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['logs']);
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
||||
// 3. Wrong A record fails to verify
|
||||
$rule = $this->createAPIRule('wrong-a-webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 4. Correct A record can verify
|
||||
$rule = $this->createAPIRule('webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// 5. Correct CNAME record can verify (no CAA record)
|
||||
$rule = $this->createAPIRule('stage.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
|
||||
// 6. Missing CNAME record fails to verify
|
||||
$rule = $this->createAPIRule('stage-missing-cname.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 7. Wrong CNAME record fails to verify
|
||||
$rule = $this->createAPIRule('stage-wrong-cname.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 8. Wrong CAA record fails to verify
|
||||
$rule = $this->createAPIRule('stage-wrong-caa.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['message']);
|
||||
|
||||
$rule = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
|
||||
// 9. Correct CAA record can verify
|
||||
$rule = $this->createAPIRule('stage-correct-caa.webapp.com');
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('verifying', $rule['body']['status']);
|
||||
$this->assertEmpty($rule['body']['logs']);
|
||||
|
||||
$this->cleanupRule($rule['body']['$id']);
|
||||
}
|
||||
|
||||
public function testUpdateRuleVerificationWithSameDataUpdatesTimestamp(): void
|
||||
{
|
||||
$domain = \uniqid() . '-timestamp-test.webapp.com';
|
||||
$rule = $this->createAPIRule($domain);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code']);
|
||||
$this->assertEquals('created', $rule['body']['status']);
|
||||
$this->assertNotEmpty($rule['body']['logs']);
|
||||
|
||||
$ruleId = $rule['body']['$id'];
|
||||
$initialUpdatedAt = $rule['body']['$updatedAt'];
|
||||
$initiallogs = $rule['body']['logs'];
|
||||
|
||||
sleep(1);
|
||||
|
||||
$updatedRule = $this->updateRuleVerification($ruleId);
|
||||
|
||||
$this->assertEquals(400, $updatedRule['headers']['status-code']);
|
||||
$this->assertStringContainsString($initiallogs, $updatedRule['body']['message']);
|
||||
|
||||
$ruleAfterUpdate = $this->getRule($ruleId);
|
||||
$this->assertEquals(200, $ruleAfterUpdate['headers']['status-code']);
|
||||
$this->assertEquals('created', $ruleAfterUpdate['body']['status']);
|
||||
$this->assertEquals($initiallogs, $ruleAfterUpdate['body']['logs']);
|
||||
$this->assertNotEquals($initialUpdatedAt, $ruleAfterUpdate['body']['$updatedAt']);
|
||||
|
||||
$initialTime = new \DateTime($initialUpdatedAt);
|
||||
$updatedTime = new \DateTime($ruleAfterUpdate['body']['$updatedAt']);
|
||||
$this->assertGreaterThan($initialTime, $updatedTime);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Proxy;
|
||||
|
||||
use Appwrite\ID;
|
||||
use Appwrite\Tests\Async;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Console;
|
||||
|
||||
trait ProxyHelpers
|
||||
{
|
||||
use Async;
|
||||
|
||||
protected function listRules(array $params = []): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createAPIRule(string $domain): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function updateRuleStatus(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_PATCH, '/proxy/rules/' . $ruleId . '/status', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createSiteRule(string $domain, string $siteId, string $branch = ''): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'siteId' => $siteId,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function getRule(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/redirect', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'url' => $url,
|
||||
'statusCode' => $statusCode,
|
||||
'resourceType' => $resourceType,
|
||||
'resourceId' => $resourceId,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function createFunctionRule(string $domain, string $functionId, string $branch = ''): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/function', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'domain' => $domain,
|
||||
'functionId' => $functionId,
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function deleteRule(string $ruleId): mixed
|
||||
{
|
||||
$rule = $this->client->call(Client::METHOD_DELETE, '/proxy/rules/' . $ruleId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
protected function setupAPIRule(string $domain): string
|
||||
{
|
||||
$rule = $this->createAPIRule($domain);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function setupRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): string
|
||||
{
|
||||
$rule = $this->createRedirectRule($domain, $url, $statusCode, $resourceType, $resourceId);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function setupFunctionRule(string $domain, string $functionId, string $branch = ''): string
|
||||
{
|
||||
$rule = $this->createFunctionRule($domain, $functionId, $branch);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function setupSiteRule(string $domain, string $siteId, string $branch = ''): string
|
||||
{
|
||||
$rule = $this->createSiteRule($domain, $siteId, $branch);
|
||||
|
||||
$this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule));
|
||||
|
||||
return $rule['body']['$id'];
|
||||
}
|
||||
|
||||
protected function cleanupRule(string $ruleId): void
|
||||
{
|
||||
$rule = $this->deleteRule($ruleId);
|
||||
$this->assertEquals(204, $rule['headers']['status-code'], 'Failed to cleanup rule: ' . \json_encode($rule));
|
||||
}
|
||||
|
||||
protected function cleanupSite(string $siteId): void
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(204, $site['headers']['status-code'], 'Failed to cleanup site: ' . \json_encode($site));
|
||||
}
|
||||
|
||||
protected function cleanupFunction(string $functionId): void
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(204, $function['headers']['status-code'], 'Failed to cleanup function: ' . \json_encode($function));
|
||||
}
|
||||
|
||||
protected function setupSite(): mixed
|
||||
{
|
||||
// Site
|
||||
$site = $this->client->call(Client::METHOD_POST, '/sites', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Proxy site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals($site['headers']['status-code'], 201, 'Setup site failed with status code: ' . $site['headers']['status-code'] . ' and response: ' . json_encode($site['body'], JSON_PRETTY_PRINT));
|
||||
|
||||
$siteId = $site['body']['$id'];
|
||||
|
||||
// Deployment
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'code' => $this->packageSite('static'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$site = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals($deploymentId, $site['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($site['body'], JSON_PRETTY_PRINT));
|
||||
}, 120000, 500);
|
||||
|
||||
return ['siteId' => $siteId, 'deploymentId' => $deploymentId];
|
||||
}
|
||||
|
||||
protected function setupFunction(): mixed
|
||||
{
|
||||
// Function
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-22',
|
||||
'name' => 'Proxy Function',
|
||||
'entrypoint' => 'index.js',
|
||||
'commands' => '',
|
||||
'execute' => ['any']
|
||||
]);
|
||||
|
||||
$this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'], JSON_PRETTY_PRINT));
|
||||
|
||||
$functionId = $function['body']['$id'];
|
||||
|
||||
// Deployment
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'code' => $this->packageFunction('basic'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals($deploymentId, $function['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($function['body'], JSON_PRETTY_PRINT));
|
||||
}, 100000, 500);
|
||||
|
||||
return ['functionId' => $functionId, 'deploymentId' => $deploymentId];
|
||||
}
|
||||
|
||||
private function packageSite(string $site): CURLFile
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
$folderPath = realpath(__DIR__ . '/../../../resources/sites') . "/$site";
|
||||
$tarPath = "$folderPath/code.tar.gz";
|
||||
|
||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz --exclude node_modules -czf code.tar.gz .", '', $stdout, $stderr);
|
||||
|
||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||
}
|
||||
|
||||
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
|
||||
}
|
||||
|
||||
private function packageFunction(string $function): CURLFile
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
|
||||
$tarPath = "$folderPath/code.tar.gz";
|
||||
|
||||
Console::execute("cd $folderPath && tar --exclude code.tar.gz --exclude node_modules -czf code.tar.gz .", '', $stdout, $stderr);
|
||||
|
||||
if (filesize($tarPath) > 1024 * 1024 * 5) {
|
||||
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
|
||||
}
|
||||
|
||||
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\TablesDB;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ApiTablesDB;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SchemaPolling;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Tests\E2E\Traits\DatabasesUrlHelpers;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
||||
class DatabasesNumericTypesTest extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
use ApiTablesDB;
|
||||
use DatabasesUrlHelpers;
|
||||
use SchemaPolling;
|
||||
|
||||
private static array $setupCache = [];
|
||||
|
||||
/**
|
||||
* Setup database, table, and numeric columns for parallel-safe tests.
|
||||
*/
|
||||
protected function setupDatabaseAndTable(): array
|
||||
{
|
||||
$cacheKey = $this->getProject()['$id'] ?? 'default';
|
||||
if (!empty(self::$setupCache[$cacheKey])) {
|
||||
return self::$setupCache[$cacheKey];
|
||||
}
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$apiKey = $this->getProject()['apiKey'];
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apiKey,
|
||||
];
|
||||
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', $headers, [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Numeric Types Test Database',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', $headers, [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Numeric Types Table',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create integer column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
|
||||
'key' => 'integer_field',
|
||||
'required' => false,
|
||||
'min' => -10,
|
||||
'max' => 10,
|
||||
'default' => 0,
|
||||
]);
|
||||
|
||||
// Create bigint column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
|
||||
'key' => 'bigint_field',
|
||||
'required' => false,
|
||||
'min' => -9007199254740991,
|
||||
'max' => 9007199254740991,
|
||||
'default' => 9007199254740000,
|
||||
]);
|
||||
|
||||
// Create unsigned integer column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
|
||||
'key' => 'unsigned_int_field',
|
||||
'required' => false,
|
||||
'min' => 0,
|
||||
'max' => 100,
|
||||
'default' => 0,
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
// Create unsigned bigint column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
|
||||
'key' => 'unsigned_bigint_field',
|
||||
'required' => false,
|
||||
'min' => 0,
|
||||
'max' => 9223372036854775807,
|
||||
'default' => 0,
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
// Cache before waiting so that if waitForAllAttributes times out,
|
||||
// subsequent calls don't try to re-create the same columns (causing 409)
|
||||
self::$setupCache[$cacheKey] = [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
];
|
||||
|
||||
// Wait for all columns to be available
|
||||
$this->waitForAllAttributes($databaseId, $tableId);
|
||||
|
||||
return self::$setupCache[$cacheKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup database/table without caching so mutations (update/delete) don't
|
||||
* affect other tests that might be executed in a different order.
|
||||
*/
|
||||
protected function setupFreshDatabaseAndTable(): array
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$apiKey = $this->getProject()['apiKey'];
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apiKey,
|
||||
];
|
||||
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', $headers, [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Numeric Types Test Database',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', $headers, [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Numeric Types Table',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
|
||||
'key' => 'integer_field',
|
||||
'required' => false,
|
||||
'min' => -10,
|
||||
'max' => 10,
|
||||
'default' => 0,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
|
||||
'key' => 'bigint_field',
|
||||
'required' => false,
|
||||
'min' => -9007199254740991,
|
||||
'max' => 9007199254740991,
|
||||
'default' => 9007199254740000,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
|
||||
'key' => 'unsigned_int_field',
|
||||
'required' => false,
|
||||
'max' => 100,
|
||||
'default' => 0,
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
|
||||
'key' => 'unsigned_bigint_field',
|
||||
'required' => false,
|
||||
'max' => 9223372036854775807,
|
||||
'default' => 0,
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
$this->waitForAllAttributes($databaseId, $tableId);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
];
|
||||
}
|
||||
|
||||
public function testCreateDatabase(): void
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Numeric Types Test Database',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateTable(): void
|
||||
{
|
||||
$data = $this->setupDatabaseAndTable();
|
||||
|
||||
$table = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['tableId'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $table['headers']['status-code']);
|
||||
$this->assertEquals($data['tableId'], $table['body']['$id']);
|
||||
}
|
||||
|
||||
public function testGetIntegerAndBigIntColumns(): void
|
||||
{
|
||||
$data = $this->setupDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
$integerColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $integerColumn['headers']['status-code']);
|
||||
$this->assertEquals('integer_field', $integerColumn['body']['key']);
|
||||
$this->assertEquals('integer', $integerColumn['body']['type']);
|
||||
$this->assertEquals(false, $integerColumn['body']['required']);
|
||||
$this->assertEquals(false, $integerColumn['body']['array']);
|
||||
$this->assertEquals(-10, $integerColumn['body']['min']);
|
||||
$this->assertEquals(10, $integerColumn['body']['max']);
|
||||
$this->assertEquals(0, $integerColumn['body']['default']);
|
||||
|
||||
$bigintColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $bigintColumn['headers']['status-code']);
|
||||
$this->assertEquals('bigint_field', $bigintColumn['body']['key']);
|
||||
|
||||
$this->assertEquals('bigint', $bigintColumn['body']['type']);
|
||||
$this->assertEquals(false, $bigintColumn['body']['required']);
|
||||
$this->assertEquals(false, $bigintColumn['body']['array']);
|
||||
$this->assertEquals(-9007199254740991, $bigintColumn['body']['min']);
|
||||
$this->assertEquals(9007199254740991, $bigintColumn['body']['max']);
|
||||
$this->assertEquals(9007199254740000, $bigintColumn['body']['default']);
|
||||
}
|
||||
|
||||
public function testGetUnsignedIntegerAndBigIntColumns(): void
|
||||
{
|
||||
$data = $this->setupDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
$unsignedIntColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/unsigned_int_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $unsignedIntColumn['headers']['status-code']);
|
||||
$this->assertEquals('unsigned_int_field', $unsignedIntColumn['body']['key']);
|
||||
$this->assertEquals('integer', $unsignedIntColumn['body']['type']);
|
||||
$this->assertEquals(false, $unsignedIntColumn['body']['required']);
|
||||
$this->assertEquals(false, $unsignedIntColumn['body']['array']);
|
||||
$this->assertEquals(false, $unsignedIntColumn['body']['signed']);
|
||||
$this->assertEquals(0, $unsignedIntColumn['body']['min']);
|
||||
$this->assertEquals(100, $unsignedIntColumn['body']['max']);
|
||||
$this->assertEquals(0, $unsignedIntColumn['body']['default']);
|
||||
|
||||
$unsignedBigintColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/unsigned_bigint_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $unsignedBigintColumn['headers']['status-code']);
|
||||
$this->assertEquals('unsigned_bigint_field', $unsignedBigintColumn['body']['key']);
|
||||
$this->assertEquals('bigint', $unsignedBigintColumn['body']['type']);
|
||||
$this->assertEquals(false, $unsignedBigintColumn['body']['required']);
|
||||
$this->assertEquals(false, $unsignedBigintColumn['body']['array']);
|
||||
$this->assertEquals(false, $unsignedBigintColumn['body']['signed']);
|
||||
$this->assertEquals(0, $unsignedBigintColumn['body']['min']);
|
||||
$this->assertEquals(9223372036854775807, $unsignedBigintColumn['body']['max']);
|
||||
$this->assertEquals(0, $unsignedBigintColumn['body']['default']);
|
||||
}
|
||||
|
||||
public function testListColumnsWithNumericTypes(): void
|
||||
{
|
||||
$data = $this->setupDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $columns['headers']['status-code']);
|
||||
$this->assertIsArray($columns['body']['columns']);
|
||||
$this->assertGreaterThan(0, $columns['body']['total']);
|
||||
|
||||
$columnKeys = array_map(fn ($col) => $col['key'], $columns['body']['columns']);
|
||||
$this->assertContains('integer_field', $columnKeys);
|
||||
$this->assertContains('bigint_field', $columnKeys);
|
||||
$this->assertContains('unsigned_int_field', $columnKeys);
|
||||
$this->assertContains('unsigned_bigint_field', $columnKeys);
|
||||
|
||||
$columnTypeByKey = [];
|
||||
foreach ($columns['body']['columns'] as $col) {
|
||||
$columnTypeByKey[$col['key']] = $col['type'];
|
||||
}
|
||||
|
||||
$this->assertEquals('integer', $columnTypeByKey['integer_field']);
|
||||
$this->assertEquals('bigint', $columnTypeByKey['bigint_field']);
|
||||
$this->assertEquals('integer', $columnTypeByKey['unsigned_int_field']);
|
||||
$this->assertEquals('bigint', $columnTypeByKey['unsigned_bigint_field']);
|
||||
}
|
||||
|
||||
public function testCreateRowWithIntegerAndBigIntTypes(): void
|
||||
{
|
||||
$data = $this->setupDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'rowId' => ID::unique(),
|
||||
'data' => [
|
||||
'integer_field' => 5,
|
||||
'bigint_field' => 456,
|
||||
'unsigned_int_field' => 50,
|
||||
'unsigned_bigint_field' => 9007199254740000,
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row['headers']['status-code']);
|
||||
$this->assertEquals(5, $row['body']['integer_field']);
|
||||
$this->assertEquals(456, $row['body']['bigint_field']);
|
||||
$this->assertEquals(50, $row['body']['unsigned_int_field']);
|
||||
$this->assertEquals(9007199254740000, $row['body']['unsigned_bigint_field']);
|
||||
}
|
||||
|
||||
public function testUpdateIntegerAndBigIntColumns(): void
|
||||
{
|
||||
$data = $this->setupFreshDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
// Update integer column
|
||||
$updateInteger = $this->client->call(
|
||||
Client::METHOD_PATCH,
|
||||
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer/integer_field',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'required' => false,
|
||||
'min' => -20,
|
||||
'max' => 20,
|
||||
'default' => 3,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $updateInteger['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($databaseId, $tableId) {
|
||||
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $column['headers']['status-code']);
|
||||
$this->assertEquals(-20, $column['body']['min']);
|
||||
$this->assertEquals(20, $column['body']['max']);
|
||||
$this->assertEquals(3, $column['body']['default']);
|
||||
}, 30000, 250);
|
||||
|
||||
// Update bigint column
|
||||
$updateBigint = $this->client->call(
|
||||
Client::METHOD_PATCH,
|
||||
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint/bigint_field',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'required' => false,
|
||||
'min' => -999,
|
||||
'max' => 999,
|
||||
'default' => 10,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $updateBigint['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($databaseId, $tableId) {
|
||||
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $column['headers']['status-code']);
|
||||
$this->assertEquals(-999, $column['body']['min']);
|
||||
$this->assertEquals(999, $column['body']['max']);
|
||||
$this->assertEquals(10, $column['body']['default']);
|
||||
}, 30000, 250);
|
||||
}
|
||||
|
||||
public function testDeleteIntegerAndBigIntColumns(): void
|
||||
{
|
||||
$data = $this->setupFreshDatabaseAndTable();
|
||||
$databaseId = $data['databaseId'];
|
||||
$tableId = $data['tableId'];
|
||||
|
||||
// Delete integer column
|
||||
$deleteInteger = $this->client->call(
|
||||
Client::METHOD_DELETE,
|
||||
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(204, $deleteInteger['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($databaseId, $tableId) {
|
||||
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $column['headers']['status-code']);
|
||||
}, 30000, 250);
|
||||
|
||||
// Delete bigint column
|
||||
$deleteBigint = $this->client->call(
|
||||
Client::METHOD_DELETE,
|
||||
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(204, $deleteBigint['headers']['status-code']);
|
||||
|
||||
$this->assertEventually(function () use ($databaseId, $tableId) {
|
||||
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $column['headers']['status-code']);
|
||||
}, 30000, 250);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user