diff --git a/app/config/errors.php b/app/config/errors.php index 644a0c75b3..6d9d29a8ea 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -81,7 +81,7 @@ return [ ], Exception::GENERAL_ROUTE_NOT_FOUND => [ 'name' => Exception::GENERAL_ROUTE_NOT_FOUND, - 'description' => 'The requested route was not found. Please refer to the API docs and try again.', + 'description' => 'Route not found. Please ensure the endpoint is configured correctly and that the API route is valid for this SDK version. Refer to the API docs for more details.', 'code' => 404, ], Exception::GENERAL_CURSOR_NOT_FOUND => [ diff --git a/app/config/frameworks.php b/app/config/frameworks.php index ae9830314d..f4d8ec7ffa 100644 --- a/app/config/frameworks.php +++ b/app/config/frameworks.php @@ -260,6 +260,23 @@ return [ ], ], ], + 'react-native' => [ + 'key' => 'react-native', + 'name' => 'React Native', + 'screenshotSleep' => 3000, + 'buildRuntime' => 'node-22', + 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'adapters' => [ + 'static' => [ + 'key' => 'static', + 'buildCommand' => 'npm run build', + 'installCommand' => 'npm install', + 'outputDirectory' => './dist', + 'startCommand' => 'bash helpers/server.sh', + 'fallbackFile' => 'index.html' + ] + ] + ], 'vite' => [ 'key' => 'vite', 'name' => 'Vite', diff --git a/app/config/platforms.php b/app/config/platforms.php index 98afd1a9b4..aff6aaf7c0 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -134,7 +134,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.7.3', + 'version' => '0.7.4', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, diff --git a/app/config/specs/open-api3-1.6.x-client.json b/app/config/specs/open-api3-1.6.x-client.json index 3cb424e53b..e84c511f78 100644 --- a/app/config/specs/open-api3-1.6.x-client.json +++ b/app/config/specs/open-api3-1.6.x-client.json @@ -4485,26 +4485,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/open-api3-1.6.x-console.json b/app/config/specs/open-api3-1.6.x-console.json index ede32a8dbc..16ac0ee040 100644 --- a/app/config/specs/open-api3-1.6.x-console.json +++ b/app/config/specs/open-api3-1.6.x-console.json @@ -7976,26 +7976,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/open-api3-1.6.x-server.json b/app/config/specs/open-api3-1.6.x-server.json index 584caa2956..e3d2255c8d 100644 --- a/app/config/specs/open-api3-1.6.x-server.json +++ b/app/config/specs/open-api3-1.6.x-server.json @@ -7525,26 +7525,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 355ea4800f..528645eaca 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3381,13 +3381,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -3513,13 +3513,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -4059,13 +4059,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -4485,26 +4485,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -6410,13 +6390,13 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" }, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index f1df40d7d9..66d90ce15f 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3386,13 +3386,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -3518,13 +3518,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -4064,13 +4064,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -8044,26 +8044,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -8967,6 +8947,14 @@ "items": { "type": "string" } + }, + "lengths": { + "type": "array", + "description": "Length of index. Maximum of 100", + "x-example": null, + "items": { + "type": "integer" + } } }, "required": [ @@ -25729,6 +25717,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -26372,6 +26361,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -29143,13 +29133,13 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" }, @@ -37163,6 +37153,15 @@ }, "x-example": [] }, + "lengths": { + "type": "array", + "description": "Index attributes length.", + "items": { + "type": "integer", + "format": "int32" + }, + "x-example": [] + }, "orders": { "type": "array", "description": "Index orders.", @@ -37189,6 +37188,7 @@ "status", "error", "attributes", + "lengths", "$createdAt", "$updatedAt" ] diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 8c94f21bad..44b9c4be42 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3068,13 +3068,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -3202,13 +3202,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -3752,13 +3752,13 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" } @@ -7525,26 +7525,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/components\/schemas\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -8368,6 +8348,14 @@ "items": { "type": "string" } + }, + "lengths": { + "type": "array", + "description": "Length of index. Maximum of 100", + "x-example": null, + "items": { + "type": "integer" + } } }, "required": [ @@ -17281,6 +17269,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -17696,6 +17685,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -20423,13 +20413,13 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "schema": { "type": "integer", "format": "int32", - "x-example": 0, - "default": 100 + "x-example": -1, + "default": -1 }, "in": "query" }, @@ -27129,6 +27119,15 @@ }, "x-example": [] }, + "lengths": { + "type": "array", + "description": "Index attributes length.", + "items": { + "type": "integer", + "format": "int32" + }, + "x-example": [] + }, "orders": { "type": "array", "description": "Index orders.", @@ -27155,6 +27154,7 @@ "status", "error", "attributes", + "lengths", "$createdAt", "$updatedAt" ] diff --git a/app/config/specs/swagger2-1.6.x-client.json b/app/config/specs/swagger2-1.6.x-client.json index e3b2663cab..6033eb40ea 100644 --- a/app/config/specs/swagger2-1.6.x-client.json +++ b/app/config/specs/swagger2-1.6.x-client.json @@ -4625,26 +4625,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/swagger2-1.6.x-console.json b/app/config/specs/swagger2-1.6.x-console.json index 808ce06f85..5b61ba75d6 100644 --- a/app/config/specs/swagger2-1.6.x-console.json +++ b/app/config/specs/swagger2-1.6.x-console.json @@ -8107,26 +8107,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/swagger2-1.6.x-server.json b/app/config/specs/swagger2-1.6.x-server.json index f3da5cf4f8..697f28c1e4 100644 --- a/app/config/specs/swagger2-1.6.x-server.json +++ b/app/config/specs/swagger2-1.6.x-server.json @@ -7644,26 +7644,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n" } ], "auth": { diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 49b1cb9989..ffd7eaabe6 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3524,12 +3524,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -3655,12 +3655,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -4205,12 +4205,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -4625,26 +4625,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -6557,12 +6537,12 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" }, { diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 873d4fda31..7bbc9391d3 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3545,12 +3545,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -3676,12 +3676,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -4226,12 +4226,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -8173,26 +8173,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -9062,6 +9042,15 @@ "items": { "type": "string" } + }, + "lengths": { + "type": "array", + "description": "Length of index. Maximum of 100", + "default": [], + "x-example": null, + "items": { + "type": "integer" + } } }, "required": [ @@ -25992,6 +25981,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -26638,6 +26628,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -29401,12 +29392,12 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" }, { @@ -37395,6 +37386,15 @@ }, "x-example": [] }, + "lengths": { + "type": "array", + "description": "Index attributes length.", + "items": { + "type": "integer", + "format": "int32" + }, + "x-example": [] + }, "orders": { "type": "array", "description": "Index orders.", @@ -37421,6 +37421,7 @@ "status", "error", "attributes", + "lengths", "$createdAt", "$updatedAt" ] diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index e63988d896..554a8579a6 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3223,12 +3223,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -3356,12 +3356,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -3910,12 +3910,12 @@ }, { "name": "quality", - "description": "Image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" } ] @@ -7644,26 +7644,6 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." - }, - { - "name": "createDocuments", - "parameters": [ - "databaseId", - "collectionId", - "documents" - ], - "required": [ - "databaseId", - "collectionId", - "documents" - ], - "responses": [ - { - "code": 201, - "model": "#\/definitions\/documentList" - } - ], - "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { @@ -8460,6 +8440,15 @@ "items": { "type": "string" } + }, + "lengths": { + "type": "array", + "description": "Length of index. Maximum of 100", + "default": [], + "x-example": null, + "items": { + "type": "integer" + } } }, "required": [ @@ -17579,6 +17568,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -18007,6 +17997,7 @@ "remix", "lynx", "flutter", + "react-native", "vite", "other" ], @@ -20730,12 +20721,12 @@ }, { "name": "quality", - "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to 100.", + "description": "Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.", "required": false, "type": "integer", "format": "int32", - "x-example": 0, - "default": 100, + "x-example": -1, + "default": -1, "in": "query" }, { @@ -27428,6 +27419,15 @@ }, "x-example": [] }, + "lengths": { + "type": "array", + "description": "Index attributes length.", + "items": { + "type": "integer", + "format": "int32" + }, + "x-example": [] + }, "orders": { "type": "array", "description": "Index orders.", @@ -27454,6 +27454,7 @@ "status", "error", "attributes", + "lengths", "$createdAt", "$updatedAt" ] diff --git a/app/config/templates/site.php b/app/config/templates/site.php index 7f8951d0f9..ab6d5c4d4d 100644 --- a/app/config/templates/site.php +++ b/app/config/templates/site.php @@ -106,6 +106,16 @@ const TEMPLATE_FRAMEWORKS = [ 'outputDirectory' => './dist', 'fallbackFile' => 'index.html', ], + 'REACT_NATIVE' => [ + 'key' => 'react-native', + 'name' => 'React Native', + 'installCommand' => 'npm install', + 'buildCommand' => 'npm run build', + 'buildRuntime' => 'node-22', + 'adapter' => 'static', + 'outputDirectory' => './dist', + 'fallbackFile' => '+not-found.html', + ], 'ANGULAR' => [ 'key' => 'angular', 'name' => 'Angular', @@ -866,9 +876,8 @@ return [ 'screenshotDark' => $url . '/images/sites/templates/starter-for-react-native-dark.png', 'screenshotLight' => $url . '/images/sites/templates/starter-for-react-native-light.png', 'frameworks' => [ - getFramework('REACT', [ + getFramework('REACT_NATIVE', [ 'providerRootDirectory' => './', - 'fallbackFile' => '+not-found.html', ]), ], 'vcsProvider' => 'github', @@ -1332,7 +1341,7 @@ return [ 'screenshotDark' => $url . '/images/sites/templates/playground-for-react-native-dark.png', 'screenshotLight' => $url . '/images/sites/templates/playground-for-react-native-light.png', 'frameworks' => [ - getFramework('REACT', [ + getFramework('REACT_NATIVE', [ 'providerRootDirectory' => './react-native/starter', ]), ], diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 769b555d11..779a188089 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -187,7 +187,7 @@ App::get('/v1/avatars/credit-cards/:code') ->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: ' . \implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))) . '.') ->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) - ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) + ->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); @@ -215,7 +215,7 @@ App::get('/v1/avatars/browsers/:code') ->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-browsers'))), 'Browser Code.') ->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) - ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) + ->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); @@ -243,7 +243,7 @@ App::get('/v1/avatars/flags/:code') ->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-flags'))), 'Country Code. ISO Alpha-2 country code format.') ->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) - ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) + ->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 0bdb42ec1c..a72aaf66a3 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2813,11 +2813,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional:true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2877,9 +2878,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'size' => 0 ]; - // lengths hidden by default - $lengths = []; - foreach ($attributes as $i => $attribute) { // find attribute metadata in collection document $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); @@ -2901,8 +2899,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Attribute not available: ' . $oldAttributes[$attributeIndex]['key']); } - $lengths[$i] = null; - + $lengths[$i] ??= null; if ($attributeArray === true) { $lengths[$i] = Database::ARRAY_INDEX_LENGTH; $orders[$i] = null; @@ -3204,25 +3201,25 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') new Parameter('permissions', optional: true), ] ), - new Method( - namespace: 'databases', - group: 'documents', - name: 'createDocuments', - description: '/docs/references/databases/create-documents.md', - auth: [AuthType::KEY], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_DOCUMENT_LIST, - ) - ], - contentType: ContentType::JSON, - parameters: [ - new Parameter('databaseId', optional: false), - new Parameter('collectionId', optional: false), - new Parameter('documents', optional: false), - ] - ) + // new Method( + // namespace: 'databases', + // group: 'documents', + // name: 'createDocuments', + // description: '/docs/references/databases/create-documents.md', + // auth: [AuthType::KEY], + // responses: [ + // new SDKResponse( + // code: Response::STATUS_CODE_CREATED, + // model: Response::MODEL_DOCUMENT_LIST, + // ) + // ], + // contentType: ContentType::JSON, + // parameters: [ + // new Parameter('databaseId', optional: false), + // new Parameter('collectionId', optional: false), + // new Parameter('documents', optional: false), + // ] + // ) ] ) ->param('databaseId', '', new UID(), 'Database ID.') @@ -4742,8 +4739,8 @@ App::get('/v1/databases/:databaseId/usage') str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_READS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASES_OPERATIONS_WRITES) + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES) ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index b6e89a1591..ee6279c3bc 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -943,7 +943,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) ->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true) - ->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true) + ->param('quality', -1, new Range(-1, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) ->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true) ->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true) ->param('borderRadius', 0, new Range(0, 4000), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.', true) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 2c3988a528..49d9005c54 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1090,7 +1090,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') // Quick check: fetch up to 2 owners to determine if only one exists $ownersCount = $dbForProject->count( collection: 'memberships', - queries: [Query::contains('roles', ['owner'])], + queries: [ + Query::contains('roles', ['owner']), + Query::equal('teamInternalId', [$team->getInternalId()]) + ], max: 2 ); diff --git a/app/init/resources.php b/app/init/resources.php index aa82f26b71..fdbb426458 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -509,29 +509,26 @@ App::setResource('timelimit', function (\Redis $redis) { }; }, ['redis']); -App::setResource('deviceForLocal', function () { - return new Local(); -}); +App::setResource('deviceForLocal', function (Telemetry $telemetry) { + return new Device\Telemetry($telemetry, new Local()); +}, ['telemetry']); -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); +App::setResource('deviceForFiles', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForSites', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForImports', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -App::setResource('deviceForSites', function ($project) { - return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForImports', function (Document $project) { - return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); +App::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); function getDevice(string $root, string $connection = ''): Device { diff --git a/app/worker.php b/app/worker.php index 00d5251bb3..ad0e92bf6a 100644 --- a/app/worker.php +++ b/app/worker.php @@ -37,7 +37,10 @@ use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; use Utopia\Registry\Registry; +use Utopia\Storage\Device; use Utopia\System\System; +use Utopia\Telemetry\Adapter as Telemetry; +use Utopia\Telemetry\Adapter\None as NoTelemetry; Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); @@ -334,29 +337,31 @@ Server::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); -Server::setResource('deviceForSites', function (Document $project) { - return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('telemetry', fn () => new NoTelemetry()); -Server::setResource('deviceForImports', function (Document $project) { - return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForSites', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForFunctions', function (Document $project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForImports', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForFiles', function (Document $project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForFunctions', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForBuilds', function (Document $project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForFiles', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForCache', function (Document $project) { - return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForBuilds', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); + +Server::setResource('deviceForCache', function (Document $project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId())); +}, ['project', 'telemetry']); Server::setResource( 'isResourceBlocked', diff --git a/composer.json b/composer.json index 21ce161c64..d9109f40f8 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/detector": "0.1.*", - "utopia-php/database": "0.68.*", + "utopia-php/database": "0.69.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", @@ -61,7 +61,7 @@ "utopia-php/image": "0.8.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.6.*", - "utopia-php/messaging": "0.16.*", + "utopia-php/messaging": "0.17.*", "utopia-php/migration": "0.9.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", diff --git a/composer.lock b/composer.lock index e8d3fb11e0..c68d6bf4d7 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "18d31817580d569f001c88d865dcbfc6", + "content-hash": "735023e2b70ce47fe65985f195b257bd", "packages": [ { "name": "adhocore/jwt", @@ -709,16 +709,16 @@ }, { "name": "google/protobuf", - "version": "v4.30.2", + "version": "v4.31.0", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced" + "reference": "d59e31ce4bf0e4b48728e90c4d880839edb5be07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/a4c4d8565b40b9f76debc9dfeb221412eacb8ced", - "reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/d59e31ce4bf0e4b48728e90c4d880839edb5be07", + "reference": "d59e31ce4bf0e4b48728e90c4d880839edb5be07", "shasum": "" }, "require": { @@ -747,9 +747,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.2" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.31.0" }, - "time": "2025-03-26T18:01:50+00:00" + "time": "2025-05-14T16:17:23+00:00" }, { "name": "league/csv", @@ -3499,16 +3499,16 @@ }, { "name": "utopia-php/database", - "version": "0.68.1", + "version": "0.69.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "72b2e2c0b875028f7d9dd755f6d4524b693c6507" + "reference": "60591ab073bb80bb9843338754b679bb8169e4ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/72b2e2c0b875028f7d9dd755f6d4524b693c6507", - "reference": "72b2e2c0b875028f7d9dd755f6d4524b693c6507", + "url": "https://api.github.com/repos/utopia-php/database/zipball/60591ab073bb80bb9843338754b679bb8169e4ed", + "reference": "60591ab073bb80bb9843338754b679bb8169e4ed", "shasum": "" }, "require": { @@ -3549,9 +3549,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.68.1" + "source": "https://github.com/utopia-php/database/tree/0.69.2" }, - "time": "2025-05-09T10:08:53+00:00" + "time": "2025-05-14T07:51:44+00:00" }, { "name": "utopia-php/detector", @@ -3793,16 +3793,16 @@ }, { "name": "utopia-php/image", - "version": "0.8.2", + "version": "0.8.3", "source": { "type": "git", "url": "https://github.com/utopia-php/image.git", - "reference": "6c736965177f9a9e71311e22b80cfa88511768e9" + "reference": "8820b0e53b3636b7bdf815e92394d333fef06f26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/6c736965177f9a9e71311e22b80cfa88511768e9", - "reference": "6c736965177f9a9e71311e22b80cfa88511768e9", + "url": "https://api.github.com/repos/utopia-php/image/zipball/8820b0e53b3636b7bdf815e92394d333fef06f26", + "reference": "8820b0e53b3636b7bdf815e92394d333fef06f26", "shasum": "" }, "require": { @@ -3836,9 +3836,9 @@ ], "support": { "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.8.2" + "source": "https://github.com/utopia-php/image/tree/0.8.3" }, - "time": "2025-04-08T11:31:45+00:00" + "time": "2025-05-15T10:39:28+00:00" }, { "name": "utopia-php/locale", @@ -3947,16 +3947,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.16.0", + "version": "0.17.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "5f3083697102b1821d6624938186761b1e09c54e" + "reference": "c51915d0e030db3a3add37f1561751d18b2d9a85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/5f3083697102b1821d6624938186761b1e09c54e", - "reference": "5f3083697102b1821d6624938186761b1e09c54e", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/c51915d0e030db3a3add37f1561751d18b2d9a85", + "reference": "c51915d0e030db3a3add37f1561751d18b2d9a85", "shasum": "" }, "require": { @@ -3992,9 +3992,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.16.0" + "source": "https://github.com/utopia-php/messaging/tree/0.17.0" }, - "time": "2025-02-18T08:27:00+00:00" + "time": "2025-05-12T16:14:08+00:00" }, { "name": "utopia-php/migration", @@ -4376,16 +4376,16 @@ }, { "name": "utopia-php/storage", - "version": "0.18.10", + "version": "0.18.12", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e" + "reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", - "reference": "76f31158f4251abb207f7a9b16f7cb0bfdb3b39e", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97", + "reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97", "shasum": "" }, "require": { @@ -4398,9 +4398,10 @@ "ext-xz": "*", "ext-zlib": "*", "ext-zstd": "*", - "php": ">=8.0", + "php": ">=8.1", "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" + "utopia-php/system": "0.*.*", + "utopia-php/telemetry": "0.1.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -4427,9 +4428,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.10" + "source": "https://github.com/utopia-php/storage/tree/0.18.12" }, - "time": "2025-03-03T10:47:54+00:00" + "time": "2025-05-15T07:55:58+00:00" }, { "name": "utopia-php/swoole", @@ -8239,7 +8240,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8263,5 +8264,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index 21fbe5456c..f720e11ac6 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1 +1,6 @@ -# Change log \ No newline at end of file +# Change log + +## 0.7.4 + +* Upgrade dependencies to resolve PlatformConstants error with Expo 53 +* Update doc examples to use new multi-region endpoint \ No newline at end of file diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index b8ceca31f0..3cf89f9f44 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Tasks; -use Swoole\Runtime; use Swoole\Timer; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -66,8 +65,6 @@ abstract class ScheduleBase extends Action */ public function action(Group $pools, Database $dbForPlatform, callable $getProjectDB, Telemetry $telemetry): void { - Runtime::enableCoroutine(); - Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); @@ -91,13 +88,18 @@ abstract class ScheduleBase extends Action }); while (true) { - $this->enqueueResources($pools, $dbForPlatform, $getProjectDB); - $this->scheduleTelemetryCount->record(count($this->schedules), ['resourceType' => static::getSupportedResource()]); - sleep(static::ENQUEUE_TIMER); + try { + go(fn () => $this->enqueueResources($pools, $dbForPlatform, $getProjectDB)); + $this->scheduleTelemetryCount->record(count($this->schedules), ['resourceType' => static::getSupportedResource()]); + sleep(static::ENQUEUE_TIMER); + } catch (\Throwable $th) { + Console::error('Failed to enqueue resources: ' . $th->getMessage()); + } + } } - private function collectSchedules(Group $pools, Database $dbForPlatform, callable $getProjectDB, ?string &$lastSyncUpdate): void + private function collectSchedules(Group $pools, Database $dbForPlatform, callable $getProjectDB, string &$lastSyncUpdate): void { // If we haven't synced yet, load all active schedules $initialLoad = $lastSyncUpdate === "0"; @@ -202,10 +204,8 @@ abstract class ScheduleBase extends Action Console::success("{$total} resources were loaded in " . $duration . " seconds"); } - protected function recordEnqueueDelay(string $expectedExecutionSchedule): void + protected function recordEnqueueDelay(\DateTime $expectedExecutionSchedule): void { - $now = strtotime('now'); - $scheduledAt = strtotime($expectedExecutionSchedule); - $this->enqueueDelayTelemetry->record($now - $scheduledAt, ['resourceType' => static::getSupportedResource()]); + $this->enqueueDelayTelemetry->record(time() - $expectedExecutionSchedule->getTimestamp(), ['resourceType' => static::getSupportedResource()]); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index ae809fe51e..2a8fd99b7f 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -59,7 +59,7 @@ class ScheduleExecutions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - \go(function () use ($queueForFunctions, $schedule, $delay, $data) { + \go(function () use ($queueForFunctions, $schedule, $scheduledAt, $delay, $data) { Co::sleep($delay); $queueForFunctions->setType('schedule') @@ -75,7 +75,7 @@ class ScheduleExecutions extends ScheduleBase ->setUserId($data['userId'] ?? '') ->trigger(); - $this->recordEnqueueDelay($schedule['schedule']); + $this->recordEnqueueDelay($scheduledAt); }); $dbForPlatform->deleteDocument( diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index abcfe132e3..6788748f3d 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -46,7 +46,13 @@ class ScheduleFunctions extends ScheduleBase $delayedExecutions = []; // Group executions with same delay to share one coroutine foreach ($this->schedules as $key => $schedule) { - $cron = new CronExpression($schedule['schedule']); + try { + $cron = new CronExpression($schedule['schedule']); + } catch (\InvalidArgumentException) { + // ignore invalid cron expressions + continue; + } + $nextDate = $cron->getNextRunDate(); $next = DateTime::format($nextDate); @@ -66,17 +72,18 @@ class ScheduleFunctions extends ScheduleBase $delayedExecutions[$delay] = []; } - $delayedExecutions[$delay][] = $key; + $delayedExecutions[$delay][] = ['key' => $key, 'nextDate' => $nextDate]; } - foreach ($delayedExecutions as $delay => $scheduleKeys) { - \go(function () use ($delay, $scheduleKeys, $pools, $dbForPlatform) { + foreach ($delayedExecutions as $delay => $schedules) { + \go(function () use ($delay, $schedules, $pools, $dbForPlatform) { \sleep($delay); // in seconds $queue = $pools->get('publisher')->pop(); $connection = $queue->getResource(); - foreach ($scheduleKeys as $scheduleKey) { + foreach ($schedules as $delayConfig) { + $scheduleKey = $delayConfig['key']; // Ensure schedule was not deleted if (!\array_key_exists($scheduleKey, $this->schedules)) { return; @@ -96,7 +103,7 @@ class ScheduleFunctions extends ScheduleBase ->setProject($schedule['project']) ->trigger(); - $this->recordEnqueueDelay($schedule['schedule']); + $this->recordEnqueueDelay($delayConfig['nextDate']); } $queue->reclaim(); diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 9b962c99ee..a15df6ed5b 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -40,7 +40,7 @@ class ScheduleMessages extends ScheduleBase continue; } - \go(function () use ($schedule, $pools, $dbForPlatform) { + \go(function () use ($schedule, $scheduledAt, $pools, $dbForPlatform) { $queue = $pools->get('publisher')->pop(); $connection = $queue->getResource(); $queueForMessaging = new Messaging($connection); @@ -59,7 +59,7 @@ class ScheduleMessages extends ScheduleBase ); $queue->reclaim(); - $this->recordEnqueueDelay($schedule['schedule']); + $this->recordEnqueueDelay($scheduledAt); unset($this->schedules[$schedule['$internalId']]); }); } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 6f94764bbf..8491e91a6c 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -24,6 +24,7 @@ use Utopia\Messaging\Adapter\Push\FCM; use Utopia\Messaging\Adapter\SMS as SMSAdapter; use Utopia\Messaging\Adapter\SMS\Fast2SMS; use Utopia\Messaging\Adapter\SMS\GEOSMS; +use Utopia\Messaging\Adapter\SMS\Inforu; use Utopia\Messaging\Adapter\SMS\Mock; use Utopia\Messaging\Adapter\SMS\Msg91; use Utopia\Messaging\Adapter\SMS\Telesign; @@ -455,6 +456,10 @@ class Messaging extends Action $credentials['messageId'] ?? '', $credentials['useDLT'] ?? true ), + 'inforu' => new Inforu( + $credentials['senderId'] ?? '', + $credentials['apiKey'] ?? '', + ), default => null }; } @@ -780,6 +785,10 @@ class Messaging extends Action 'messageId' => $dsn->getParam('messageId'), 'useDLT' => $dsn->getParam('useDLT'), ], + 'inforu' => [ + 'senderId' => $user, + 'apiKey' => $password, + ], default => null }, 'options' => match ($host) { diff --git a/src/Appwrite/Utopia/Response/Model/Index.php b/src/Appwrite/Utopia/Response/Model/Index.php index 2d795ad439..fcd978b5be 100644 --- a/src/Appwrite/Utopia/Response/Model/Index.php +++ b/src/Appwrite/Utopia/Response/Model/Index.php @@ -41,6 +41,13 @@ class Index extends Model 'example' => [], 'array' => true, ]) + ->addRule('lengths', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Index attributes length.', + 'default' => [], + 'example' => [], + 'array' => true, + ]) ->addRule('orders', [ 'type' => self::TYPE_STRING, 'description' => 'Index orders.', diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 1f19c514d1..2b60dee856 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1422,9 +1422,102 @@ trait DatabasesBase return $data; } + /** - * @depends testCreateIndexes - */ + * @depends testCreateAttributes + */ + public function testGetIndexByKeyWithLengths(array $data): void + { + $databaseId = $data['databaseId']; + $collectionId = $data['moviesId']; + + // Test case for valid lengths + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthTestIndex', + 'type' => 'key', + 'attributes' => ['title','description'], + 'lengths' => [128,200] + ]); + $this->assertEquals(202, $create['headers']['status-code']); + + // Fetch index and check correct lengths + $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthTestIndex", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + $this->assertEquals(200, $index['headers']['status-code']); + $this->assertEquals('lengthTestIndex', $index['body']['key']); + $this->assertEquals([128, 200], $index['body']['lengths']); + + // Test case for lengths array overriding + // set a length for an array attribute, it should get overriden with Database::ARRAY_INDEX_LENGTH + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthOverrideTestIndex', + 'type' => 'key', + 'attributes' => ['actors'], + 'lengths' => [120] + ]); + $this->assertEquals(202, $create['headers']['status-code']); + + $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthOverrideTestIndex", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + $this->assertEquals([Database::ARRAY_INDEX_LENGTH], $index['body']['lengths']); + + // Test case for count of lengths greater than attributes (should throw 400) + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthCountExceededIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [128, 128] + ]); + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for lengths exceeding total of 768 + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthTooLargeIndex', + 'type' => 'key', + 'attributes' => ['title','description','tagline','actors'], + 'lengths' => [256,256,256,20] + ]); + + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for negative length values + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'negativeLengthIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [-1] + ]); + $this->assertEquals(400, $create['headers']['status-code']); + } + /** + * @depends testCreateIndexes + */ public function testListIndexes(array $data): void { $databaseId = $data['databaseId']; diff --git a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php index dec61d5258..f096831a76 100644 --- a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php +++ b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php @@ -110,7 +110,7 @@ class TeamsConsoleClientTest extends Scope $session = $data['session'] ?? ''; /** - * Test for SUCCESS + * Test for FAILURE */ $roles = ['developer']; $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([ @@ -121,12 +121,8 @@ class TeamsConsoleClientTest extends Scope 'roles' => $roles ]); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertNotEmpty($response['body']['userId']); - $this->assertNotEmpty($response['body']['teamId']); - $this->assertCount(count($roles), $response['body']['roles']); - $this->assertEquals($roles[0], $response['body']['roles'][0]); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('There must be at least one owner in the organization.', $response['body']['message']); /** * Test for unknown team