mirror of
https://github.com/appwrite/appwrite
synced 2026-05-20 23:48:23 +00:00
Merge branch '1.8.x' of https://github.com/appwrite/appwrite into check-audits
This commit is contained in:
commit
a7c4e16545
35 changed files with 325 additions and 108 deletions
28
.github/workflows/tests.yml
vendored
28
.github/workflows/tests.yml
vendored
|
|
@ -8,7 +8,15 @@ env:
|
|||
IMAGE: appwrite-dev
|
||||
CACHE_KEY: appwrite-dev-${{ github.event.pull_request.head.sha }}
|
||||
|
||||
on: [ pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
response_format:
|
||||
description: 'Response format version to test (e.g., 1.5.0, 1.4.0)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
check_database_changes:
|
||||
|
|
@ -100,7 +108,10 @@ jobs:
|
|||
run: docker compose exec -T appwrite vars
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: docker compose exec appwrite test /usr/src/code/tests/unit
|
||||
run: |
|
||||
docker compose exec \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/unit
|
||||
|
||||
e2e_general_test:
|
||||
name: E2E General Test
|
||||
|
|
@ -132,7 +143,10 @@ jobs:
|
|||
done
|
||||
|
||||
- name: Run General Tests
|
||||
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug
|
||||
run: |
|
||||
docker compose exec -T \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/General --debug
|
||||
|
||||
- name: Failure Logs
|
||||
if: failure()
|
||||
|
|
@ -208,6 +222,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -296,6 +311,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -337,6 +353,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -392,6 +409,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -434,6 +452,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -490,6 +509,7 @@ jobs:
|
|||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
- name: Failure Logs
|
||||
|
|
@ -498,4 +518,4 @@ jobs:
|
|||
echo "=== Appwrite Worker Builds Logs ==="
|
||||
docker compose logs appwrite-worker-builds
|
||||
echo "=== OpenRuntimes Executor Logs ==="
|
||||
docker compose logs openruntimes-executor
|
||||
docker compose logs openruntimes-executor
|
||||
|
|
@ -435,6 +435,11 @@ return [
|
|||
'description' => 'The requested favicon could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::AVATAR_SVG_SANITIZATION_FAILED => [
|
||||
'name' => Exception::AVATAR_SVG_SANITIZATION_FAILED,
|
||||
'description' => 'SVG sanitization failed.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Storage */
|
||||
Exception::STORAGE_FILE_ALREADY_EXISTS => [
|
||||
|
|
|
|||
|
|
@ -9948,17 +9948,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -47120,17 +47120,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -35978,17 +35978,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -9948,17 +9948,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -47120,17 +47120,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -35978,17 +35978,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -9944,17 +9944,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -47166,17 +47166,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -36115,17 +36115,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -9944,17 +9944,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -47166,17 +47166,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -36115,17 +36115,20 @@
|
|||
"type": "integer",
|
||||
"description": "Row automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"readOnly": true
|
||||
},
|
||||
"$tableId": {
|
||||
"type": "string",
|
||||
"description": "Table ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$databaseId": {
|
||||
"type": "string",
|
||||
"description": "Database ID.",
|
||||
"x-example": "5e5ea5c15117e"
|
||||
"x-example": "5e5ea5c15117e",
|
||||
"readOnly": true
|
||||
},
|
||||
"$createdAt": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ App::get('/v1/avatars/favicon')
|
|||
$sanitizer->minify(true);
|
||||
$cleanSvg = $sanitizer->sanitize($data);
|
||||
if ($cleanSvg === false) {
|
||||
throw new \Exception('SVG sanitization failed');
|
||||
throw new Exception(Exception::AVATAR_SVG_SANITIZATION_FAILED);
|
||||
}
|
||||
$response
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
|
|
|
|||
|
|
@ -3011,7 +3011,7 @@ App::post('/v1/messaging/messages/email')
|
|||
->inject('project')
|
||||
->inject('queueForMessaging')
|
||||
->inject('response')
|
||||
->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, array $attachments, bool $draft, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
->action(function (string $messageId, string $subject, string $content, ?array $topics, ?array $users, ?array $targets, ?array $cc, ?array $bcc, ?array $attachments, bool $draft, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
$messageId = $messageId == 'unique()'
|
||||
? ID::unique()
|
||||
: $messageId;
|
||||
|
|
@ -3184,7 +3184,7 @@ App::post('/v1/messaging/messages/sms')
|
|||
->inject('project')
|
||||
->inject('queueForMessaging')
|
||||
->inject('response')
|
||||
->action(function (string $messageId, string $content, array $topics, array $users, array $targets, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
->action(function (string $messageId, string $content, ?array $topics, ?array $users, ?array $targets, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
$messageId = $messageId == 'unique()'
|
||||
? ID::unique()
|
||||
: $messageId;
|
||||
|
|
@ -3319,7 +3319,7 @@ App::post('/v1/messaging/messages/push')
|
|||
->inject('project')
|
||||
->inject('queueForMessaging')
|
||||
->inject('response')
|
||||
->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $image, string $icon, string $sound, string $color, string $tag, int $badge, bool $draft, ?string $scheduledAt, bool $contentAvailable, bool $critical, string $priority, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
->action(function (string $messageId, string $title, string $body, ?array $topics, ?array $users, ?array $targets, ?array $data, string $action, string $image, string $icon, string $sound, string $color, string $tag, int $badge, bool $draft, ?string $scheduledAt, bool $contentAvailable, bool $critical, string $priority, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) {
|
||||
$messageId = $messageId == 'unique()'
|
||||
? ID::unique()
|
||||
: $messageId;
|
||||
|
|
|
|||
|
|
@ -1060,26 +1060,6 @@ App::init()
|
|||
$response->addHeader('Access-Control-Allow-Origin', '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecation Warning
|
||||
*/
|
||||
/** @var \Appwrite\SDK\Method $sdk */
|
||||
$sdk = $route->getLabel('sdk', false);
|
||||
$deprecationWarning = 'This route is deprecated. See the updated documentation for improved compatibility and migration details.';
|
||||
$sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []);
|
||||
if (!empty($sdkItems) && count($sdkItems) > 0) {
|
||||
$allDeprecated = true;
|
||||
foreach ($sdkItems as $sdkItem) {
|
||||
if (!$sdkItem->isDeprecated()) {
|
||||
$allDeprecated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allDeprecated) {
|
||||
$warnings[] = $deprecationWarning;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($warnings)) {
|
||||
$response->addHeader('X-Appwrite-Warning', implode(';', $warnings));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ use Utopia\Database\DateTime;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
|
|
@ -416,6 +415,7 @@ App::init()
|
|||
->inject('user')
|
||||
->inject('publisher')
|
||||
->inject('publisherFunctions')
|
||||
->inject('publisherWebhooks')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForMessaging')
|
||||
->inject('queueForAudits')
|
||||
|
|
@ -431,7 +431,7 @@ App::init()
|
|||
->inject('plan')
|
||||
->inject('devKey')
|
||||
->inject('telemetry')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, BrokerPool $publisherFunctions, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
|
|
@ -544,7 +544,7 @@ App::init()
|
|||
// from overwriting the events that are supposed to be triggered in the shutdown hook.
|
||||
$queueForEventsClone = new Event($publisher);
|
||||
$queueForFunctions = new Func($publisherFunctions);
|
||||
$queueForWebhooks = new Webhook($publisher);
|
||||
$queueForWebhooks = new Webhook($publisherWebhooks);
|
||||
$queueForRealtime = new Realtime();
|
||||
|
||||
$dbForProject
|
||||
|
|
|
|||
|
|
@ -84,25 +84,28 @@ App::setResource('localeCodes', function () {
|
|||
App::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
App::setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherDatabases', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherFunctions', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherFunctions', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherMigrations', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherStatsUsage', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMails', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherMails', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherDeletes', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherDeletes', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMessaging', function (BrokerPool $publisher) {
|
||||
App::setResource('publisherMessaging', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherWebhooks', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ class Exception extends \Exception
|
|||
public const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found';
|
||||
public const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed';
|
||||
public const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found';
|
||||
public const AVATAR_SVG_SANITIZATION_FAILED = 'avatar_svg_sanitization_failed';
|
||||
|
||||
/** Storage */
|
||||
public const STORAGE_FILE_ALREADY_EXISTS = 'storage_file_already_exists';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Appwrite\Platform;
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Coroutine as Co;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
|
|
@ -157,4 +159,45 @@ class Action extends UtopiaAction
|
|||
Console::info("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to apply (request) select queries to response model.
|
||||
*
|
||||
* This prevents default values of rules to be presnet for not-selected attributes
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Document $document
|
||||
* @return void
|
||||
*/
|
||||
public function applySelectQueries(Request $request, Response $response, string $model): void
|
||||
{
|
||||
$queries = $request->getParam('queries', []);
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
$selectQueries = Query::groupByType($queries)['selections'] ?? [];
|
||||
|
||||
// No select queries means no filtering out
|
||||
if (empty($selectQueries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
foreach ($selectQueries as $query) {
|
||||
foreach ($query->getValues() as $attribute) {
|
||||
$attributes[] = $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$responseModel = $response->getModel($model);
|
||||
foreach ($responseModel->getRules() as $ruleName => $rule) {
|
||||
if (\str_starts_with($ruleName, '$')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\in_array($ruleName, $attributes)) {
|
||||
$responseModel->removeRule($ruleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace Appwrite\Platform\Modules\Compute;
|
|||
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
|
|
@ -11,7 +12,6 @@ use Utopia\Database\Helpers\ID;
|
|||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\System\System;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
|
|
|||
|
|
@ -231,8 +231,6 @@ class Update extends Action
|
|||
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
|
||||
->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations);
|
||||
|
||||
\var_dump($newDocument);
|
||||
|
||||
try {
|
||||
$document = $dbForProject->withRequestTimestamp(
|
||||
$requestTimestamp,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
namespace Appwrite\Platform\Modules\Functions\Http\Deployments;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -19,7 +21,7 @@ use Utopia\Platform\Action;
|
|||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class XList extends Action
|
||||
class XList extends Base
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
|
|
@ -55,6 +57,7 @@ class XList extends Action
|
|||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). 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(', ', Deployments::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
|
|
@ -64,6 +67,7 @@ class XList extends Action
|
|||
string $functionId,
|
||||
array $queries,
|
||||
string $search,
|
||||
Request $request,
|
||||
Response $response,
|
||||
Database $dbForProject
|
||||
) {
|
||||
|
|
@ -121,6 +125,7 @@ class XList extends Action
|
|||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$this->applySelectQueries($request, $response, Response::MODEL_DEPLOYMENT);
|
||||
$response->dynamic(new Document([
|
||||
'deployments' => $results,
|
||||
'total' => $total,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
namespace Appwrite\Platform\Modules\Sites\Http\Deployments;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -19,7 +21,7 @@ use Utopia\Platform\Action;
|
|||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class XList extends Action
|
||||
class XList extends Base
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
|
|
@ -55,13 +57,20 @@ class XList extends Action
|
|||
->param('siteId', '', new UID(), 'Site ID.')
|
||||
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). 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(', ', Deployments::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $siteId, array $queries, string $search, Response $response, Database $dbForProject)
|
||||
{
|
||||
public function action(
|
||||
string $siteId,
|
||||
array $queries,
|
||||
string $search,
|
||||
Request $request,
|
||||
Response $response,
|
||||
Database $dbForProject
|
||||
) {
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
if ($site->isEmpty()) {
|
||||
|
|
@ -116,6 +125,7 @@ class XList extends Action
|
|||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$this->applySelectQueries($request, $response, Response::MODEL_DEPLOYMENT);
|
||||
$response->dynamic(new Document([
|
||||
'deployments' => $results,
|
||||
'total' => $total,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Utopia\Database\Validator\Query\Filter;
|
|||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Query\Order;
|
||||
use Utopia\Database\Validator\Query\Select;
|
||||
|
||||
class Base extends Queries
|
||||
{
|
||||
|
|
@ -40,41 +41,51 @@ class Base extends Queries
|
|||
$allowedAttributesLookup[$attribute] = true;
|
||||
}
|
||||
|
||||
$allAttributes = [];
|
||||
$attributes = [];
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$key = $attribute['$id'];
|
||||
|
||||
if (!isset($allowedAttributesLookup[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[] = new Document([
|
||||
$attributeDocument = new Document([
|
||||
'key' => $key,
|
||||
'type' => $attribute['type'],
|
||||
'array' => $attribute['array'],
|
||||
]);
|
||||
|
||||
$allAttributes[] = $attributeDocument;
|
||||
|
||||
if (isset($allowedAttributesLookup[$key])) {
|
||||
$attributes[] = $attributeDocument;
|
||||
}
|
||||
}
|
||||
|
||||
$attributes[] = new Document([
|
||||
'key' => '$id',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]);
|
||||
$attributes[] = new Document([
|
||||
'key' => '$createdAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]);
|
||||
$attributes[] = new Document([
|
||||
'key' => '$updatedAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]);
|
||||
$attributes[] = new Document([
|
||||
'key' => '$sequence',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'array' => false,
|
||||
]);
|
||||
$internalAttributes = [
|
||||
new Document([
|
||||
'key' => '$id',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]),
|
||||
new Document([
|
||||
'key' => '$createdAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]),
|
||||
new Document([
|
||||
'key' => '$updatedAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]),
|
||||
new Document([
|
||||
'key' => '$sequence',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'array' => false,
|
||||
])
|
||||
];
|
||||
|
||||
foreach ($internalAttributes as $attribute) {
|
||||
$attributes[] = $attribute;
|
||||
$allAttributes[] = $attribute;
|
||||
}
|
||||
|
||||
$validators = [
|
||||
new Limit(),
|
||||
|
|
@ -84,6 +95,15 @@ class Base extends Queries
|
|||
new Order($attributes),
|
||||
];
|
||||
|
||||
if ($this->isSelectQueryAllowed()) {
|
||||
$validators[] = new Select($allAttributes);
|
||||
}
|
||||
|
||||
parent::__construct($validators);
|
||||
}
|
||||
|
||||
public function isSelectQueryAllowed(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,4 +22,9 @@ class Deployments extends Base
|
|||
{
|
||||
parent::__construct('deployments', self::ALLOWED_ATTRIBUTES);
|
||||
}
|
||||
|
||||
public function isSelectQueryAllowed(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ class Response extends SwooleResponse
|
|||
/**
|
||||
* Response constructor.
|
||||
*
|
||||
* @param float $time
|
||||
* @param SwooleHTTPResponse $response Native response to be passed to parent constructor
|
||||
*/
|
||||
public function __construct(SwooleHTTPResponse $response)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ abstract class Model
|
|||
|
||||
/**
|
||||
* Filter Document Structure
|
||||
* @param Document $document Document to apply filter on
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
|
|
@ -105,7 +106,7 @@ abstract class Model
|
|||
* @param string $key
|
||||
* @return Model
|
||||
*/
|
||||
protected function removeRule(string $key): self
|
||||
public function removeRule(string $key): self
|
||||
{
|
||||
if (isset($this->rules[$key])) {
|
||||
unset($this->rules[$key]);
|
||||
|
|
|
|||
|
|
@ -41,18 +41,21 @@ class Row extends Any
|
|||
'description' => 'Row automatically incrementing ID.',
|
||||
'default' => 0,
|
||||
'example' => 1,
|
||||
'readOnly' => true,
|
||||
])
|
||||
->addRule('$tableId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Table ID.',
|
||||
'default' => '',
|
||||
'example' => '5e5ea5c15117e',
|
||||
'readOnly' => true,
|
||||
])
|
||||
->addRule('$databaseId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Database ID.',
|
||||
'default' => '',
|
||||
'example' => '5e5ea5c15117e',
|
||||
'readOnly' => true,
|
||||
])
|
||||
->addRule('$createdAt', [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
|
|
|
|||
|
|
@ -108,6 +108,20 @@ class Client
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Response Format
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return self $this
|
||||
*/
|
||||
public function setResponseFormat(string $value): self
|
||||
{
|
||||
$this->addHeader('X-Appwrite-Response-Format', $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $status true
|
||||
* @return self $this
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Appwrite\Tests\Retryable;
|
|||
use PHPUnit\Framework\TestCase;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
abstract class Scope extends TestCase
|
||||
{
|
||||
|
|
@ -23,6 +24,17 @@ abstract class Scope extends TestCase
|
|||
{
|
||||
$this->client = new Client();
|
||||
$this->client->setEndpoint($this->endpoint);
|
||||
|
||||
$format = System::getEnv('_APP_E2E_RESPONSE_FORMAT');
|
||||
if (!empty($format)) {
|
||||
if (
|
||||
!\preg_match('/^\d+\.\d+\.\d+$/', $format) ||
|
||||
!\version_compare($format, APP_VERSION_STABLE, '<=')
|
||||
) {
|
||||
throw new \Exception('E2E response format must be ' . APP_VERSION_STABLE . ' or lower.');
|
||||
}
|
||||
$this->client->setResponseFormat($format);
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
|
|
|
|||
|
|
@ -721,6 +721,30 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $deployments['body']['deployments']);
|
||||
|
||||
$deployments = $this->listDeployments($functionId, [
|
||||
'queries' => [
|
||||
Query::select(['status'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertArrayHasKey('status', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayHasKey('status', $deployments['body']['deployments'][1]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]);
|
||||
|
||||
// Extra select query check, for attribute not allowed by filter queries
|
||||
$deployments = $this->listDeployments($functionId, [
|
||||
'queries' => [
|
||||
Query::select(['buildLogs'])->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][1]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]);
|
||||
|
||||
$deployments = $this->listDeployments($functionId, [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString(),
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,30 @@ class SitesCustomServerTest extends Scope
|
|||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $deployments['body']['deployments']);
|
||||
|
||||
$deployments = $this->listDeployments($siteId, [
|
||||
'queries' => [
|
||||
Query::select(['status'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertArrayHasKey('status', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayHasKey('status', $deployments['body']['deployments'][1]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]);
|
||||
|
||||
// Extra select query check, for attribute not allowed by filter queries
|
||||
$deployments = $this->listDeployments($siteId, [
|
||||
'queries' => [
|
||||
Query::select(['buildLogs'])->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
$this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][1]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]);
|
||||
$this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]);
|
||||
|
||||
$deployments = $this->listDeployments($siteId, [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString(),
|
||||
|
|
|
|||
|
|
@ -445,6 +445,19 @@ trait UsersBase
|
|||
|
||||
$user1 = $response['body']['users'][1];
|
||||
|
||||
// This test ensures that by default, endpoints dont support select queries
|
||||
// If we add select query to this endpoint, you will need to remove this test
|
||||
// Please make sure to add it to another place, unless all endpoints support select queries
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['name'])->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue