mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.9.x' into feat-public-organization-api
This commit is contained in:
+49
-49
@@ -8,55 +8,6 @@ return [
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'sdks' => [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '22.4.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_SDK_PLATFORM_CLIENT,
|
||||
'prism' => 'javascript',
|
||||
'source' => \realpath(__DIR__ . '/../sdks/client-web'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git',
|
||||
'gitRepoName' => 'sdk-for-web',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
|
||||
'demos' => [
|
||||
[
|
||||
'icon' => 'react.svg',
|
||||
'name' => 'Todo App with React JS',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-react',
|
||||
'url' => 'https://appwrite-todo-with-react.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'vue.svg',
|
||||
'name' => 'Todo App with Vue JS',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-vue',
|
||||
'url' => 'https://appwrite-todo-with-vue.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'angular.svg',
|
||||
'name' => 'Todo App with Angular',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-angular',
|
||||
'url' => 'https://appwrite-todo-with-angular.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'svelte.svg',
|
||||
'name' => 'Todo App with Svelte',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-svelte',
|
||||
'url' => 'https://appwrite-todo-with-svelte.vercel.app/',
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
@@ -350,6 +301,55 @@ return [
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'sdks' => [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '22.4.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_SDK_PLATFORM_SERVER,
|
||||
'prism' => 'javascript',
|
||||
'source' => \realpath(__DIR__ . '/../sdks/client-web'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git',
|
||||
'gitRepoName' => 'sdk-for-web',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
|
||||
'demos' => [
|
||||
[
|
||||
'icon' => 'react.svg',
|
||||
'name' => 'Todo App with React JS',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-react',
|
||||
'url' => 'https://appwrite-todo-with-react.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'vue.svg',
|
||||
'name' => 'Todo App with Vue JS',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-vue',
|
||||
'url' => 'https://appwrite-todo-with-vue.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'angular.svg',
|
||||
'name' => 'Todo App with Angular',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-angular',
|
||||
'url' => 'https://appwrite-todo-with-angular.vercel.app/',
|
||||
],
|
||||
[
|
||||
'icon' => 'svelte.svg',
|
||||
'name' => 'Todo App with Svelte',
|
||||
'description' => 'A simple Todo app that uses both the Appwrite account and database APIs.',
|
||||
'source' => 'https://github.com/appwrite/todo-with-svelte',
|
||||
'url' => 'https://appwrite-todo-with-svelte.vercel.app/',
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
|
||||
@@ -1239,7 +1239,6 @@ Http::get('/v1/account/sessions/oauth2/:provider')
|
||||
)
|
||||
],
|
||||
contentType: ContentType::HTML,
|
||||
hide: [APP_SDK_PLATFORM_SERVER],
|
||||
))
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
@@ -4515,7 +4514,7 @@ Http::post('/v1/account/targets/push')
|
||||
group: 'pushTargets',
|
||||
name: 'createPushTarget',
|
||||
description: '/docs/references/account/create-push-target.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION],
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
@@ -4599,7 +4598,7 @@ Http::put('/v1/account/targets/:targetId/push')
|
||||
group: 'pushTargets',
|
||||
name: 'updatePushTarget',
|
||||
description: '/docs/references/account/update-push-target.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION],
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -4669,7 +4668,7 @@ Http::delete('/v1/account/targets/:targetId/push')
|
||||
group: 'pushTargets',
|
||||
name: 'deletePushTarget',
|
||||
description: '/docs/references/account/delete-push-target.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION],
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@
|
||||
"utopia-php/emails": "0.7.*",
|
||||
"utopia-php/dns": "1.7.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/http": "^2.0@RC",
|
||||
"utopia-php/http": "2.0.0-rc1",
|
||||
"utopia-php/fetch": "^1.1",
|
||||
"utopia-php/validators": "0.2.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
|
||||
Generated
+91
-160
@@ -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": "b645c7da1728536497fe8186b158f9c6",
|
||||
"content-hash": "597066d71be48add0c649828d820a505",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -3615,16 +3615,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cache.git",
|
||||
"reference": "ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176"
|
||||
"reference": "086687d7ae23dd1dae67b943161e8cef143539e1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cache/zipball/ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176",
|
||||
"reference": "ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176",
|
||||
"url": "https://api.github.com/repos/utopia-php/cache/zipball/086687d7ae23dd1dae67b943161e8cef143539e1",
|
||||
"reference": "086687d7ae23dd1dae67b943161e8cef143539e1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3663,9 +3663,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cache/issues",
|
||||
"source": "https://github.com/utopia-php/cache/tree/3.0.0"
|
||||
"source": "https://github.com/utopia-php/cache/tree/3.0.2"
|
||||
},
|
||||
"time": "2026-05-14T14:13:17+00:00"
|
||||
"time": "2026-05-19T22:38:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/circuit-breaker",
|
||||
@@ -4079,16 +4079,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dns",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/dns.git",
|
||||
"reference": "90bf1bc4a51ceca93590d09e7365317b28d1eb89"
|
||||
"reference": "5225f52a82d4128e69ad17c2a81fcfea6aa00ae1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/90bf1bc4a51ceca93590d09e7365317b28d1eb89",
|
||||
"reference": "90bf1bc4a51ceca93590d09e7365317b28d1eb89",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/5225f52a82d4128e69ad17c2a81fcfea6aa00ae1",
|
||||
"reference": "5225f52a82d4128e69ad17c2a81fcfea6aa00ae1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4099,9 +4099,9 @@
|
||||
"utopia-php/validators": "0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.25.*",
|
||||
"laravel/pint": "1.29.*",
|
||||
"phpstan/phpstan": "2.0.*",
|
||||
"phpunit/phpunit": "12.4.*",
|
||||
"phpunit/phpunit": "12.5.*",
|
||||
"swoole/ide-helper": "5.1.8"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -4130,9 +4130,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/dns/issues",
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.7.0"
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.7.2"
|
||||
},
|
||||
"time": "2026-05-13T07:11:31+00:00"
|
||||
"time": "2026-05-20T04:49:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
@@ -4245,16 +4245,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/emails",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/emails.git",
|
||||
"reference": "115e24aa908e2b1f06c7ff3b94434a0bdbed9107"
|
||||
"reference": "a5f1d111e5023918731f2de96d348f5b6a0de143"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/emails/zipball/115e24aa908e2b1f06c7ff3b94434a0bdbed9107",
|
||||
"reference": "115e24aa908e2b1f06c7ff3b94434a0bdbed9107",
|
||||
"url": "https://api.github.com/repos/utopia-php/emails/zipball/a5f1d111e5023918731f2de96d348f5b6a0de143",
|
||||
"reference": "a5f1d111e5023918731f2de96d348f5b6a0de143",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4300,9 +4300,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/emails/issues",
|
||||
"source": "https://github.com/utopia-php/emails/tree/0.7.0"
|
||||
"source": "https://github.com/utopia-php/emails/tree/0.7.1"
|
||||
},
|
||||
"time": "2026-05-13T05:01:26+00:00"
|
||||
"time": "2026-05-20T13:05:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/fetch",
|
||||
@@ -4606,16 +4606,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/messaging",
|
||||
"version": "0.22.2",
|
||||
"version": "0.22.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/messaging.git",
|
||||
"reference": "f99feceab575243f3a86ee2e90cd1a6407805def"
|
||||
"reference": "67366d5f45cc92efe7adb6aab5d6dcd2342f2f9e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/f99feceab575243f3a86ee2e90cd1a6407805def",
|
||||
"reference": "f99feceab575243f3a86ee2e90cd1a6407805def",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/67366d5f45cc92efe7adb6aab5d6dcd2342f2f9e",
|
||||
"reference": "67366d5f45cc92efe7adb6aab5d6dcd2342f2f9e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4651,9 +4651,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/messaging/issues",
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.22.2"
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.22.3"
|
||||
},
|
||||
"time": "2026-05-14T08:51:26+00:00"
|
||||
"time": "2026-05-19T05:31:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
@@ -5638,16 +5638,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.29.5",
|
||||
"version": "1.31.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "e670edcdfb9ffcec36125b1eb3e4473dce30b620"
|
||||
"reference": "5699f6da951aef9378fabdcf12f40a9a54fb3128"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e670edcdfb9ffcec36125b1eb3e4473dce30b620",
|
||||
"reference": "e670edcdfb9ffcec36125b1eb3e4473dce30b620",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/5699f6da951aef9378fabdcf12f40a9a54fb3128",
|
||||
"reference": "5699f6da951aef9378fabdcf12f40a9a54fb3128",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5656,7 +5656,7 @@
|
||||
"ext-mbstring": "*",
|
||||
"matthiasmullie/minify": "1.3.*",
|
||||
"php": ">=8.3",
|
||||
"twig/twig": "3.14.*"
|
||||
"twig/twig": "3.26.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "7.*",
|
||||
@@ -5683,9 +5683,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.29.5"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.31.1"
|
||||
},
|
||||
"time": "2026-05-15T06:49:05+00:00"
|
||||
"time": "2026-05-20T22:22:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
@@ -6394,11 +6394,11 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.54",
|
||||
"version": "2.1.55",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
|
||||
"reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566",
|
||||
"reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6443,7 +6443,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-29T13:31:09+00:00"
|
||||
"time": "2026-05-18T11:57:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
@@ -6882,23 +6882,23 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
"version": "4.2.0",
|
||||
"version": "4.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/cli-parser.git",
|
||||
"reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04"
|
||||
"reference": "7d05781b13f7dec9043a629a21d086ed74582a15"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04",
|
||||
"reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15",
|
||||
"reference": "7d05781b13f7dec9043a629a21d086ed74582a15",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.0"
|
||||
"phpunit/phpunit": "^12.5.25"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -6927,7 +6927,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
|
||||
"security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0"
|
||||
"source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6947,7 +6947,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-14T09:36:45+00:00"
|
||||
"time": "2026-05-17T05:29:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
@@ -7244,25 +7244,25 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "7.0.2",
|
||||
"version": "7.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "016951ae10980765e4e7aee491eb288c64e505b7"
|
||||
"reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7",
|
||||
"reference": "016951ae10980765e4e7aee491eb288c64e505b7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
|
||||
"reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.3",
|
||||
"sebastian/recursion-context": "^7.0"
|
||||
"sebastian/recursion-context": "^7.0.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.0"
|
||||
"phpunit/phpunit": "^12.5.25"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -7310,7 +7310,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2"
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7330,7 +7330,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-24T06:16:11+00:00"
|
||||
"time": "2026-05-20T04:37:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
@@ -7408,24 +7408,24 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
|
||||
"reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f"
|
||||
"reference": "d543b8ef219dcd8da262cbb958639a96bedba10e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f",
|
||||
"reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e",
|
||||
"reference": "d543b8ef219dcd8da262cbb958639a96bedba10e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^5.0",
|
||||
"nikic/php-parser": "^5.7.0",
|
||||
"php": ">=8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.0"
|
||||
"phpunit/phpunit": "^12.5.25"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -7454,15 +7454,27 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
|
||||
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0"
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://liberapay.com/sebastianbergmann",
|
||||
"type": "liberapay"
|
||||
},
|
||||
{
|
||||
"url": "https://thanks.dev/u/gh/sebastianbergmann",
|
||||
"type": "thanks_dev"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-07T04:57:28+00:00"
|
||||
"time": "2026-05-19T16:22:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-enumerator",
|
||||
@@ -7656,23 +7668,23 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/type",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/type.git",
|
||||
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d"
|
||||
"reference": "82ff822c2edc46724be9f7411d3163021f602773"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773",
|
||||
"reference": "82ff822c2edc46724be9f7411d3163021f602773",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.0"
|
||||
"phpunit/phpunit": "^12.5.25"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -7701,7 +7713,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||
"security": "https://github.com/sebastianbergmann/type/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/type/tree/6.0.3"
|
||||
"source": "https://github.com/sebastianbergmann/type/tree/6.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7721,7 +7733,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-09T06:57:12+00:00"
|
||||
"time": "2026-05-20T06:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/version",
|
||||
@@ -8201,86 +8213,6 @@
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php81\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v8.0.11",
|
||||
@@ -8537,26 +8469,27 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.14.2",
|
||||
"version": "v3.26.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a"
|
||||
"reference": "1fcae487b180d78e6351f4e0afa91f9eab96a2bc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a",
|
||||
"reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/1fcae487b180d78e6351f4e0afa91f9eab96a2bc",
|
||||
"reference": "1fcae487b180d78e6351f4e0afa91f9eab96a2bc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"php": ">=8.1.0",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php81": "^1.29"
|
||||
"symfony/polyfill-mbstring": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-cs-fixer/shim": "^3.0@stable",
|
||||
"phpstan/phpstan": "^2.0@stable",
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
|
||||
},
|
||||
@@ -8600,7 +8533,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.14.2"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.26.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8612,14 +8545,12 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-07T12:36:22+00:00"
|
||||
"time": "2026-05-20T07:31:59+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"utopia-php/http": 5
|
||||
},
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
@@ -72,7 +72,7 @@ class Get extends Action
|
||||
->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true, example: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15')
|
||||
->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true, example: 'true')
|
||||
->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true, example: 'en-US')
|
||||
->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true, example: 'america/new_york')
|
||||
->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true, example: 'America/New_York')
|
||||
->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true, example: '37.7749')
|
||||
->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true, example: '-122.4194')
|
||||
->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true, example: '100')
|
||||
|
||||
@@ -60,12 +60,12 @@ class Update extends Action
|
||||
))
|
||||
->param('host', null, new Nullable(new Hostname()), 'SMTP server hostname (domain)', optional: true)
|
||||
->param('port', null, new Nullable(new Integer()), 'SMTP server port', optional: true)
|
||||
->param('username', null, new Nullable(new Text(256)), 'SMTP server username. Leave empty for no authorization.', optional: true)
|
||||
->param('password', null, new Nullable(new Text(256)), 'SMTP server password. Leave empty for no authorization. This property is stored securely and cannot be read in future (write-only).', optional: true)
|
||||
->param('senderEmail', null, new Nullable(new Email()), 'Email address shown in inbox as the sender of the email.', optional: true)
|
||||
->param('senderName', null, new Nullable(new Text(256)), 'Name shown in inbox as the sender of the email.', optional: true)
|
||||
->param('replyToEmail', null, new Nullable(new Email()), 'Email used when user replies to the email.', optional: true)
|
||||
->param('replyToName', null, new Nullable(new Text(256)), 'Name used when user replies to the email.', optional: true)
|
||||
->param('username', null, new Nullable(new Text(256, 0)), 'SMTP server username. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('password', null, new Nullable(new Text(256, 0)), 'SMTP server password. Pass an empty string to clear a previously set value. This property is stored securely and cannot be read in future (write-only).', optional: true)
|
||||
->param('senderEmail', null, new Nullable(new Email(allowEmpty: true)), 'Email address shown in inbox as the sender of the email. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('senderName', null, new Nullable(new Text(256, 0)), 'Name shown in inbox as the sender of the email. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('replyToEmail', null, new Nullable(new Email(allowEmpty: true)), 'Email used when user replies to the email. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('replyToName', null, new Nullable(new Text(256, 0)), 'Name used when user replies to the email. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('secure', null, new Nullable(new WhiteList(['tls', 'ssl'], true)), 'Configures if communication with SMTP server is encrypted. Allowed values are: tls, ssl. Leave empty for no encryption.', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'Enable or disable custom SMTP. Custom SMTP is useful for branding purposes, but also allows use of custom email templates.', optional: true)
|
||||
->inject('response')
|
||||
@@ -95,7 +95,8 @@ class Update extends Action
|
||||
// Fetch current configuration
|
||||
$smtp = $project->getAttribute('smtp', []);
|
||||
|
||||
// Apply changes
|
||||
// Apply changes — null means "not provided, keep existing".
|
||||
// Empty string explicitly clears a previously-set value.
|
||||
$keys = ['host', 'port', 'username', 'password', 'senderEmail', 'senderName', 'replyToEmail', 'replyToName', 'secure', 'enabled'];
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_null(${$key})) {
|
||||
@@ -120,7 +121,7 @@ class Update extends Action
|
||||
// Validate when the caller is explicitly enabling or hasn't expressed a preference
|
||||
// (so a credentials-only PATCH can auto-enable). Skip only when the caller is
|
||||
// explicitly keeping/turning SMTP off.
|
||||
if (\is_null($enabled) || $enabled === true) {
|
||||
if ((\is_null($enabled) || $enabled === true) && !empty($smtp['senderEmail'] ?? '')) {
|
||||
$mail = new PHPMailer(true);
|
||||
$mail->isSMTP();
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ class Update extends Action
|
||||
->param('subject', null, new Nullable(new Text(255)), 'Subject of the email template. Can be up to 255 characters.', optional: true)
|
||||
->param('message', null, new Nullable(new Text(10485760)), 'Plain or HTML body of the email template message. Can be up to 10MB of content.', optional: true)
|
||||
->param('senderName', null, new Nullable(new Text(255, 0)), 'Name of the email sender.', optional: true)
|
||||
->param('senderEmail', null, new Nullable(new Email()), 'Email of the sender.', optional: true)
|
||||
->param('replyToEmail', null, new Nullable(new Email()), 'Reply to email.', optional: true)
|
||||
->param('senderEmail', null, new Nullable(new Email(allowEmpty: true)), 'Email of the sender. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('replyToEmail', null, new Nullable(new Email(allowEmpty: true)), 'Reply to email. Pass an empty string to clear a previously set value.', optional: true)
|
||||
->param('replyToName', null, new Nullable(new Text(255, 0)), 'Reply to name.', optional: true)
|
||||
->inject('response')
|
||||
->inject('queueForEvents')
|
||||
@@ -99,7 +99,8 @@ class Update extends Action
|
||||
$templates = $project->getAttribute('templates', []);
|
||||
$template = $templates['email.' . $templateId . '-' . $locale] ?? [];
|
||||
|
||||
// Apply changes
|
||||
// Apply changes — null means "not provided, keep existing".
|
||||
// Empty string explicitly clears a previously-set value.
|
||||
$keys = ['senderName', 'senderEmail', 'replyToEmail', 'replyToName', 'message', 'subject'];
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_null(${$key})) {
|
||||
|
||||
@@ -29,6 +29,7 @@ abstract class Format
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'endpoint' => 'https://localhost',
|
||||
'endpoint.docs' => 'https://<REGION>.cloud.appwrite.io/v1',
|
||||
'version' => '1.0.0',
|
||||
'terms' => '',
|
||||
'support.email' => '',
|
||||
@@ -1032,6 +1033,19 @@ abstract class Format
|
||||
return $values;
|
||||
}
|
||||
|
||||
protected function shouldEmitDefaultForSchema(mixed $default, array $schema): bool
|
||||
{
|
||||
if (isset($schema['enum'])) {
|
||||
return \in_array($default, $schema['enum'], true);
|
||||
}
|
||||
|
||||
if (isset($schema['items']['enum'])) {
|
||||
return \is_array($default) && empty(\array_diff($default, $schema['items']['enum']));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getRequestParameterConfig(string $service, string $method, string $param, bool $optional, bool $nullable, mixed $default): array
|
||||
{
|
||||
$config = [
|
||||
|
||||
@@ -55,11 +55,22 @@ class OpenAPI3 extends Format
|
||||
'servers' => [
|
||||
[
|
||||
'url' => $this->getParam('endpoint', ''),
|
||||
'description' => 'Appwrite Cloud endpoint.',
|
||||
],
|
||||
[
|
||||
'url' => $this->getParam('endpoint.docs', ''),
|
||||
'url' => \str_replace('<REGION>', '{region}', $this->getParam('endpoint.docs', '')),
|
||||
'description' => 'Appwrite Cloud regional endpoint. Replace `{region}` with your project region.',
|
||||
'variables' => [
|
||||
'region' => [
|
||||
'default' => 'fra',
|
||||
'description' => 'Appwrite Cloud region.',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'x-appwrite' => [
|
||||
'endpointDocs' => $this->getParam('endpoint.docs', ''),
|
||||
],
|
||||
'paths' => [],
|
||||
'tags' => $this->services,
|
||||
'components' => [
|
||||
@@ -768,7 +779,7 @@ class OpenAPI3 extends Format
|
||||
break;
|
||||
}
|
||||
|
||||
if ($parameter['emitDefault']) { // Param has default value
|
||||
if ($parameter['emitDefault'] && $this->shouldEmitDefaultForSchema($param['default'], $node['schema'])) { // Param has default value
|
||||
$node['schema']['default'] = $param['default'];
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ class Swagger2 extends Format
|
||||
],
|
||||
'host' => \parse_url($this->getParam('endpoint', ''), PHP_URL_HOST),
|
||||
'x-host-docs' => \parse_url($this->getParam('endpoint.docs', ''), PHP_URL_HOST),
|
||||
'x-appwrite' => [
|
||||
'endpointDocs' => $this->getParam('endpoint.docs', ''),
|
||||
],
|
||||
'basePath' => \parse_url($this->getParam('endpoint', ''), PHP_URL_PATH),
|
||||
'schemes' => [\parse_url($this->getParam('endpoint', ''), PHP_URL_SCHEME)],
|
||||
'consumes' => ['application/json', 'multipart/form-data'],
|
||||
@@ -731,7 +734,7 @@ class Swagger2 extends Format
|
||||
break;
|
||||
}
|
||||
|
||||
if ($parameter['emitDefault']) { // Param has default value
|
||||
if ($parameter['emitDefault'] && $this->shouldEmitDefaultForSchema($param['default'], $node)) { // Param has default value
|
||||
$node['default'] = $param['default'];
|
||||
}
|
||||
|
||||
@@ -767,10 +770,13 @@ class Swagger2 extends Format
|
||||
$body['schema']['properties'][$name] = [
|
||||
'type' => $node['type'],
|
||||
'description' => $node['description'],
|
||||
'default' => $node['default'] ?? null,
|
||||
'x-example' => $node['x-example'] ?? null,
|
||||
];
|
||||
|
||||
if (\array_key_exists('default', $node)) {
|
||||
$body['schema']['properties'][$name]['default'] = $node['default'];
|
||||
}
|
||||
|
||||
if (isset($node['format'])) {
|
||||
$body['schema']['properties'][$name]['format'] = $node['format'];
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class HTTPTest extends Scope
|
||||
$body = $response['body'];
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsString($body['server']);
|
||||
$this->assertIsString($body['client-web']);
|
||||
$this->assertIsString($body['server-web']);
|
||||
$this->assertIsString($body['client-flutter']);
|
||||
$this->assertIsString($body['console-web']);
|
||||
$this->assertIsString($body['server-nodejs']);
|
||||
|
||||
@@ -1026,123 +1026,101 @@ class AccountCustomClientTest extends Scope
|
||||
// Use fresh account for predictable log count
|
||||
$data = $this->createFreshAccountWithSession();
|
||||
$session = $data['session'];
|
||||
$headers = array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
$this->assertEventually(function () use ($headers) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/logs', $headers);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsArray($response['body']['logs']);
|
||||
$this->assertNotEmpty($response['body']['logs']);
|
||||
// Fresh account: session.create is always logged. user.create audit may or may not
|
||||
// be present depending on async audit processing timing.
|
||||
$logCount = count($response['body']['logs']);
|
||||
$this->assertContains($logCount, [1, 2]);
|
||||
$this->assertIsNumeric($response['body']['total']);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertIsArray($response['body']['logs']);
|
||||
$this->assertNotEmpty($response['body']['logs']);
|
||||
$logCount = count($response['body']['logs']);
|
||||
$this->assertContains($logCount, [1, 2]);
|
||||
$this->assertIsNumeric($response['body']['total']);
|
||||
|
||||
// Check session.create log (logs[0] - most recent)
|
||||
$this->assertEquals('Windows', $response['body']['logs'][0]['osName']);
|
||||
$this->assertEquals('WIN', $response['body']['logs'][0]['osCode']);
|
||||
$this->assertEquals('10', $response['body']['logs'][0]['osVersion']);
|
||||
$this->assertEquals('session.create', $response['body']['logs'][0]['event']);
|
||||
$this->assertEquals('Windows', $response['body']['logs'][0]['osName']);
|
||||
$this->assertEquals('WIN', $response['body']['logs'][0]['osCode']);
|
||||
$this->assertEquals('10', $response['body']['logs'][0]['osVersion']);
|
||||
|
||||
$this->assertEquals('browser', $response['body']['logs'][0]['clientType']);
|
||||
$this->assertEquals('Chrome', $response['body']['logs'][0]['clientName']);
|
||||
$this->assertEquals('CH', $response['body']['logs'][0]['clientCode']);
|
||||
$this->assertEquals('70.0', $response['body']['logs'][0]['clientVersion']);
|
||||
$this->assertEquals('Blink', $response['body']['logs'][0]['clientEngine']);
|
||||
$this->assertEquals('browser', $response['body']['logs'][0]['clientType']);
|
||||
$this->assertEquals('Chrome', $response['body']['logs'][0]['clientName']);
|
||||
$this->assertEquals('CH', $response['body']['logs'][0]['clientCode']);
|
||||
$this->assertEquals('70.0', $response['body']['logs'][0]['clientVersion']);
|
||||
$this->assertEquals('Blink', $response['body']['logs'][0]['clientEngine']);
|
||||
|
||||
$this->assertEquals('desktop', $response['body']['logs'][0]['deviceName']);
|
||||
$this->assertEquals('', $response['body']['logs'][0]['deviceBrand']);
|
||||
$this->assertEquals('', $response['body']['logs'][0]['deviceModel']);
|
||||
$this->assertEquals(filter_var($response['body']['logs'][0]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][0]['ip']);
|
||||
$this->assertEquals('desktop', $response['body']['logs'][0]['deviceName']);
|
||||
$this->assertEquals('', $response['body']['logs'][0]['deviceBrand']);
|
||||
$this->assertEquals('', $response['body']['logs'][0]['deviceModel']);
|
||||
$this->assertEquals(filter_var($response['body']['logs'][0]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][0]['ip']);
|
||||
|
||||
$this->assertEquals('--', $response['body']['logs'][0]['countryCode']);
|
||||
$this->assertEquals('Unknown', $response['body']['logs'][0]['countryName']);
|
||||
$this->assertEquals('--', $response['body']['logs'][0]['countryCode']);
|
||||
$this->assertEquals('Unknown', $response['body']['logs'][0]['countryName']);
|
||||
|
||||
if ($logCount === 2) {
|
||||
// Check user.create log (logs[1] - oldest)
|
||||
$this->assertEquals('user.create', $response['body']['logs'][1]['event']);
|
||||
$this->assertEquals(filter_var($response['body']['logs'][1]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][1]['ip']);
|
||||
$this->assertTrue((new DatetimeValidator())->isValid($response['body']['logs'][1]['time']));
|
||||
}
|
||||
if ($logCount === 2) {
|
||||
$this->assertEquals('user.create', $response['body']['logs'][1]['event']);
|
||||
$this->assertEquals(filter_var($response['body']['logs'][1]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][1]['ip']);
|
||||
$this->assertTrue((new DatetimeValidator())->isValid($response['body']['logs'][1]['time']));
|
||||
}
|
||||
|
||||
$responseLimit = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'queries' => [
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$responseLimit = $this->client->call(Client::METHOD_GET, '/account/logs', $headers, [
|
||||
'queries' => [
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $responseLimit['headers']['status-code']);
|
||||
$this->assertIsArray($responseLimit['body']['logs']);
|
||||
$this->assertNotEmpty($responseLimit['body']['logs']);
|
||||
$this->assertCount(1, $responseLimit['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimit['body']['total']);
|
||||
$this->assertEquals(200, $responseLimit['headers']['status-code']);
|
||||
$this->assertIsArray($responseLimit['body']['logs']);
|
||||
$this->assertNotEmpty($responseLimit['body']['logs']);
|
||||
$this->assertCount(1, $responseLimit['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimit['body']['total']);
|
||||
|
||||
$this->assertEquals($response['body']['logs'][0], $responseLimit['body']['logs'][0]);
|
||||
$this->assertEquals($response['body']['logs'][0], $responseLimit['body']['logs'][0]);
|
||||
|
||||
$responseOffset = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString()
|
||||
]
|
||||
]);
|
||||
$responseOffset = $this->client->call(Client::METHOD_GET, '/account/logs', $headers, [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($responseOffset['headers']['status-code'], 200);
|
||||
$this->assertIsArray($responseOffset['body']['logs']);
|
||||
// With offset(1), remaining logs = logCount - 1
|
||||
$this->assertCount($logCount - 1, $responseOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseOffset['body']['total']);
|
||||
$this->assertEquals(200, $responseOffset['headers']['status-code']);
|
||||
$this->assertIsArray($responseOffset['body']['logs']);
|
||||
$this->assertCount($logCount - 1, $responseOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseOffset['body']['total']);
|
||||
|
||||
if ($logCount === 2) {
|
||||
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
|
||||
}
|
||||
if ($logCount === 2) {
|
||||
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
|
||||
}
|
||||
|
||||
$responseLimitOffset = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString(),
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
$responseLimitOffset = $this->client->call(Client::METHOD_GET, '/account/logs', $headers, [
|
||||
'queries' => [
|
||||
Query::offset(1)->toString(),
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $responseLimitOffset['headers']['status-code']);
|
||||
$this->assertIsArray($responseLimitOffset['body']['logs']);
|
||||
// With offset(1)+limit(1), remaining logs = min(1, logCount - 1)
|
||||
$this->assertCount(min(1, $logCount - 1), $responseLimitOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimitOffset['body']['total']);
|
||||
$this->assertEquals(200, $responseLimitOffset['headers']['status-code']);
|
||||
$this->assertIsArray($responseLimitOffset['body']['logs']);
|
||||
$this->assertCount(min(1, $logCount - 1), $responseLimitOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimitOffset['body']['total']);
|
||||
|
||||
if ($logCount === 2) {
|
||||
$this->assertEquals($response['body']['logs'][1], $responseLimitOffset['body']['logs'][0]);
|
||||
}
|
||||
if ($logCount === 2) {
|
||||
$this->assertEquals($response['body']['logs'][1], $responseLimitOffset['body']['logs'][0]);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test for total=false
|
||||
*/
|
||||
$logsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
$logsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/account/logs', $headers, [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
|
||||
@@ -2778,17 +2778,17 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEmpty($executions['body']['executions'][0]['logs']);
|
||||
$this->assertEmpty($executions['body']['executions'][0]['errors']);
|
||||
|
||||
// Ensure executions count
|
||||
$executions = $this->listExecutions($functionId);
|
||||
$this->assertEventually(function () use ($functionId) {
|
||||
$executions = $this->listExecutions($functionId);
|
||||
|
||||
$this->assertEquals(200, $executions['headers']['status-code']);
|
||||
$this->assertCount(3, $executions['body']['executions']);
|
||||
$this->assertEquals(200, $executions['headers']['status-code']);
|
||||
$this->assertCount(3, $executions['body']['executions']);
|
||||
|
||||
// Double check logs and errors are empty
|
||||
foreach ($executions['body']['executions'] as $execution) {
|
||||
$this->assertEmpty($execution['logs']);
|
||||
$this->assertEmpty($execution['errors']);
|
||||
}
|
||||
foreach ($executions['body']['executions'] as $execution) {
|
||||
$this->assertEmpty($execution['logs']);
|
||||
$this->assertEmpty($execution['errors']);
|
||||
}
|
||||
}, 10000, 500);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
@@ -294,6 +294,7 @@ trait SMTPBase
|
||||
|
||||
public function testUpdateSMTPEmptySenderName(): void
|
||||
{
|
||||
// Empty sender name is valid — PHPMailer accepts '' as display name.
|
||||
$response = $this->updateSMTP(
|
||||
senderName: '',
|
||||
senderEmail: 'sender@example.com',
|
||||
@@ -301,11 +302,17 @@ trait SMTPBase
|
||||
port: 1025,
|
||||
);
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertSame('', $response['body']['smtpSenderName']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPEmptySenderEmail(): void
|
||||
{
|
||||
// Empty senderEmail clears the stored value; connection test is skipped when
|
||||
// there is no valid From address, so this is accepted even without enabled=false.
|
||||
$response = $this->updateSMTP(
|
||||
senderName: 'Test',
|
||||
senderEmail: '',
|
||||
@@ -313,7 +320,11 @@ trait SMTPBase
|
||||
port: 1025,
|
||||
);
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertSame('', $response['body']['smtpSenderEmail']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPEmptyHost(): void
|
||||
@@ -353,6 +364,59 @@ trait SMTPBase
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPReplyToEmailCanBeCleared(): void
|
||||
{
|
||||
// Step 1: Set a custom replyToEmail.
|
||||
$set = $this->updateSMTP(
|
||||
senderName: 'Test Sender',
|
||||
senderEmail: 'sender@example.com',
|
||||
host: 'maildev',
|
||||
port: 1025,
|
||||
replyToEmail: 'reply@example.com',
|
||||
);
|
||||
$this->assertSame(200, $set['headers']['status-code']);
|
||||
$this->assertSame('reply@example.com', $set['body']['smtpReplyToEmail']);
|
||||
|
||||
// Step 2: Clear it with an empty string.
|
||||
$clear = $this->updateSMTP(replyToEmail: '');
|
||||
$this->assertSame(200, $clear['headers']['status-code']);
|
||||
$this->assertSame('', $clear['body']['smtpReplyToEmail']);
|
||||
|
||||
// Step 3: Verify the cleared value persists.
|
||||
$verify = $this->updateSMTP();
|
||||
$this->assertSame(200, $verify['headers']['status-code']);
|
||||
$this->assertSame('', $verify['body']['smtpReplyToEmail']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPSenderEmailCanBeClearedWhenDisabled(): void
|
||||
{
|
||||
// Step 1: Configure SMTP with a sender email, then disable it.
|
||||
$this->updateSMTP(
|
||||
senderName: 'Test Sender',
|
||||
senderEmail: 'sender@example.com',
|
||||
host: 'maildev',
|
||||
port: 1025,
|
||||
enabled: false,
|
||||
);
|
||||
|
||||
// Step 2: Clear senderEmail while keeping SMTP disabled.
|
||||
// enabled=false skips the PHPMailer connection check so empty senderEmail is valid.
|
||||
$clear = $this->updateSMTP(
|
||||
senderEmail: '',
|
||||
enabled: false,
|
||||
);
|
||||
$this->assertSame(200, $clear['headers']['status-code']);
|
||||
$this->assertSame('', $clear['body']['smtpSenderEmail']);
|
||||
|
||||
// Step 3: Verify the cleared value persists.
|
||||
$verify = $this->updateSMTP(enabled: false);
|
||||
$this->assertSame(200, $verify['headers']['status-code']);
|
||||
$this->assertSame('', $verify['body']['smtpSenderEmail']);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPInvalidSecure(): void
|
||||
{
|
||||
$response = $this->updateSMTP(
|
||||
@@ -461,6 +525,7 @@ trait SMTPBase
|
||||
|
||||
public function testUpdateSMTPUsernameEmpty(): void
|
||||
{
|
||||
// Empty string clears a previously-set username (no-auth SMTP).
|
||||
$response = $this->updateSMTP(
|
||||
senderName: 'Test',
|
||||
senderEmail: 'sender@example.com',
|
||||
@@ -469,7 +534,11 @@ trait SMTPBase
|
||||
username: '',
|
||||
);
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
$this->assertSame('', $response['body']['smtpUsername']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPPasswordMinLength(): void
|
||||
@@ -524,6 +593,7 @@ trait SMTPBase
|
||||
|
||||
public function testUpdateSMTPPasswordEmpty(): void
|
||||
{
|
||||
// Empty string clears a previously-set password (no-auth SMTP).
|
||||
$response = $this->updateSMTP(
|
||||
senderName: 'Test',
|
||||
senderEmail: 'sender@example.com',
|
||||
@@ -532,7 +602,45 @@ trait SMTPBase
|
||||
password: '',
|
||||
);
|
||||
|
||||
$this->assertSame(400, $response['headers']['status-code']);
|
||||
$this->assertSame(200, $response['headers']['status-code']);
|
||||
// smtpPassword is write-only and never echoed back.
|
||||
$this->assertSame('', $response['body']['smtpPassword']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPCredentialsCanBeCleared(): void
|
||||
{
|
||||
// Step 1: Set username and password.
|
||||
$set = $this->updateSMTP(
|
||||
senderName: 'Test Sender',
|
||||
senderEmail: 'sender@example.com',
|
||||
host: 'maildev',
|
||||
port: 1025,
|
||||
username: 'myuser',
|
||||
password: 'mypassword',
|
||||
);
|
||||
$this->assertSame(200, $set['headers']['status-code']);
|
||||
$this->assertSame('myuser', $set['body']['smtpUsername']);
|
||||
|
||||
// Step 2: Clear both credentials by passing empty strings.
|
||||
$clear = $this->updateSMTP(
|
||||
username: '',
|
||||
password: '',
|
||||
);
|
||||
$this->assertSame(200, $clear['headers']['status-code']);
|
||||
$this->assertSame('', $clear['body']['smtpUsername']);
|
||||
// smtpPassword is write-only and never echoed back regardless.
|
||||
$this->assertSame('', $clear['body']['smtpPassword']);
|
||||
|
||||
// Step 3: Verify the cleared username persists (a no-params PATCH must not restore it).
|
||||
$verify = $this->updateSMTP();
|
||||
$this->assertSame(200, $verify['headers']['status-code']);
|
||||
$this->assertSame('', $verify['body']['smtpUsername']);
|
||||
|
||||
// Cleanup
|
||||
$this->updateSMTP(enabled: false);
|
||||
}
|
||||
|
||||
public function testUpdateSMTPWithoutSecure(): void
|
||||
|
||||
@@ -548,6 +548,59 @@ trait TemplatesBase
|
||||
$this->assertSame(401, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testUpdateEmailTemplateSenderFieldsCanBeCleared(): void
|
||||
{
|
||||
$this->ensureSMTPEnabled();
|
||||
|
||||
// Step 1: Set a custom en verification template with sender and reply-to fields.
|
||||
$first = $this->updateEmailTemplate(
|
||||
templateId: 'verification',
|
||||
locale: 'en',
|
||||
subject: 'Verify your email',
|
||||
message: 'Please verify: {{url}}',
|
||||
senderName: 'Custom Sender',
|
||||
senderEmail: 'custom-sender@appwrite.io',
|
||||
replyToName: 'Custom Reply',
|
||||
replyToEmail: 'custom-reply@appwrite.io',
|
||||
);
|
||||
$this->assertSame(200, $first['headers']['status-code']);
|
||||
$this->assertSame('Custom Sender', $first['body']['senderName']);
|
||||
$this->assertSame('custom-sender@appwrite.io', $first['body']['senderEmail']);
|
||||
$this->assertSame('Custom Reply', $first['body']['replyToName']);
|
||||
$this->assertSame('custom-reply@appwrite.io', $first['body']['replyToEmail']);
|
||||
|
||||
// Step 2: GET en verification template and ensure it reflects the custom values.
|
||||
$get = $this->getEmailTemplate('verification', 'en');
|
||||
$this->assertSame(200, $get['headers']['status-code']);
|
||||
$this->assertSame('Custom Sender', $get['body']['senderName']);
|
||||
$this->assertSame('custom-sender@appwrite.io', $get['body']['senderEmail']);
|
||||
$this->assertSame('Custom Reply', $get['body']['replyToName']);
|
||||
$this->assertSame('custom-reply@appwrite.io', $get['body']['replyToEmail']);
|
||||
|
||||
// Step 3: Update the same template, clearing sender and reply-to fields to empty strings.
|
||||
$clear = $this->updateEmailTemplate(
|
||||
templateId: 'verification',
|
||||
locale: 'en',
|
||||
senderName: '',
|
||||
senderEmail: '',
|
||||
replyToName: '',
|
||||
replyToEmail: '',
|
||||
);
|
||||
$this->assertSame(200, $clear['headers']['status-code']);
|
||||
$this->assertSame('', $clear['body']['senderName']);
|
||||
$this->assertSame('', $clear['body']['senderEmail']);
|
||||
$this->assertSame('', $clear['body']['replyToName']);
|
||||
$this->assertSame('', $clear['body']['replyToEmail']);
|
||||
|
||||
// Step 4: GET again to confirm the cleared values persist.
|
||||
$getAfter = $this->getEmailTemplate('verification', 'en');
|
||||
$this->assertSame(200, $getAfter['headers']['status-code']);
|
||||
$this->assertSame('', $getAfter['body']['senderName']);
|
||||
$this->assertSame('', $getAfter['body']['senderEmail']);
|
||||
$this->assertSame('', $getAfter['body']['replyToName']);
|
||||
$this->assertSame('', $getAfter['body']['replyToEmail']);
|
||||
}
|
||||
|
||||
public function testUpdateEmailTemplateBlockedWhenSMTPDisabled(): void
|
||||
{
|
||||
// Custom templates only make sense alongside a custom SMTP configuration.
|
||||
|
||||
Reference in New Issue
Block a user