Merge branch '1.8.x' into 'lazy-load-relationships'.

This commit is contained in:
Darshan
2025-06-11 10:08:51 +05:30
24 changed files with 173169 additions and 1218 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+278 -38
View File
@@ -4457,6 +4457,7 @@
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"client",
"server",
"server"
@@ -4466,6 +4467,7 @@
{
"name": "createDocument",
"auth": {
"Admin": [],
"Session": [],
"Key": [],
"JWT": []
@@ -4890,7 +4892,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 117,
"weight": 119,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4951,6 +4953,236 @@
]
}
},
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": {
"patch": {
"summary": "Decrement document attribute",
"operationId": "databasesDecrementDocumentAttribute",
"tags": [
"databases"
],
"description": "Decrement a specific attribute of a document by a given value.",
"responses": {
"200": {
"description": "Document",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/document"
}
}
}
}
},
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 116,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "databases\/decrement-document-attribute.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md",
"rate-limit": 120,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"server",
"client",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "databaseId",
"description": "Database ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<DATABASE_ID>"
},
"in": "path"
},
{
"name": "collectionId",
"description": "Collection ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<COLLECTION_ID>"
},
"in": "path"
},
{
"name": "documentId",
"description": "Document ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<DOCUMENT_ID>"
},
"in": "path"
},
{
"name": "attribute",
"description": "Attribute key.",
"required": true,
"schema": {
"type": "string"
},
"in": "path"
}
],
"requestBody": {
"content": {
"application\/json": {
"schema": {
"type": "object",
"properties": {
"value": {
"type": "number",
"description": "Value to decrement the attribute by. The value must be a number.",
"x-example": null
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"x-example": null
}
}
}
}
}
}
}
},
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": {
"patch": {
"summary": "Increment document attribute",
"operationId": "databasesIncrementDocumentAttribute",
"tags": [
"databases"
],
"description": "Increment a specific attribute of a document by a given value.",
"responses": {
"200": {
"description": "Document",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/document"
}
}
}
}
},
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 115,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "databases\/increment-document-attribute.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md",
"rate-limit": 120,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"server",
"client",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "databaseId",
"description": "Database ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<DATABASE_ID>"
},
"in": "path"
},
{
"name": "collectionId",
"description": "Collection ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<COLLECTION_ID>"
},
"in": "path"
},
{
"name": "documentId",
"description": "Document ID.",
"required": true,
"schema": {
"type": "string",
"x-example": "<DOCUMENT_ID>"
},
"in": "path"
},
{
"name": "attribute",
"description": "Attribute key.",
"required": true,
"schema": {
"type": "string"
},
"in": "path"
}
],
"requestBody": {
"content": {
"application\/json": {
"schema": {
"type": "object",
"properties": {
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"x-example": null
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"x-example": null
}
}
}
}
}
}
}
},
"\/functions\/{functionId}\/executions": {
"get": {
"summary": "List executions",
@@ -4974,7 +5206,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 392,
"weight": 394,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5049,7 +5281,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 390,
"weight": 392,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5164,7 +5396,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 391,
"weight": 393,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5238,7 +5470,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 306,
"weight": 308,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5290,7 +5522,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 305,
"weight": 307,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5342,7 +5574,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 122,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5394,7 +5626,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 123,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5446,7 +5678,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 127,
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5498,7 +5730,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 124,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5550,7 +5782,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 125,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5602,7 +5834,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 126,
"weight": 128,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5654,7 +5886,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 128,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5706,7 +5938,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 129,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5758,7 +5990,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 352,
"weight": 354,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5841,7 +6073,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 356,
"weight": 358,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5916,7 +6148,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 212,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6002,7 +6234,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 211,
"weight": 213,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6100,7 +6332,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 213,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6172,7 +6404,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 218,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6261,7 +6493,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 219,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6328,7 +6560,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 215,
"weight": 217,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6406,7 +6638,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 214,
"weight": 216,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6595,7 +6827,8 @@
"png",
"webp",
"heic",
"avif"
"avif",
"gif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -6633,7 +6866,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 216,
"weight": 218,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6718,7 +6951,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 223,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6794,7 +7027,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 222,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6879,7 +7112,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 224,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6941,7 +7174,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 226,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7015,7 +7248,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 228,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7079,7 +7312,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 230,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7165,7 +7398,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 229,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7276,7 +7509,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 231,
"weight": 233,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7348,7 +7581,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 232,
"weight": 234,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7435,7 +7668,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 234,
"weight": 236,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7509,7 +7742,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 233,
"weight": 235,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7607,7 +7840,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 225,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7668,7 +7901,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 227,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -8182,6 +8415,12 @@
"description": "Document ID.",
"x-example": "5e5ea5c16897e"
},
"$sequence": {
"type": "integer",
"description": "Document automatically incrementing ID.",
"x-example": 1,
"format": "int32"
},
"$collectionId": {
"type": "string",
"description": "Collection ID.",
@@ -8216,6 +8455,7 @@
"additionalProperties": true,
"required": [
"$id",
"$sequence",
"$collectionId",
"$databaseId",
"$createdAt",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+266 -38
View File
@@ -4603,6 +4603,7 @@
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"client",
"server",
"server"
@@ -4612,6 +4613,7 @@
{
"name": "createDocument",
"auth": {
"Admin": [],
"Session": [],
"Key": [],
"JWT": []
@@ -5023,7 +5025,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 117,
"weight": 119,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5078,6 +5080,224 @@
]
}
},
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": {
"patch": {
"summary": "Decrement document attribute",
"operationId": "databasesDecrementDocumentAttribute",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"databases"
],
"description": "Decrement a specific attribute of a document by a given value.",
"responses": {
"200": {
"description": "Document",
"schema": {
"$ref": "#\/definitions\/document"
}
}
},
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 116,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "databases\/decrement-document-attribute.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md",
"rate-limit": 120,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"server",
"client",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "databaseId",
"description": "Database ID.",
"required": true,
"type": "string",
"x-example": "<DATABASE_ID>",
"in": "path"
},
{
"name": "collectionId",
"description": "Collection ID.",
"required": true,
"type": "string",
"x-example": "<COLLECTION_ID>",
"in": "path"
},
{
"name": "documentId",
"description": "Document ID.",
"required": true,
"type": "string",
"x-example": "<DOCUMENT_ID>",
"in": "path"
},
{
"name": "attribute",
"description": "Attribute key.",
"required": true,
"type": "string",
"in": "path"
},
{
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"value": {
"type": "number",
"description": "Value to decrement the attribute by. The value must be a number.",
"default": 1,
"x-example": null
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"default": null,
"x-example": null
}
}
}
}
]
}
},
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": {
"patch": {
"summary": "Increment document attribute",
"operationId": "databasesIncrementDocumentAttribute",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"databases"
],
"description": "Increment a specific attribute of a document by a given value.",
"responses": {
"200": {
"description": "Document",
"schema": {
"$ref": "#\/definitions\/document"
}
}
},
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 115,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "databases\/increment-document-attribute.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md",
"rate-limit": 120,
"rate-time": 60,
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
"scope": "documents.write",
"platforms": [
"console",
"server",
"client",
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "databaseId",
"description": "Database ID.",
"required": true,
"type": "string",
"x-example": "<DATABASE_ID>",
"in": "path"
},
{
"name": "collectionId",
"description": "Collection ID.",
"required": true,
"type": "string",
"x-example": "<COLLECTION_ID>",
"in": "path"
},
{
"name": "documentId",
"description": "Document ID.",
"required": true,
"type": "string",
"x-example": "<DOCUMENT_ID>",
"in": "path"
},
{
"name": "attribute",
"description": "Attribute key.",
"required": true,
"type": "string",
"in": "path"
},
{
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"default": 1,
"x-example": null
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"default": null,
"x-example": null
}
}
}
}
]
}
},
"\/functions\/{functionId}\/executions": {
"get": {
"summary": "List executions",
@@ -5101,7 +5321,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 392,
"weight": 394,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5175,7 +5395,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 390,
"weight": 392,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5292,7 +5512,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 391,
"weight": 393,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5364,7 +5584,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 306,
"weight": 308,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5438,7 +5658,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 305,
"weight": 307,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5510,7 +5730,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 122,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5562,7 +5782,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 123,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5614,7 +5834,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 127,
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5666,7 +5886,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 124,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5718,7 +5938,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 125,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5770,7 +5990,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 126,
"weight": 128,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5822,7 +6042,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 128,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5874,7 +6094,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 129,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5928,7 +6148,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 352,
"weight": 354,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6013,7 +6233,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 356,
"weight": 358,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6084,7 +6304,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 212,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6167,7 +6387,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 211,
"weight": 213,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6257,7 +6477,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 213,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6327,7 +6547,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 218,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6416,7 +6636,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 219,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6486,7 +6706,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 215,
"weight": 217,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6565,7 +6785,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 214,
"weight": 216,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6729,7 +6949,8 @@
"png",
"webp",
"heic",
"avif"
"avif",
"gif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -6771,7 +6992,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 216,
"weight": 218,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6850,7 +7071,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 223,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6925,7 +7146,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 222,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7015,7 +7236,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 224,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7077,7 +7298,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 226,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7152,7 +7373,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 228,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7214,7 +7435,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 230,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7297,7 +7518,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 229,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7410,7 +7631,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 231,
"weight": 233,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7480,7 +7701,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 232,
"weight": 234,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7566,7 +7787,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 234,
"weight": 236,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7638,7 +7859,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 233,
"weight": 235,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7732,7 +7953,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 225,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7793,7 +8014,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 227,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -8285,6 +8506,12 @@
"description": "Document ID.",
"x-example": "5e5ea5c16897e"
},
"$sequence": {
"type": "integer",
"description": "Document automatically incrementing ID.",
"x-example": 1,
"format": "int32"
},
"$collectionId": {
"type": "string",
"description": "Collection ID.",
@@ -8319,6 +8546,7 @@
"additionalProperties": true,
"required": [
"$id",
"$sequence",
"$collectionId",
"$databaseId",
"$createdAt",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+163 -8
View File
@@ -38,6 +38,7 @@ use Utopia\Database\Exception\Relationship as RelationshipException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Exception\Type as TypeException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -62,6 +63,7 @@ use Utopia\Validator\Integer;
use Utopia\Validator\IP;
use Utopia\Validator\JSON;
use Utopia\Validator\Nullable;
use Utopia\Validator\Numeric;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
@@ -839,7 +841,7 @@ App::delete('/v1/databases/:databaseId')
}
$dbForProject->purgeCachedDocument('databases', $database->getId());
$dbForProject->purgeCachedCollection('databases_' . $database->getSequence());
$dbForProject->purgeCachedCollection('database_' . $database->getSequence());
$queueForDatabase
->setType(DATABASE_TYPE_DELETE_DATABASE)
@@ -2847,7 +2849,7 @@ 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)
->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')
@@ -3126,7 +3128,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
$response->dynamic($index, Response::MODEL_INDEX);
});
App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->alias('/v1/database/collections/:collectionId/indexes/:key')
->desc('Delete index')
@@ -3220,7 +3221,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
group: 'documents',
name: 'createDocument',
description: '/docs/references/databases/create-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -3241,7 +3242,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
group: 'documents',
name: 'createDocuments',
description: '/docs/references/databases/create-documents.md',
auth: [AuthType::KEY],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -4490,6 +4491,160 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/increment')
->desc('Increment document attribute')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].increment')
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'documents.increment')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'incrementDocumentAttribute',
description: '/docs/references/databases/increment-document-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY, AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('documentId', '', new UID(), 'Document ID.')
->param('attribute', '', new Key(), 'Attribute key.')
->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true)
->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
try {
$document = $dbForProject->increaseDocumentAttribute(
collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(),
id: $documentId,
attribute: $attribute,
value: $value,
max: $max
);
} catch (ConflictException) {
throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT);
} catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the maximum value of ' . $max);
} catch (TypeException) {
throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number');
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1);
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collectionId)
->setContext('collection', $collection)
->setContext('database', $database);
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/decrement')
->desc('Decrement document attribute')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].decrement')
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'documents.decrement')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'decrementDocumentAttribute',
description: '/docs/references/databases/decrement-document-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY, AuthType::SESSION, AuthType::JWT],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('documentId', '', new UID(), 'Document ID.')
->param('attribute', '', new Key(), 'Attribute key.')
->param('value', 1, new Numeric(), 'Value to decrement the attribute by. The value must be a number.', true)
->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
try {
$document = $dbForProject->decreaseDocumentAttribute(
collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(),
id: $documentId,
attribute: $attribute,
value: $value,
min: $min
);
} catch (ConflictException) {
throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT);
} catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the minimum value of ' . $min);
} catch (TypeException) {
throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number');
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1);
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collectionId)
->setContext('collection', $collection)
->setContext('database', $database);
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
App::patch('/v1/databases/:databaseId/collections/:collectionId/documents')
->desc('Update documents')
->groups(['api', 'database'])
@@ -4505,7 +4660,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents')
group: 'documents',
name: 'updateDocuments',
description: '/docs/references/databases/update-documents.md',
auth: [AuthType::KEY],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -4615,7 +4770,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents')
group: 'documents',
name: 'upsertDocuments',
description: '/docs/references/databases/upsert-documents.md',
auth: [AuthType::KEY],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -4834,7 +4989,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents')
group: 'documents',
name: 'deleteDocuments',
description: '/docs/references/databases/delete-documents.md',
auth: [AuthType::KEY],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
+39 -39
View File
@@ -316,15 +316,15 @@ App::put('/v1/storage/buckets/:bucketId')
$permissions = Permission::aggregate($permissions);
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
->setAttribute('name', $name)
->setAttribute('$permissions', $permissions)
->setAttribute('maximumFileSize', $maximumFileSize)
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
->setAttribute('fileSecurity', $fileSecurity)
->setAttribute('enabled', $enabled)
->setAttribute('encryption', $encryption)
->setAttribute('compression', $compression)
->setAttribute('antivirus', $antivirus));
->setAttribute('name', $name)
->setAttribute('$permissions', $permissions)
->setAttribute('maximumFileSize', $maximumFileSize)
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
->setAttribute('fileSecurity', $fileSecurity)
->setAttribute('enabled', $enabled)
->setAttribute('encryption', $encryption)
->setAttribute('compression', $compression)
->setAttribute('antivirus', $antivirus));
$dbForProject->updateCollection('bucket_' . $bucket->getSequence(), $permissions, $fileSecurity);
@@ -1004,7 +1004,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$algorithm = $file->getAttribute('algorithm', Compression::NONE);
$cipher = $file->getAttribute('openSSLCipher');
$mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', APP_STORAGE_READ_BUFFER)) {
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
} else {
@@ -1178,13 +1178,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
}
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
;
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
@@ -1193,7 +1186,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
if ($end === null) {
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
$end = min(($start + MAX_OUTPUT_CHUNK_SIZE - 1), ($size - 1));
}
@@ -1208,6 +1201,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
}
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
;
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceForFiles->read($path);
@@ -1345,15 +1345,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$contentType = $file->getAttribute('mimeType');
}
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
;
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
@@ -1362,8 +1353,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
if ($end === null) {
$end = min(($start + 2000000 - 1), ($size - 1));
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
$end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1));
}
if ($unit != 'bytes' || $start >= $end || $end >= $size) {
@@ -1377,6 +1368,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
}
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
;
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceForFiles->read($path);
@@ -1498,14 +1498,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
$contentType = $file->getAttribute('mimeType');
}
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage());
$size = $file->getAttribute('sizeOriginal', 0);
$rangeHeader = $request->getHeader('range');
@@ -1514,8 +1506,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
$end = $request->getRangeEnd();
$unit = $request->getRangeUnit();
if ($end === null) {
$end = min(($start + 2000000 - 1), ($size - 1));
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
$end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1));
}
if ($unit != 'bytes' || $start >= $end || $end >= $size) {
@@ -1529,6 +1521,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
}
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage());
$source = '';
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
$source = $deviceForFiles->read($path);
Generated
+18 -18
View File
@@ -3490,16 +3490,16 @@
},
{
"name": "utopia-php/database",
"version": "0.71.3",
"version": "0.71.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "f0c28b78548e2b740d940ca17dca30e1e532d53c"
"reference": "308cbeb65780f954f9f3abfff2ef17c5941ae00e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f0c28b78548e2b740d940ca17dca30e1e532d53c",
"reference": "f0c28b78548e2b740d940ca17dca30e1e532d53c",
"url": "https://api.github.com/repos/utopia-php/database/zipball/308cbeb65780f954f9f3abfff2ef17c5941ae00e",
"reference": "308cbeb65780f954f9f3abfff2ef17c5941ae00e",
"shasum": ""
},
"require": {
@@ -3540,9 +3540,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.71.3"
"source": "https://github.com/utopia-php/database/tree/0.71.4"
},
"time": "2025-06-10T03:53:35+00:00"
"time": "2025-06-10T15:47:50+00:00"
},
{
"name": "utopia-php/detector",
@@ -4584,16 +4584,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.10.4",
"version": "0.10.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "f635b368909eb3c3fe57344fe43525e74e8fdc03"
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/f635b368909eb3c3fe57344fe43525e74e8fdc03",
"reference": "f635b368909eb3c3fe57344fe43525e74e8fdc03",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/b358439dc387f6097019eb83ebb9fc258fe9da05",
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05",
"shasum": ""
},
"require": {
@@ -4627,9 +4627,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.10.4"
"source": "https://github.com/utopia-php/vcs/tree/0.10.5"
},
"time": "2025-06-02T09:18:36+00:00"
"time": "2025-06-10T15:01:16+00:00"
},
{
"name": "utopia-php/websocket",
@@ -4807,16 +4807,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.41.2",
"version": "0.41.4",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "e9a324efef9080808e07a782be2420cd4454cff7"
"reference": "07804269131f411576aac60c795a5ebc3afaa48a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e9a324efef9080808e07a782be2420cd4454cff7",
"reference": "e9a324efef9080808e07a782be2420cd4454cff7",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07804269131f411576aac60c795a5ebc3afaa48a",
"reference": "07804269131f411576aac60c795a5ebc3afaa48a",
"shasum": ""
},
"require": {
@@ -4852,9 +4852,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/0.41.2"
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.4"
},
"time": "2025-06-10T03:08:44+00:00"
"time": "2025-06-10T08:28:11+00:00"
},
{
"name": "doctrine/annotations",
@@ -0,0 +1 @@
Decrement a specific attribute of a document by a given value.
@@ -0,0 +1 @@
Increment a specific attribute of a document by a given value.
+16 -8
View File
@@ -4,9 +4,11 @@ namespace Appwrite\Platform\Tasks;
use Appwrite\Event\Certificate;
use Appwrite\Event\Delete;
use DateInterval;
use DateTime;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\DateTime as DatabaseDateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Platform\Action;
@@ -58,29 +60,35 @@ class Maintenance extends Action
Console::info('Setting loop start time to ' . $next->format("Y-m-d H:i:s.v") . '. Delaying for ' . $delay . ' seconds.');
Console::loop(function () use ($interval, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForPlatform, $console, $queueForDeletes, $queueForCertificates) {
$time = DateTime::now();
$time = DatabaseDateTime::now();
Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds");
// Iterate through project only if it was accessed in last 24 hours
$dateInterval = DateInterval::createFromDateString('24 hours');
$before24h = (new DateTime())->sub($dateInterval);
$dbForPlatform->foreach(
'projects',
function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) {
$queueForDeletes
->setType(DELETE_TYPE_MAINTENANCE)
->setProject($project)
->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly))
->setUsageRetentionHourlyDateTime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly))
->trigger();
},
[
Query::equal('region', [System::getEnv('_APP_REGION', 'default')]),
Query::limit(100),
Query::greaterThanEqual('accessedAt', DatabaseDateTime::format($before24h)),
Query::orderAsc('teamInternalId'),
]
);
$queueForDeletes
->setType(DELETE_TYPE_MAINTENANCE)
->setProject($console)
->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly))
->setUsageRetentionHourlyDateTime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly))
->trigger();
$this->notifyDeleteConnections($queueForDeletes);
@@ -94,13 +102,13 @@ class Maintenance extends Action
{
$queueForDeletes
->setType(DELETE_TYPE_REALTIME)
->setDatetime(DateTime::addSeconds(new \DateTime(), -60))
->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -60))
->trigger();
}
private function renewCertificates(Database $dbForPlatform, Certificate $queueForCertificate): void
{
$time = DateTime::now();
$time = DatabaseDateTime::now();
$certificates = $dbForPlatform->find('certificates', [
Query::lessThan('attempts', 5), // Maximum 5 attempts
@@ -129,7 +137,7 @@ class Maintenance extends Action
{
$queueForDeletes
->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP)
->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval))
->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $interval))
->trigger();
}
@@ -137,7 +145,7 @@ class Maintenance extends Action
{
$queueForDeletes
->setType(DELETE_TYPE_SCHEDULES)
->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval))
->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $interval))
->trigger();
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ class SDKs extends Action
$message ??= Console::confirm('Please enter your commit message:');
}
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', '1.6.x', '1.7.x', 'latest'])) {
if (!\in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', '1.6.x', '1.7.x', '1.8.x', 'latest'])) {
throw new \Exception('Unknown version given');
}
@@ -444,11 +444,11 @@ class OpenAPI3 extends Format
$node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
$node['schema']['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
case 'Utopia\Validator\Integer':
$node['schema']['type'] = $validator->getType();
$node['schema']['format'] = 'int32';
break;
case 'Utopia\Validator\Numeric':
case 'Utopia\Validator\FloatValidator':
$node['schema']['type'] = 'number';
$node['schema']['format'] = 'float';
@@ -453,11 +453,11 @@ class Swagger2 extends Format
$node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
case 'Utopia\Validator\Integer':
$node['type'] = $validator->getType();
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Numeric':
case 'Utopia\Validator\FloatValidator':
$node['type'] = 'number';
$node['format'] = 'float';
@@ -25,7 +25,7 @@ class Base extends Queries
{
$config = Config::getParam('collections', []);
$collections = array_merge(
$collections = \array_merge(
$config['projects'],
$config['buckets'],
$config['databases'],
@@ -34,7 +34,7 @@ class Base extends Queries
);
$collection = $collections[$collection];
// array for constant lookup time
$allowedAttributesLookup = [];
foreach ($allowedAttributes as $attribute) {
$allowedAttributesLookup[$attribute] = true;
@@ -70,10 +70,9 @@ class Base extends Queries
'type' => Database::VAR_DATETIME,
'array' => false,
]);
$sequence = new Document([
$attributes[] = new Document([
'key' => '$sequence',
'type' => Database::VAR_STRING,
'type' => Database::VAR_INTEGER,
'array' => false,
]);
@@ -82,7 +81,7 @@ class Base extends Queries
new Offset(),
new Cursor(),
new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES),
new Order([...$attributes, $sequence]),
new Order($attributes),
];
parent::__construct($validators);
@@ -5369,4 +5369,211 @@ trait DatabasesBase
'x-appwrite-key' => $this->getProject()['apiKey']
]));
}
/**
* @throws \Exception
*/
public function testIncrementAttribute(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CounterDatabase'
]);
$databaseId = $database['body']['$id'];
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'CounterCollection',
'documentSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$collectionId = $collection['body']['$id'];
// Add integer attribute
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'count',
'required' => true,
]);
\sleep(2);
// Create document with initial count = 5
$doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'counter1',
'data' => [
'count' => 5
],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
],
]);
$this->assertEquals(201, $doc['headers']['status-code']);
// Increment by default 1
$inc = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1/count/increment', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(200, $inc['headers']['status-code']);
$this->assertEquals(6, $inc['body']['count']);
// Verify count = 6
$get = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(6, $get['body']['count']);
// Increment by custom value 4
$inc2 = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1/count/increment', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 4
]);
$this->assertEquals(200, $inc2['headers']['status-code']);
$this->assertEquals(10, $inc2['body']['count']);
$get2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(10, $get2['body']['count']);
// Test max limit exceeded
$err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1/count/increment', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['max' => 8]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test attribute not found
$notFound = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/counter1/unknown/increment', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(404, $notFound['headers']['status-code']);
}
public function testDecrementAttribute(): void
{
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => 'CounterDatabase'
]);
$databaseId = $database['body']['$id'];
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::unique(),
'name' => 'CounterCollection',
'documentSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
],
]);
$collectionId = $collection['body']['$id'];
// Add integer attribute
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'count',
'required' => true,
]);
\sleep(2);
// Create document with initial count = 10
$doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::unique(),
'data' => ['count' => 10],
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
],
]);
$documentId = $doc['body']['$id'];
// Decrement by default 1 (count = 10 -> 9)
$dec = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(200, $dec['headers']['status-code']);
$this->assertEquals(9, $dec['body']['count']);
$get = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(9, $get['body']['count']);
// Decrement by custom value 3 (count 9 -> 6)
$dec2 = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 3
]);
$this->assertEquals(200, $dec2['headers']['status-code']);
$this->assertEquals(6, $dec2['body']['count']);
$get2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(6, $get2['body']['count']);
// Test min limit exceeded
$err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['min' => 7]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test type error on non-numeric attribut
$typeErr = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), ['value' => 'not-a-number']);
$this->assertEquals(400, $typeErr['headers']['status-code']);
}
}
Binary file not shown.