From b6de14d7b7a4e9aa5beea92e7006881e3d889679 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 2 Sep 2026 14:05:33 +0100 Subject: [PATCH] chore: update PHP SDK to 30.0.0 --- CHANGELOG.md | 10 +++ README.md | 4 +- docs/account.md | 2 +- docs/avatars.md | 8 +- docs/documentsdb.md | 2 + docs/examples/documentsdb/create-document.md | 3 +- docs/examples/documentsdb/create-documents.md | 3 +- .../project/update-o-auth-2-cloudflare.md | 19 +++++ .../project/update-o-auth-2-resend.md | 19 +++++ ...cutover-migration.md => create-cutover.md} | 2 +- docs/examples/vectorsdb/create-document.md | 3 +- docs/examples/vectorsdb/create-documents.md | 3 +- docs/mongo.md | 2 +- docs/mysql.md | 2 +- docs/postgresql.md | 2 +- docs/project.md | 30 +++++++ docs/tablesdb.md | 2 +- docs/vectorsdb.md | 2 + src/Appwrite/Client.php | 6 +- src/Appwrite/Enums/OAuthProvider.php | 16 ++++ src/Appwrite/Enums/ProjectKeyScopes.php | 32 +++++++ src/Appwrite/Enums/ProjectOAuthProviderId.php | 16 ++++ src/Appwrite/Models/BillingPlan.php | 4 + src/Appwrite/Models/DedicatedDatabase.php | 9 +- .../Models/DedicatedDatabaseOperation.php | 2 +- src/Appwrite/Models/FrameworkAdapter.php | 9 +- src/Appwrite/Models/OAuth2Cloudflare.php | 70 ++++++++++++++++ src/Appwrite/Models/OAuth2Resend.php | 70 ++++++++++++++++ src/Appwrite/Services/Avatars.php | 17 ++-- src/Appwrite/Services/DocumentsDB.php | 12 ++- src/Appwrite/Services/Mongo.php | 12 +-- src/Appwrite/Services/Mysql.php | 12 +-- src/Appwrite/Services/Postgresql.php | 12 +-- src/Appwrite/Services/Project.php | 84 ++++++++++++++++++- src/Appwrite/Services/TablesDB.php | 4 +- src/Appwrite/Services/VectorsDB.php | 12 ++- tests/Appwrite/Services/DocumentsDBTest.php | 1 + tests/Appwrite/Services/MongoTest.php | 64 ++++---------- tests/Appwrite/Services/MysqlTest.php | 64 ++++---------- tests/Appwrite/Services/PostgresqlTest.php | 66 +++++---------- tests/Appwrite/Services/ProjectTest.php | 40 +++++++++ tests/Appwrite/Services/SitesTest.php | 3 +- tests/Appwrite/Services/TablesDBTest.php | 5 +- tests/Appwrite/Services/VectorsDBTest.php | 1 + 44 files changed, 560 insertions(+), 201 deletions(-) create mode 100644 docs/examples/project/update-o-auth-2-cloudflare.md create mode 100644 docs/examples/project/update-o-auth-2-resend.md rename docs/examples/tablesdb/{cutover-migration.md => create-cutover.md} (91%) create mode 100644 src/Appwrite/Models/OAuth2Cloudflare.php create mode 100644 src/Appwrite/Models/OAuth2Resend.php diff --git a/CHANGELOG.md b/CHANGELOG.md index eca2e74d..b9a9ccfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## 30.0.0 + +* Stable release of the dedicated database APIs: `mysql`, `postgresql`, `mongo`, `documentsDB`, and `vectorsDB` services, previously released as release candidates +* Breaking: `tablesDB.cutoverMigration` is renamed to `tablesDB.createCutover` +* Added: Cloudflare and Resend OAuth providers +* Added: `usageAggregateOnlyMetrics` on the `BillingPlan` model +* Fixed: `transactionId` is accepted again by `documentsDB` and `vectorsDB` `createDocument` and `createDocuments` +* Updated: `X-Appwrite-Response-Format` is now `2.0.0` +* Updated: `FrameworkAdapter.fallbackFile` is now optional + ## 30.0.0-rc.1 * Breaking: `EmbeddingModel` no longer offers `embedding-gemma` or `bge-small` diff --git a/README.md b/README.md index 0afbcba6..bd76997b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Appwrite PHP SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-php.svg?style=flat-square&v=1) -![Version](https://img.shields.io/badge/api%20version-1.9.6-blue.svg?style=flat-square&v=1) +![Version](https://img.shields.io/badge/api%20version-2.0.0-blue.svg?style=flat-square&v=1) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK is compatible with Appwrite server version 1.9.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-php/releases).** +**This SDK targets Appwrite server version 1.9.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-php/releases) when APIs differ. Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the PHP SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs) diff --git a/docs/account.md b/docs/account.md index 4ed44759..f2850877 100644 --- a/docs/account.md +++ b/docs/account.md @@ -635,7 +635,7 @@ A user is limited to 10 active sessions at a time by default. [Learn more about | Field Name | Type | Description | Default | | --- | --- | --- | --- | -| provider | string | **Required** OAuth2 Provider. Currently, supported providers are: amazon, apple, appwrite, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, huggingface, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom. | | +| provider | string | **Required** OAuth2 Provider. Currently, supported providers are: amazon, apple, appwrite, auth0, authentik, autodesk, bitbucket, bitly, box, cloudflare, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, huggingface, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, resend, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom. | | | success | string | URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API. | | | failure | string | URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API. | | | scopes | array | A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of 100 scopes are allowed, each 4096 characters long. | [] | diff --git a/docs/avatars.md b/docs/avatars.md index d9f421f5..367d29f6 100644 --- a/docs/avatars.md +++ b/docs/avatars.md @@ -118,7 +118,7 @@ GET https://cloud.appwrite.io/v1/avatars/photo ** Returns the best available profile photo for a user. The endpoint tries each source in priority order and returns the first successful result: OAuth2 identity photo, Gravatar, Libravatar, Appwrite Initials, built-in static fallback. -The photo resolves for the currently authenticated user unless `userId` points at another user. Passing `emailHash` and/or `name` resolves the avatar from those values alone: the hash is looked up on Gravatar and Libravatar, the name is rendered as initials, and the user's own identity photos, email, and name leave the chain so they never shadow the avatar being asked for. Emails are only ever accepted pre-hashed, so no address ends up in a URL. ** +Passing `userId` — `current()` for the authenticated user — resolves the photo from everything known about that user: identity photos, email, and name. An explicit `emailHash` or `name` then overrides just that value, and the user's remaining sources stay in the chain. Without `userId`, passing `emailHash` and/or `name` resolves the avatar from those values alone: the hash is looked up on Gravatar and Libravatar, the name is rendered as initials, and the session user stays out of the chain so their own photo never shadows the avatar being asked for. When nothing is passed, the photo resolves for the currently authenticated user. Emails are only ever accepted pre-hashed, so no address ends up in a URL. ** ### Parameters @@ -129,9 +129,9 @@ The photo resolves for the currently authenticated user unless `userId` points a | quality | integer | Output image quality between 0 and 100. Defaults to 100. | 100 | | output | string | Output image format. Defaults to 'png'. | png | | rating | string | Maximum image rating to fetch from Gravatar/Libravatar. Defaults to 'g'. | g | -| userId | string | User ID to resolve the photo for. Defaults to 'current()' for the currently authenticated user. | current() | -| emailHash | string | SHA256 hash of the lowercase, trimmed email address to look up on Gravatar and Libravatar instead of the user's own photo sources. Pass the hash, never the address itself. | | -| name | string | Name to render initials from instead of the user's own photo sources. Max length: 128 chars. | | +| userId | string | User ID to resolve the photo for. Pass 'current()' for the currently authenticated user. When omitted, the session user is used only if no emailHash and no name is passed. | | +| emailHash | string | SHA256 hash of the lowercase, trimmed email address to look up on Gravatar and Libravatar instead of the user's own email. Pass the hash, never the address itself. | | +| name | string | Name to render initials from instead of the user's own name. Max length: 128 chars. | | ```http request diff --git a/docs/documentsdb.md b/docs/documentsdb.md index 6f585a2c..49e3a09a 100644 --- a/docs/documentsdb.md +++ b/docs/documentsdb.md @@ -282,6 +282,7 @@ POST https://cloud.appwrite.io/v1/documentsdb/{databaseId}/collections/{collecti | documentId | string | Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. | | | data | object | Document data as JSON object. | {} | | permissions | array | An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). | | +| transactionId | string | Transaction ID for staging the operation. | | ```http request @@ -297,6 +298,7 @@ POST https://cloud.appwrite.io/v1/documentsdb/{databaseId}/collections/{collecti | databaseId | string | **Required** Database ID. | | | collectionId | string | **Required** Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents. | | | documents | array | Array of documents data as JSON objects. | [] | +| transactionId | string | Transaction ID for staging the operation. | | ```http request diff --git a/docs/examples/documentsdb/create-document.md b/docs/examples/documentsdb/create-document.md index dc88f16d..4b894f41 100644 --- a/docs/examples/documentsdb/create-document.md +++ b/docs/examples/documentsdb/create-document.md @@ -24,6 +24,7 @@ $result = $documentsDB->createDocument( 'age' => 30, 'isAdmin' => false ], - permissions: [Permission::read(Role::any())] // optional + permissions: [Permission::read(Role::any())], // optional + transactionId: '' // optional ); ``` diff --git a/docs/examples/documentsdb/create-documents.md b/docs/examples/documentsdb/create-documents.md index e8f0dedd..ec6a4355 100644 --- a/docs/examples/documentsdb/create-documents.md +++ b/docs/examples/documentsdb/create-documents.md @@ -14,6 +14,7 @@ $documentsDB = new DocumentsDB($client); $result = $documentsDB->createDocuments( databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional ); ``` diff --git a/docs/examples/project/update-o-auth-2-cloudflare.md b/docs/examples/project/update-o-auth-2-cloudflare.md new file mode 100644 index 00000000..95e98cbb --- /dev/null +++ b/docs/examples/project/update-o-auth-2-cloudflare.md @@ -0,0 +1,19 @@ +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$project = new Project($client); + +$result = $project->updateOAuth2Cloudflare( + clientId: '', // optional + clientSecret: '', // optional + enabled: false // optional +); +``` diff --git a/docs/examples/project/update-o-auth-2-resend.md b/docs/examples/project/update-o-auth-2-resend.md new file mode 100644 index 00000000..89c0ba70 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-resend.md @@ -0,0 +1,19 @@ +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$project = new Project($client); + +$result = $project->updateOAuth2Resend( + clientId: '', // optional + clientSecret: '', // optional + enabled: false // optional +); +``` diff --git a/docs/examples/tablesdb/cutover-migration.md b/docs/examples/tablesdb/create-cutover.md similarity index 91% rename from docs/examples/tablesdb/cutover-migration.md rename to docs/examples/tablesdb/create-cutover.md index 624449ea..b07aa3cb 100644 --- a/docs/examples/tablesdb/cutover-migration.md +++ b/docs/examples/tablesdb/create-cutover.md @@ -11,7 +11,7 @@ $client = (new Client()) $tablesDB = new TablesDB($client); -$result = $tablesDB->cutoverMigration( +$result = $tablesDB->createCutover( databaseId: '', migrationId: '' ); diff --git a/docs/examples/vectorsdb/create-document.md b/docs/examples/vectorsdb/create-document.md index e024efec..b97f81e4 100644 --- a/docs/examples/vectorsdb/create-document.md +++ b/docs/examples/vectorsdb/create-document.md @@ -28,6 +28,7 @@ $result = $vectorsDB->createDocument( 'key' => 'value' ] ], - permissions: [Permission::read(Role::any())] // optional + permissions: [Permission::read(Role::any())], // optional + transactionId: '' // optional ); ``` diff --git a/docs/examples/vectorsdb/create-documents.md b/docs/examples/vectorsdb/create-documents.md index 07b5894a..2d1c9c2f 100644 --- a/docs/examples/vectorsdb/create-documents.md +++ b/docs/examples/vectorsdb/create-documents.md @@ -14,6 +14,7 @@ $vectorsDB = new VectorsDB($client); $result = $vectorsDB->createDocuments( databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional ); ``` diff --git a/docs/mongo.md b/docs/mongo.md index 7ec032fd..084c9fca 100644 --- a/docs/mongo.md +++ b/docs/mongo.md @@ -307,7 +307,7 @@ DELETE https://cloud.appwrite.io/v1/mongo/{databaseId}/branches/{branchId} PATCH https://cloud.appwrite.io/v1/mongo/{databaseId}/credentials ``` -** Rotate the primary connection credentials for a dedicated database. Generates a new password and updates the database atomically. Previous credentials stop working immediately. Returns the database with a refreshed connection string carrying the new password. ** +** Queue a rotation of the primary connection credentials for a dedicated database. A hibernated database is woken by the worker before rotation. List database operations until the returned operation reaches a terminal status, then fetch the database again for the refreshed connection string. ** ### Parameters diff --git a/docs/mysql.md b/docs/mysql.md index c88ce400..ced7ecea 100644 --- a/docs/mysql.md +++ b/docs/mysql.md @@ -307,7 +307,7 @@ DELETE https://cloud.appwrite.io/v1/mysql/{databaseId}/branches/{branchId} PATCH https://cloud.appwrite.io/v1/mysql/{databaseId}/credentials ``` -** Rotate the primary connection credentials for a dedicated database. Generates a new password and updates the database atomically. Previous credentials stop working immediately. Returns the database with a refreshed connection string carrying the new password. ** +** Queue a rotation of the primary connection credentials for a dedicated database. A hibernated database is woken by the worker before rotation. List database operations until the returned operation reaches a terminal status, then fetch the database again for the refreshed connection string. ** ### Parameters diff --git a/docs/postgresql.md b/docs/postgresql.md index 982d69fc..d1af41f3 100644 --- a/docs/postgresql.md +++ b/docs/postgresql.md @@ -307,7 +307,7 @@ DELETE https://cloud.appwrite.io/v1/postgresql/{databaseId}/branches/{branchId} PATCH https://cloud.appwrite.io/v1/postgresql/{databaseId}/credentials ``` -** Rotate the primary connection credentials for a dedicated database. Generates a new password and updates the database atomically. Previous credentials stop working immediately. Returns the database with a refreshed connection string carrying the new password. ** +** Queue a rotation of the primary connection credentials for a dedicated database. A hibernated database is woken by the worker before rotation. List database operations until the returned operation reaches a terminal status, then fetch the database again for the refreshed connection string. ** ### Parameters diff --git a/docs/project.md b/docs/project.md index dc09a767..85e7a379 100644 --- a/docs/project.md +++ b/docs/project.md @@ -363,6 +363,21 @@ PATCH https://cloud.appwrite.io/v1/project/oauth2/box | enabled | boolean | OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. | | +```http request +PATCH https://cloud.appwrite.io/v1/project/oauth2/cloudflare +``` + +** Update the project OAuth2 Cloudflare configuration. ** + +### Parameters + +| Field Name | Type | Description | Default | +| --- | --- | --- | --- | +| clientId | string | 'Client ID' of Cloudflare OAuth2 app. For example: 4b866000000000000000000000c9e4e2 | | +| clientSecret | string | 'Client Secret' of Cloudflare OAuth2 app. For example: cfoc_5Q6YRl0000000000000000000000000000000000003d214f | | +| enabled | boolean | OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. | | + + ```http request PATCH https://cloud.appwrite.io/v1/project/oauth2/dailymotion ``` @@ -707,6 +722,21 @@ PATCH https://cloud.appwrite.io/v1/project/oauth2/podio | enabled | boolean | OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. | | +```http request +PATCH https://cloud.appwrite.io/v1/project/oauth2/resend +``` + +** Update the project OAuth2 Resend configuration. ** + +### Parameters + +| Field Name | Type | Description | Default | +| --- | --- | --- | --- | +| clientId | string | 'Client ID' of Resend OAuth2 app. For example: f47ac10b-58cc-4372-a567-0e02b2c3d479 | | +| clientSecret | string | 'Client Secret' of Resend OAuth2 app. For example: 9c1e4b00000000000000000000000000000000000000000000000000a72d5f4 | | +| enabled | boolean | OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. | | + + ```http request PATCH https://cloud.appwrite.io/v1/project/oauth2/salesforce ``` diff --git a/docs/tablesdb.md b/docs/tablesdb.md index bdd7882d..4e1b191d 100644 --- a/docs/tablesdb.md +++ b/docs/tablesdb.md @@ -238,7 +238,7 @@ DELETE https://cloud.appwrite.io/v1/tablesdb/{databaseId}/migrations/{migrationI ```http request -POST https://cloud.appwrite.io/v1/tablesdb/{databaseId}/migrations/{migrationId}/cutover +POST https://cloud.appwrite.io/v1/tablesdb/{databaseId}/migrations/{migrationId}/cutovers ``` ** Cut a verified TablesDB migration over to its dedicated compute. Only applies to a migration created with `autoCutover` disabled, which waits at `ready_to_cutover` until this is called. The routing flip happens shortly after this returns, with a brief read-only window. One call buys one attempt: a cutover that fails a check returns the migration to `verifying` and parks it again, so call this once more to retry. ** diff --git a/docs/vectorsdb.md b/docs/vectorsdb.md index c2db2cbe..d63113b2 100644 --- a/docs/vectorsdb.md +++ b/docs/vectorsdb.md @@ -281,6 +281,7 @@ POST https://cloud.appwrite.io/v1/vectorsdb/{databaseId}/collections/{collection | documentId | string | Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. | | | data | object | Document data as JSON object. | {} | | permissions | array | An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). | | +| transactionId | string | Transaction ID for staging the operation. | | ```http request @@ -296,6 +297,7 @@ POST https://cloud.appwrite.io/v1/vectorsdb/{databaseId}/collections/{collection | databaseId | string | **Required** Database ID. | | | collectionId | string | **Required** Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents. | | | documents | array | Array of documents data as JSON objects. | [] | +| transactionId | string | Transaction ID for staging the operation. | | ```http request diff --git a/src/Appwrite/Client.php b/src/Appwrite/Client.php index 562580f0..0335259a 100644 --- a/src/Appwrite/Client.php +++ b/src/Appwrite/Client.php @@ -43,11 +43,11 @@ class Client * */ protected array $headers = [ - 'user-agent' => 'AppwritePHPSDK/30.0.0-rc.1 ()', + 'user-agent' => 'AppwritePHPSDK/30.0.0 ()', 'x-sdk-name' => 'PHP', 'x-sdk-platform' => 'server', 'x-sdk-language' => 'php', - 'x-sdk-version' => '30.0.0-rc.1', + 'x-sdk-version' => '30.0.0', ]; /** @@ -110,7 +110,7 @@ class Client public function __construct() { $this->requestFactory = new RequestFactory(); - $this->headers['X-Appwrite-Response-Format'] = '1.9.6'; + $this->headers['X-Appwrite-Response-Format'] = '2.0.0'; } /** diff --git a/src/Appwrite/Enums/OAuthProvider.php b/src/Appwrite/Enums/OAuthProvider.php index 69900eff..0a6cf91a 100644 --- a/src/Appwrite/Enums/OAuthProvider.php +++ b/src/Appwrite/Enums/OAuthProvider.php @@ -18,6 +18,7 @@ class OAuthProvider implements JsonSerializable, Stringable private static OAuthProvider $BITBUCKET; private static OAuthProvider $BITLY; private static OAuthProvider $BOX; + private static OAuthProvider $CLOUDFLARE; private static OAuthProvider $DAILYMOTION; private static OAuthProvider $DISCORD; private static OAuthProvider $DISQUS; @@ -40,6 +41,7 @@ class OAuthProvider implements JsonSerializable, Stringable private static OAuthProvider $PAYPAL; private static OAuthProvider $PAYPALSANDBOX; private static OAuthProvider $PODIO; + private static OAuthProvider $RESEND; private static OAuthProvider $SALESFORCE; private static OAuthProvider $SLACK; private static OAuthProvider $SPOTIFY; @@ -123,6 +125,12 @@ public static function BOX(): OAuthProvider return self::$BOX; } + public static function CLOUDFLARE(): OAuthProvider + { + self::$CLOUDFLARE ??= new OAuthProvider('cloudflare'); + + return self::$CLOUDFLARE; + } public static function DAILYMOTION(): OAuthProvider { self::$DAILYMOTION ??= new OAuthProvider('dailymotion'); @@ -255,6 +263,12 @@ public static function PODIO(): OAuthProvider return self::$PODIO; } + public static function RESEND(): OAuthProvider + { + self::$RESEND ??= new OAuthProvider('resend'); + + return self::$RESEND; + } public static function SALESFORCE(): OAuthProvider { self::$SALESFORCE ??= new OAuthProvider('salesforce'); @@ -352,6 +366,7 @@ public static function from(string $value): self 'bitbucket' => self::BITBUCKET(), 'bitly' => self::BITLY(), 'box' => self::BOX(), + 'cloudflare' => self::CLOUDFLARE(), 'dailymotion' => self::DAILYMOTION(), 'discord' => self::DISCORD(), 'disqus' => self::DISQUS(), @@ -374,6 +389,7 @@ public static function from(string $value): self 'paypal' => self::PAYPAL(), 'paypalSandbox' => self::PAYPALSANDBOX(), 'podio' => self::PODIO(), + 'resend' => self::RESEND(), 'salesforce' => self::SALESFORCE(), 'slack' => self::SLACK(), 'spotify' => self::SPOTIFY(), diff --git a/src/Appwrite/Enums/ProjectKeyScopes.php b/src/Appwrite/Enums/ProjectKeyScopes.php index 408eb9cf..cdab332d 100644 --- a/src/Appwrite/Enums/ProjectKeyScopes.php +++ b/src/Appwrite/Enums/ProjectKeyScopes.php @@ -57,12 +57,16 @@ class ProjectKeyScopes implements JsonSerializable, Stringable private static ProjectKeyScopes $DOCUMENTSDBCOLLECTIONSWRITE; private static ProjectKeyScopes $DOCUMENTSDBDOCUMENTSREAD; private static ProjectKeyScopes $DOCUMENTSDBDOCUMENTSWRITE; + private static ProjectKeyScopes $DOCUMENTSDBINDEXESREAD; + private static ProjectKeyScopes $DOCUMENTSDBINDEXESWRITE; private static ProjectKeyScopes $VECTORSDBREAD; private static ProjectKeyScopes $VECTORSDBWRITE; private static ProjectKeyScopes $VECTORSDBCOLLECTIONSREAD; private static ProjectKeyScopes $VECTORSDBCOLLECTIONSWRITE; private static ProjectKeyScopes $VECTORSDBDOCUMENTSREAD; private static ProjectKeyScopes $VECTORSDBDOCUMENTSWRITE; + private static ProjectKeyScopes $VECTORSDBINDEXESREAD; + private static ProjectKeyScopes $VECTORSDBINDEXESWRITE; private static ProjectKeyScopes $BUCKETSREAD; private static ProjectKeyScopes $BUCKETSWRITE; private static ProjectKeyScopes $FILESREAD; @@ -429,6 +433,18 @@ public static function DOCUMENTSDBDOCUMENTSWRITE(): ProjectKeyScopes return self::$DOCUMENTSDBDOCUMENTSWRITE; } + public static function DOCUMENTSDBINDEXESREAD(): ProjectKeyScopes + { + self::$DOCUMENTSDBINDEXESREAD ??= new ProjectKeyScopes('documentsdb.indexes.read'); + + return self::$DOCUMENTSDBINDEXESREAD; + } + public static function DOCUMENTSDBINDEXESWRITE(): ProjectKeyScopes + { + self::$DOCUMENTSDBINDEXESWRITE ??= new ProjectKeyScopes('documentsdb.indexes.write'); + + return self::$DOCUMENTSDBINDEXESWRITE; + } public static function VECTORSDBREAD(): ProjectKeyScopes { self::$VECTORSDBREAD ??= new ProjectKeyScopes('vectorsdb.read'); @@ -465,6 +481,18 @@ public static function VECTORSDBDOCUMENTSWRITE(): ProjectKeyScopes return self::$VECTORSDBDOCUMENTSWRITE; } + public static function VECTORSDBINDEXESREAD(): ProjectKeyScopes + { + self::$VECTORSDBINDEXESREAD ??= new ProjectKeyScopes('vectorsdb.indexes.read'); + + return self::$VECTORSDBINDEXESREAD; + } + public static function VECTORSDBINDEXESWRITE(): ProjectKeyScopes + { + self::$VECTORSDBINDEXESWRITE ??= new ProjectKeyScopes('vectorsdb.indexes.write'); + + return self::$VECTORSDBINDEXESWRITE; + } public static function BUCKETSREAD(): ProjectKeyScopes { self::$BUCKETSREAD ??= new ProjectKeyScopes('buckets.read'); @@ -895,12 +923,16 @@ public static function from(string $value): self 'documentsdb.collections.write' => self::DOCUMENTSDBCOLLECTIONSWRITE(), 'documentsdb.documents.read' => self::DOCUMENTSDBDOCUMENTSREAD(), 'documentsdb.documents.write' => self::DOCUMENTSDBDOCUMENTSWRITE(), + 'documentsdb.indexes.read' => self::DOCUMENTSDBINDEXESREAD(), + 'documentsdb.indexes.write' => self::DOCUMENTSDBINDEXESWRITE(), 'vectorsdb.read' => self::VECTORSDBREAD(), 'vectorsdb.write' => self::VECTORSDBWRITE(), 'vectorsdb.collections.read' => self::VECTORSDBCOLLECTIONSREAD(), 'vectorsdb.collections.write' => self::VECTORSDBCOLLECTIONSWRITE(), 'vectorsdb.documents.read' => self::VECTORSDBDOCUMENTSREAD(), 'vectorsdb.documents.write' => self::VECTORSDBDOCUMENTSWRITE(), + 'vectorsdb.indexes.read' => self::VECTORSDBINDEXESREAD(), + 'vectorsdb.indexes.write' => self::VECTORSDBINDEXESWRITE(), 'buckets.read' => self::BUCKETSREAD(), 'buckets.write' => self::BUCKETSWRITE(), 'files.read' => self::FILESREAD(), diff --git a/src/Appwrite/Enums/ProjectOAuthProviderId.php b/src/Appwrite/Enums/ProjectOAuthProviderId.php index 71c58dfd..1d797c89 100644 --- a/src/Appwrite/Enums/ProjectOAuthProviderId.php +++ b/src/Appwrite/Enums/ProjectOAuthProviderId.php @@ -18,6 +18,7 @@ class ProjectOAuthProviderId implements JsonSerializable, Stringable private static ProjectOAuthProviderId $BITBUCKET; private static ProjectOAuthProviderId $BITLY; private static ProjectOAuthProviderId $BOX; + private static ProjectOAuthProviderId $CLOUDFLARE; private static ProjectOAuthProviderId $DAILYMOTION; private static ProjectOAuthProviderId $DISCORD; private static ProjectOAuthProviderId $DISQUS; @@ -40,6 +41,7 @@ class ProjectOAuthProviderId implements JsonSerializable, Stringable private static ProjectOAuthProviderId $PAYPAL; private static ProjectOAuthProviderId $PAYPALSANDBOX; private static ProjectOAuthProviderId $PODIO; + private static ProjectOAuthProviderId $RESEND; private static ProjectOAuthProviderId $SALESFORCE; private static ProjectOAuthProviderId $SLACK; private static ProjectOAuthProviderId $SPOTIFY; @@ -123,6 +125,12 @@ public static function BOX(): ProjectOAuthProviderId return self::$BOX; } + public static function CLOUDFLARE(): ProjectOAuthProviderId + { + self::$CLOUDFLARE ??= new ProjectOAuthProviderId('cloudflare'); + + return self::$CLOUDFLARE; + } public static function DAILYMOTION(): ProjectOAuthProviderId { self::$DAILYMOTION ??= new ProjectOAuthProviderId('dailymotion'); @@ -255,6 +263,12 @@ public static function PODIO(): ProjectOAuthProviderId return self::$PODIO; } + public static function RESEND(): ProjectOAuthProviderId + { + self::$RESEND ??= new ProjectOAuthProviderId('resend'); + + return self::$RESEND; + } public static function SALESFORCE(): ProjectOAuthProviderId { self::$SALESFORCE ??= new ProjectOAuthProviderId('salesforce'); @@ -352,6 +366,7 @@ public static function from(string $value): self 'bitbucket' => self::BITBUCKET(), 'bitly' => self::BITLY(), 'box' => self::BOX(), + 'cloudflare' => self::CLOUDFLARE(), 'dailymotion' => self::DAILYMOTION(), 'discord' => self::DISCORD(), 'disqus' => self::DISQUS(), @@ -374,6 +389,7 @@ public static function from(string $value): self 'paypal' => self::PAYPAL(), 'paypalSandbox' => self::PAYPALSANDBOX(), 'podio' => self::PODIO(), + 'resend' => self::RESEND(), 'salesforce' => self::SALESFORCE(), 'slack' => self::SLACK(), 'spotify' => self::SPOTIFY(), diff --git a/src/Appwrite/Models/BillingPlan.php b/src/Appwrite/Models/BillingPlan.php index 73d1a0d0..add36cb8 100644 --- a/src/Appwrite/Models/BillingPlan.php +++ b/src/Appwrite/Models/BillingPlan.php @@ -84,6 +84,7 @@ * @param int|null $members members * @param int|null $activityLogs activity log days * @param array|null $usageLogsIntervals usage log time intervals allowed for this plan (e.g. 15m, 1h, 1d). + * @param array|null $usageAggregateOnlyMetrics metrics this plan only records as a total. they cannot be broken down by dimension or filtered, because the stored events cover a fraction of the real traffic. * @param bool|null $backupsEnabled does plan support backup policies. * @param int|null $backupPolicies how many policies does plan support * @param BillingPlanLimits|null $limits plan specific limits @@ -157,6 +158,7 @@ public function __construct( public ?int $members = null, public ?int $activityLogs = null, public ?array $usageLogsIntervals = null, + public ?array $usageAggregateOnlyMetrics = null, public ?bool $backupsEnabled = null, public ?int $backupPolicies = null, public ?BillingPlanLimits $limits = null, @@ -427,6 +429,7 @@ functions: $data['functions'], members: $data['members'] ?? null, activityLogs: $data['activityLogs'] ?? null, usageLogsIntervals: $data['usageLogsIntervals'] ?? null, + usageAggregateOnlyMetrics: $data['usageAggregateOnlyMetrics'] ?? null, backupsEnabled: $data['backupsEnabled'] ?? null, backupPolicies: $data['backupPolicies'] ?? null, limits: array_key_exists('limits', $data) ? static::hydrateTypedValue(BillingPlanLimits::class, $data['limits'], true) : null, @@ -478,6 +481,7 @@ public function toArray(): array 'activityLogs' => static::serializeValue($this->activityLogs), 'usageLogs' => static::serializeValue($this->usageLogs), 'usageLogsIntervals' => static::serializeValue($this->usageLogsIntervals), + 'usageAggregateOnlyMetrics' => static::serializeValue($this->usageAggregateOnlyMetrics), 'projectInactivityDays' => static::serializeValue($this->projectInactivityDays), 'alertLimit' => static::serializeValue($this->alertLimit), 'usage' => static::serializeValue($this->usage), diff --git a/src/Appwrite/Models/DedicatedDatabase.php b/src/Appwrite/Models/DedicatedDatabase.php index a1560b58..28ad1490 100644 --- a/src/Appwrite/Models/DedicatedDatabase.php +++ b/src/Appwrite/Models/DedicatedDatabase.php @@ -25,11 +25,12 @@ * @param string $engine database engine: postgresql, mysql, or mongodb. null until the backing reports one. * @param string $version database engine version. * @param string $specification specification identifier. - * @param string $backend database backend provider. possible values: prisma, edge. + * @param string $backend database backend provider. possible values: edge. * @param string $hostname database hostname for connections. * @param int $connectionPort database port for connections. derived from the engine when the backing has not reported one yet. * @param string $connectionUser database username for connections. * @param string $connectionPassword database password for connections. + * @param int $credentialGeneration committed generation of the primary connection credentials. null until the rotation contract has been initialized. * @param string $connectionString full database connection string (uri format). * @param bool $ssl whether ssl/tls is required for client connections. * @param string $status database status. possible values: provisioning, ready, inactive, paused, failed, deleted, restoring, scaling. @@ -80,6 +81,7 @@ public function __construct( public int $connectionPort, public string $connectionUser, public string $connectionPassword, + public int $credentialGeneration, public string $connectionString, public bool $ssl, public string $status, @@ -164,6 +166,9 @@ public static function from(array $data): static if (!array_key_exists('connectionPassword', $data)) { throw new \InvalidArgumentException('Missing required field "connectionPassword" for ' . static::class . '.'); } + if (!array_key_exists('credentialGeneration', $data)) { + throw new \InvalidArgumentException('Missing required field "credentialGeneration" for ' . static::class . '.'); + } if (!array_key_exists('connectionString', $data)) { throw new \InvalidArgumentException('Missing required field "connectionString" for ' . static::class . '.'); } @@ -276,6 +281,7 @@ public static function from(array $data): static connectionPort: $data['connectionPort'], connectionUser: $data['connectionUser'], connectionPassword: $data['connectionPassword'], + credentialGeneration: $data['credentialGeneration'], connectionString: $data['connectionString'], ssl: $data['ssl'], status: $data['status'], @@ -333,6 +339,7 @@ public function toArray(): array 'connectionPort' => static::serializeValue($this->connectionPort), 'connectionUser' => static::serializeValue($this->connectionUser), 'connectionPassword' => static::serializeValue($this->connectionPassword), + 'credentialGeneration' => static::serializeValue($this->credentialGeneration), 'connectionString' => static::serializeValue($this->connectionString), 'ssl' => static::serializeValue($this->ssl), 'status' => static::serializeValue($this->status), diff --git a/src/Appwrite/Models/DedicatedDatabaseOperation.php b/src/Appwrite/Models/DedicatedDatabaseOperation.php index d52ebcf6..228f5b56 100644 --- a/src/Appwrite/Models/DedicatedDatabaseOperation.php +++ b/src/Appwrite/Models/DedicatedDatabaseOperation.php @@ -19,7 +19,7 @@ * @param string $id operation id. * @param string $createdAt operation creation time in iso 8601 format. * @param string $databaseId database id the operation ran against. - * @param string $type operation type, such as provision, update, restore, pausing, resuming, failover, backup-create or cross-region-enable. + * @param string $type operation type, such as provision, update, credentials-update, restore, pausing, resuming, failover, backup-create or cross-region-enable. * @param string $status operation status. possible values: queued (accepted and waiting to resume), running (in progress), completed (finished successfully), failed (ended in an error). * @param int $attempts number of times this operation has been attempted. * @param string $errorCode machine-readable failure code. `interrupted` marks an attempt that ended before its outcome could be confirmed. diff --git a/src/Appwrite/Models/FrameworkAdapter.php b/src/Appwrite/Models/FrameworkAdapter.php index b1e6b01e..82727ba4 100644 --- a/src/Appwrite/Models/FrameworkAdapter.php +++ b/src/Appwrite/Models/FrameworkAdapter.php @@ -20,14 +20,14 @@ * @param string $installCommand default command to download dependencies. * @param string $buildCommand default command to build site into output directory. * @param string $outputDirectory default output directory of build. - * @param string $fallbackFile name of fallback file to use instead of 404 page. if null, appwrite 404 page will be displayed. + * @param string|null $fallbackFile name of fallback file to use instead of 404 page. if null, appwrite 404 page will be displayed. */ public function __construct( public string $key, public string $installCommand, public string $buildCommand, public string $outputDirectory, - public string $fallbackFile + public ?string $fallbackFile = null ) { } @@ -48,16 +48,13 @@ public static function from(array $data): static if (!array_key_exists('outputDirectory', $data)) { throw new \InvalidArgumentException('Missing required field "outputDirectory" for ' . static::class . '.'); } - if (!array_key_exists('fallbackFile', $data)) { - throw new \InvalidArgumentException('Missing required field "fallbackFile" for ' . static::class . '.'); - } return new static( key: $data['key'], installCommand: $data['installCommand'], buildCommand: $data['buildCommand'], outputDirectory: $data['outputDirectory'], - fallbackFile: $data['fallbackFile'] + fallbackFile: $data['fallbackFile'] ?? null ); } diff --git a/src/Appwrite/Models/OAuth2Cloudflare.php b/src/Appwrite/Models/OAuth2Cloudflare.php new file mode 100644 index 00000000..1d5e9736 --- /dev/null +++ b/src/Appwrite/Models/OAuth2Cloudflare.php @@ -0,0 +1,70 @@ + $data + */ + public static function from(array $data): static + { + if (!array_key_exists('$id', $data)) { + throw new \InvalidArgumentException('Missing required field "$id" for ' . static::class . '.'); + } + if (!array_key_exists('enabled', $data)) { + throw new \InvalidArgumentException('Missing required field "enabled" for ' . static::class . '.'); + } + if (!array_key_exists('clientId', $data)) { + throw new \InvalidArgumentException('Missing required field "clientId" for ' . static::class . '.'); + } + if (!array_key_exists('clientSecret', $data)) { + throw new \InvalidArgumentException('Missing required field "clientSecret" for ' . static::class . '.'); + } + + return new static( + id: $data['$id'], + enabled: $data['enabled'], + clientId: $data['clientId'], + clientSecret: $data['clientSecret'] + ); + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + '$id' => static::serializeValue($this->id), + 'enabled' => static::serializeValue($this->enabled), + 'clientId' => static::serializeValue($this->clientId), + 'clientSecret' => static::serializeValue($this->clientSecret) + ]; + } +} diff --git a/src/Appwrite/Models/OAuth2Resend.php b/src/Appwrite/Models/OAuth2Resend.php new file mode 100644 index 00000000..5e741b93 --- /dev/null +++ b/src/Appwrite/Models/OAuth2Resend.php @@ -0,0 +1,70 @@ + $data + */ + public static function from(array $data): static + { + if (!array_key_exists('$id', $data)) { + throw new \InvalidArgumentException('Missing required field "$id" for ' . static::class . '.'); + } + if (!array_key_exists('enabled', $data)) { + throw new \InvalidArgumentException('Missing required field "enabled" for ' . static::class . '.'); + } + if (!array_key_exists('clientId', $data)) { + throw new \InvalidArgumentException('Missing required field "clientId" for ' . static::class . '.'); + } + if (!array_key_exists('clientSecret', $data)) { + throw new \InvalidArgumentException('Missing required field "clientSecret" for ' . static::class . '.'); + } + + return new static( + id: $data['$id'], + enabled: $data['enabled'], + clientId: $data['clientId'], + clientSecret: $data['clientSecret'] + ); + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + '$id' => static::serializeValue($this->id), + 'enabled' => static::serializeValue($this->enabled), + 'clientId' => static::serializeValue($this->clientId), + 'clientSecret' => static::serializeValue($this->clientSecret) + ]; + } +} diff --git a/src/Appwrite/Services/Avatars.php b/src/Appwrite/Services/Avatars.php index 39ef900a..7d59419c 100644 --- a/src/Appwrite/Services/Avatars.php +++ b/src/Appwrite/Services/Avatars.php @@ -300,13 +300,16 @@ public function getInitials(?string $name = null, ?int $width = null, ?int $heig * OAuth2 identity photo, Gravatar, Libravatar, Appwrite Initials, built-in * static fallback. * - * The photo resolves for the currently authenticated user unless `userId` - * points at another user. Passing `emailHash` and/or `name` resolves the - * avatar from those values alone: the hash is looked up on Gravatar and - * Libravatar, the name is rendered as initials, and the user's own identity - * photos, email, and name leave the chain so they never shadow the avatar - * being asked for. Emails are only ever accepted pre-hashed, so no address - * ends up in a URL. + * Passing `userId` — `current()` for the authenticated user — resolves + * the photo from everything known about that user: identity photos, email, + * and name. An explicit `emailHash` or `name` then overrides just that value, + * and the user's remaining sources stay in the chain. Without `userId`, + * passing `emailHash` and/or `name` resolves the avatar from those values + * alone: the hash is looked up on Gravatar and Libravatar, the name is + * rendered as initials, and the session user stays out of the chain so their + * own photo never shadows the avatar being asked for. When nothing is passed, + * the photo resolves for the currently authenticated user. Emails are only + * ever accepted pre-hashed, so no address ends up in a URL. * * @throws AppwriteException */ diff --git a/src/Appwrite/Services/DocumentsDB.php b/src/Appwrite/Services/DocumentsDB.php index 3919a7d9..7947b14b 100644 --- a/src/Appwrite/Services/DocumentsDB.php +++ b/src/Appwrite/Services/DocumentsDB.php @@ -746,7 +746,7 @@ public function listDocuments(string $databaseId, string $collectionId, ?array $ * * @throws AppwriteException */ - public function createDocument(string $databaseId, string $collectionId, string $documentId, array $data, ?array $permissions = null): \Appwrite\Models\Document + public function createDocument(string $databaseId, string $collectionId, string $documentId, array $data, ?array $permissions = null, ?string $transactionId = null): \Appwrite\Models\Document { $apiPath = str_replace( ['{databaseId}', '{collectionId}'], @@ -764,6 +764,10 @@ public function createDocument(string $databaseId, string $collectionId, string $apiParams['permissions'] = $permissions; } + if (!is_null($transactionId)) { + $apiParams['transactionId'] = $transactionId; + } + $apiHeaders = []; $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); $apiHeaders['content-type'] = 'application/json'; @@ -791,7 +795,7 @@ public function createDocument(string $databaseId, string $collectionId, string * * @throws AppwriteException */ - public function createDocuments(string $databaseId, string $collectionId, array $documents): \Appwrite\Models\DocumentList + public function createDocuments(string $databaseId, string $collectionId, array $documents, ?string $transactionId = null): \Appwrite\Models\DocumentList { $apiPath = str_replace( ['{databaseId}', '{collectionId}'], @@ -804,6 +808,10 @@ public function createDocuments(string $databaseId, string $collectionId, array $apiParams['collectionId'] = $collectionId; $apiParams['documents'] = $documents; + if (!is_null($transactionId)) { + $apiParams['transactionId'] = $transactionId; + } + $apiHeaders = []; $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); $apiHeaders['content-type'] = 'application/json'; diff --git a/src/Appwrite/Services/Mongo.php b/src/Appwrite/Services/Mongo.php index 71c6eda3..21771ece 100644 --- a/src/Appwrite/Services/Mongo.php +++ b/src/Appwrite/Services/Mongo.php @@ -797,14 +797,14 @@ public function deleteBranch(string $databaseId, string $branchId): \Appwrite\Mo } /** - * Rotate the primary connection credentials for a dedicated database. - * Generates a new password and updates the database atomically. Previous - * credentials stop working immediately. Returns the database with a refreshed - * connection string carrying the new password. + * Queue a rotation of the primary connection credentials for a dedicated + * database. A hibernated database is woken by the worker before rotation. + * List database operations until the returned operation reaches a terminal + * status, then fetch the database again for the refreshed connection string. * * @throws AppwriteException */ - public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabase + public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabaseOperation { $apiPath = str_replace( ['{databaseId}'], @@ -831,7 +831,7 @@ public function updateCredentials(string $databaseId): \Appwrite\Models\Dedicate throw new \UnexpectedValueException('Expected array response when hydrating a response model.'); } - return \Appwrite\Models\DedicatedDatabase::from($response); + return \Appwrite\Models\DedicatedDatabaseOperation::from($response); } /** diff --git a/src/Appwrite/Services/Mysql.php b/src/Appwrite/Services/Mysql.php index 36cb6f87..95f4cd35 100644 --- a/src/Appwrite/Services/Mysql.php +++ b/src/Appwrite/Services/Mysql.php @@ -797,14 +797,14 @@ public function deleteBranch(string $databaseId, string $branchId): \Appwrite\Mo } /** - * Rotate the primary connection credentials for a dedicated database. - * Generates a new password and updates the database atomically. Previous - * credentials stop working immediately. Returns the database with a refreshed - * connection string carrying the new password. + * Queue a rotation of the primary connection credentials for a dedicated + * database. A hibernated database is woken by the worker before rotation. + * List database operations until the returned operation reaches a terminal + * status, then fetch the database again for the refreshed connection string. * * @throws AppwriteException */ - public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabase + public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabaseOperation { $apiPath = str_replace( ['{databaseId}'], @@ -831,7 +831,7 @@ public function updateCredentials(string $databaseId): \Appwrite\Models\Dedicate throw new \UnexpectedValueException('Expected array response when hydrating a response model.'); } - return \Appwrite\Models\DedicatedDatabase::from($response); + return \Appwrite\Models\DedicatedDatabaseOperation::from($response); } /** diff --git a/src/Appwrite/Services/Postgresql.php b/src/Appwrite/Services/Postgresql.php index 5ed11274..536e5730 100644 --- a/src/Appwrite/Services/Postgresql.php +++ b/src/Appwrite/Services/Postgresql.php @@ -797,14 +797,14 @@ public function deleteBranch(string $databaseId, string $branchId): \Appwrite\Mo } /** - * Rotate the primary connection credentials for a dedicated database. - * Generates a new password and updates the database atomically. Previous - * credentials stop working immediately. Returns the database with a refreshed - * connection string carrying the new password. + * Queue a rotation of the primary connection credentials for a dedicated + * database. A hibernated database is woken by the worker before rotation. + * List database operations until the returned operation reaches a terminal + * status, then fetch the database again for the refreshed connection string. * * @throws AppwriteException */ - public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabase + public function updateCredentials(string $databaseId): \Appwrite\Models\DedicatedDatabaseOperation { $apiPath = str_replace( ['{databaseId}'], @@ -831,7 +831,7 @@ public function updateCredentials(string $databaseId): \Appwrite\Models\Dedicate throw new \UnexpectedValueException('Expected array response when hydrating a response model.'); } - return \Appwrite\Models\DedicatedDatabase::from($response); + return \Appwrite\Models\DedicatedDatabaseOperation::from($response); } /** diff --git a/src/Appwrite/Services/Project.php b/src/Appwrite/Services/Project.php index 04a2c152..18292185 100644 --- a/src/Appwrite/Services/Project.php +++ b/src/Appwrite/Services/Project.php @@ -961,6 +961,43 @@ public function updateOAuth2Box(?string $clientId = null, ?string $clientSecret return \Appwrite\Models\OAuth2Box::from($response); } + /** + * Update the project OAuth2 Cloudflare configuration. + * + * @throws AppwriteException + */ + public function updateOAuth2Cloudflare(?string $clientId = null, ?string $clientSecret = null, ?bool $enabled = null): \Appwrite\Models\OAuth2Cloudflare + { + $apiPath = str_replace( + [], + [], + '/project/oauth2/cloudflare' + ); + + $apiParams = []; + $apiParams['clientId'] = $clientId; + $apiParams['clientSecret'] = $clientSecret; + $apiParams['enabled'] = $enabled; + + $apiHeaders = []; + $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); + $apiHeaders['content-type'] = 'application/json'; + $apiHeaders['accept'] = 'application/json'; + + $response = $this->client->call( + Client::METHOD_PATCH, + $apiPath, + $apiHeaders, + $apiParams + ); + + if (!is_array($response)) { + throw new \UnexpectedValueException('Expected array response when hydrating a response model.'); + } + + return \Appwrite\Models\OAuth2Cloudflare::from($response); + } + /** * Update the project OAuth2 Dailymotion configuration. * @@ -1789,6 +1826,43 @@ public function updateOAuth2Podio(?string $clientId = null, ?string $clientSecre return \Appwrite\Models\OAuth2Podio::from($response); } + /** + * Update the project OAuth2 Resend configuration. + * + * @throws AppwriteException + */ + public function updateOAuth2Resend(?string $clientId = null, ?string $clientSecret = null, ?bool $enabled = null): \Appwrite\Models\OAuth2Resend + { + $apiPath = str_replace( + [], + [], + '/project/oauth2/resend' + ); + + $apiParams = []; + $apiParams['clientId'] = $clientId; + $apiParams['clientSecret'] = $clientSecret; + $apiParams['enabled'] = $enabled; + + $apiHeaders = []; + $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); + $apiHeaders['content-type'] = 'application/json'; + $apiHeaders['accept'] = 'application/json'; + + $response = $this->client->call( + Client::METHOD_PATCH, + $apiPath, + $apiHeaders, + $apiParams + ); + + if (!is_array($response)) { + throw new \UnexpectedValueException('Expected array response when hydrating a response model.'); + } + + return \Appwrite\Models\OAuth2Resend::from($response); + } + /** * Update the project OAuth2 Salesforce configuration. * @@ -2276,7 +2350,7 @@ public function updateOAuth2Zoom(?string $clientId = null, ?string $clientSecret * * @throws AppwriteException */ - public function getOAuth2Provider(ProjectOAuthProviderId $providerId): \Appwrite\Models\OAuth2Github|\Appwrite\Models\OAuth2Discord|\Appwrite\Models\OAuth2Figma|\Appwrite\Models\OAuth2Dropbox|\Appwrite\Models\OAuth2Dailymotion|\Appwrite\Models\OAuth2Bitbucket|\Appwrite\Models\OAuth2Bitly|\Appwrite\Models\OAuth2Box|\Appwrite\Models\OAuth2Autodesk|\Appwrite\Models\OAuth2Google|\Appwrite\Models\OAuth2Zoom|\Appwrite\Models\OAuth2Zoho|\Appwrite\Models\OAuth2Yandex|\Appwrite\Models\OAuth2X|\Appwrite\Models\OAuth2WordPress|\Appwrite\Models\OAuth2Twitch|\Appwrite\Models\OAuth2Stripe|\Appwrite\Models\OAuth2Spotify|\Appwrite\Models\OAuth2Slack|\Appwrite\Models\OAuth2Podio|\Appwrite\Models\OAuth2Notion|\Appwrite\Models\OAuth2Salesforce|\Appwrite\Models\OAuth2Yahoo|\Appwrite\Models\OAuth2HuggingFace|\Appwrite\Models\OAuth2Linkedin|\Appwrite\Models\OAuth2Disqus|\Appwrite\Models\OAuth2Amazon|\Appwrite\Models\OAuth2Etsy|\Appwrite\Models\OAuth2Facebook|\Appwrite\Models\OAuth2Tradeshift|\Appwrite\Models\OAuth2Paypal|\Appwrite\Models\OAuth2Gitlab|\Appwrite\Models\OAuth2Authentik|\Appwrite\Models\OAuth2Auth0|\Appwrite\Models\OAuth2FusionAuth|\Appwrite\Models\OAuth2Keycloak|\Appwrite\Models\OAuth2Oidc|\Appwrite\Models\OAuth2Apple|\Appwrite\Models\OAuth2Okta|\Appwrite\Models\OAuth2Kick|\Appwrite\Models\OAuth2Microsoft + public function getOAuth2Provider(ProjectOAuthProviderId $providerId): \Appwrite\Models\OAuth2Github|\Appwrite\Models\OAuth2Discord|\Appwrite\Models\OAuth2Figma|\Appwrite\Models\OAuth2Dropbox|\Appwrite\Models\OAuth2Dailymotion|\Appwrite\Models\OAuth2Bitbucket|\Appwrite\Models\OAuth2Bitly|\Appwrite\Models\OAuth2Box|\Appwrite\Models\OAuth2Autodesk|\Appwrite\Models\OAuth2Google|\Appwrite\Models\OAuth2Zoom|\Appwrite\Models\OAuth2Zoho|\Appwrite\Models\OAuth2Yandex|\Appwrite\Models\OAuth2X|\Appwrite\Models\OAuth2WordPress|\Appwrite\Models\OAuth2Twitch|\Appwrite\Models\OAuth2Stripe|\Appwrite\Models\OAuth2Spotify|\Appwrite\Models\OAuth2Slack|\Appwrite\Models\OAuth2Podio|\Appwrite\Models\OAuth2Notion|\Appwrite\Models\OAuth2Salesforce|\Appwrite\Models\OAuth2Yahoo|\Appwrite\Models\OAuth2HuggingFace|\Appwrite\Models\OAuth2Resend|\Appwrite\Models\OAuth2Cloudflare|\Appwrite\Models\OAuth2Linkedin|\Appwrite\Models\OAuth2Disqus|\Appwrite\Models\OAuth2Amazon|\Appwrite\Models\OAuth2Etsy|\Appwrite\Models\OAuth2Facebook|\Appwrite\Models\OAuth2Tradeshift|\Appwrite\Models\OAuth2Paypal|\Appwrite\Models\OAuth2Gitlab|\Appwrite\Models\OAuth2Authentik|\Appwrite\Models\OAuth2Auth0|\Appwrite\Models\OAuth2FusionAuth|\Appwrite\Models\OAuth2Keycloak|\Appwrite\Models\OAuth2Oidc|\Appwrite\Models\OAuth2Apple|\Appwrite\Models\OAuth2Okta|\Appwrite\Models\OAuth2Kick|\Appwrite\Models\OAuth2Microsoft { $apiPath = str_replace( ['{providerId}'], @@ -2398,6 +2472,14 @@ public function getOAuth2Provider(ProjectOAuthProviderId $providerId): \Appwrite return \Appwrite\Models\OAuth2HuggingFace::from($response); } + if (($response['$id'] ?? null) === 'resend') { + return \Appwrite\Models\OAuth2Resend::from($response); + } + + if (($response['$id'] ?? null) === 'cloudflare') { + return \Appwrite\Models\OAuth2Cloudflare::from($response); + } + if (($response['$id'] ?? null) === 'linkedin') { return \Appwrite\Models\OAuth2Linkedin::from($response); } diff --git a/src/Appwrite/Services/TablesDB.php b/src/Appwrite/Services/TablesDB.php index 8bdd3dd7..73dfe40b 100644 --- a/src/Appwrite/Services/TablesDB.php +++ b/src/Appwrite/Services/TablesDB.php @@ -669,12 +669,12 @@ public function deleteMigration(string $databaseId, string $migrationId): string * * @throws AppwriteException */ - public function cutoverMigration(string $databaseId, string $migrationId): \Appwrite\Models\DatabaseMigration + public function createCutover(string $databaseId, string $migrationId): \Appwrite\Models\DatabaseMigration { $apiPath = str_replace( ['{databaseId}', '{migrationId}'], [$databaseId, $migrationId], - '/tablesdb/{databaseId}/migrations/{migrationId}/cutover' + '/tablesdb/{databaseId}/migrations/{migrationId}/cutovers' ); $apiParams = []; diff --git a/src/Appwrite/Services/VectorsDB.php b/src/Appwrite/Services/VectorsDB.php index b22626c7..45d9820e 100644 --- a/src/Appwrite/Services/VectorsDB.php +++ b/src/Appwrite/Services/VectorsDB.php @@ -742,7 +742,7 @@ public function listDocuments(string $databaseId, string $collectionId, ?array $ * * @throws AppwriteException */ - public function createDocument(string $databaseId, string $collectionId, string $documentId, array $data, ?array $permissions = null): \Appwrite\Models\Document + public function createDocument(string $databaseId, string $collectionId, string $documentId, array $data, ?array $permissions = null, ?string $transactionId = null): \Appwrite\Models\Document { $apiPath = str_replace( ['{databaseId}', '{collectionId}'], @@ -760,6 +760,10 @@ public function createDocument(string $databaseId, string $collectionId, string $apiParams['permissions'] = $permissions; } + if (!is_null($transactionId)) { + $apiParams['transactionId'] = $transactionId; + } + $apiHeaders = []; $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); $apiHeaders['content-type'] = 'application/json'; @@ -787,7 +791,7 @@ public function createDocument(string $databaseId, string $collectionId, string * * @throws AppwriteException */ - public function createDocuments(string $databaseId, string $collectionId, array $documents): \Appwrite\Models\DocumentList + public function createDocuments(string $databaseId, string $collectionId, array $documents, ?string $transactionId = null): \Appwrite\Models\DocumentList { $apiPath = str_replace( ['{databaseId}', '{collectionId}'], @@ -800,6 +804,10 @@ public function createDocuments(string $databaseId, string $collectionId, array $apiParams['collectionId'] = $collectionId; $apiParams['documents'] = $documents; + if (!is_null($transactionId)) { + $apiParams['transactionId'] = $transactionId; + } + $apiHeaders = []; $apiHeaders['X-Appwrite-Project'] = $this->client->getConfig('project'); $apiHeaders['content-type'] = 'application/json'; diff --git a/tests/Appwrite/Services/DocumentsDBTest.php b/tests/Appwrite/Services/DocumentsDBTest.php index 3a9ffdda..183fdefc 100644 --- a/tests/Appwrite/Services/DocumentsDBTest.php +++ b/tests/Appwrite/Services/DocumentsDBTest.php @@ -979,6 +979,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", diff --git a/tests/Appwrite/Services/MongoTest.php b/tests/Appwrite/Services/MongoTest.php index e8b0a46f..aaa2b0ea 100644 --- a/tests/Appwrite/Services/MongoTest.php +++ b/tests/Appwrite/Services/MongoTest.php @@ -40,6 +40,7 @@ public function testMethodList(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -104,6 +105,7 @@ public function testMethodCreate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -205,6 +207,7 @@ public function testMethodGet(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -269,6 +272,7 @@ public function testMethodUpdate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -632,7 +636,7 @@ public function testMethodListBranches(): void "password" => "********", "ssl" => true, "engine" => "postgresql", - "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=disable" + "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=require" ] ] ]; @@ -667,6 +671,7 @@ public function testMethodCreateBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -731,6 +736,7 @@ public function testMethodDeleteBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -784,50 +790,12 @@ public function testMethodUpdateCredentials(): void $data = [ "\$id" => "5e5ea5c16897e", "\$createdAt" => "2020-10-15T06:38:00.000+00:00", - "\$updatedAt" => "2020-10-15T06:38:00.000+00:00", - "projectId" => "5e5ea5c16897e", - "name" => "My Production Database", - "api" => "postgresql", - "engine" => "postgresql", - "version" => "16", - "specification" => "s-2vcpu-2gb", - "backend" => "edge", - "hostname" => "db-myproject-mydb.fra.appwrite.center", - "connectionPort" => 5432, - "connectionUser" => "appwrite_user", - "connectionPassword" => "••••••••", - "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", - "ssl" => true, - "status" => "ready", - "containerStatus" => "active", - "lifecycleState" => "active", - "idleTimeoutMinutes" => 15, - "cpu" => 2000, - "memory" => 4096, - "storage" => 100, - "storageClass" => "ssd", - "storageMaxGb" => 100, - "nodePool" => "db-pool-4vcpu-8gb", - "replicas" => 2, - "syncMode" => "async", - "networkMaxConnections" => 500, - "networkIdleTimeoutSeconds" => 900, - "networkIPAllowlist" => [], - "backupEnabled" => true, - "pitr" => true, - "pitrRetentionDays" => 14, - "storageAutoscaling" => true, - "storageAutoscalingThresholdPercent" => 85, - "storageAutoscalingMaxGb" => 500, - "maintenanceWindowDay" => "sun", - "maintenanceWindowHourUtc" => 3, - "metricsEnabled" => true, - "sqlApiEnabled" => true, - "sqlApiAllowedStatements" => [], - "sqlApiMaxRows" => 10000, - "sqlApiMaxBytes" => 10485760, - "sqlApiTimeoutSeconds" => 30, - "error" => "[ERROR]" + "databaseId" => "5e5ea5c16897e", + "type" => "update", + "status" => "completed", + "attempts" => 1, + "errorCode" => "Interrupted", + "errorMessage" => "[ERRORMESSAGE]" ]; $this->client @@ -841,7 +809,7 @@ public function testMethodUpdateCredentials(): void "" ); - $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabase::class, $response); + $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabaseOperation::class, $response); } public function testMethodCreateFailover(): void { @@ -860,6 +828,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -924,6 +893,7 @@ public function testMethodUpdateMaintenance(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -990,6 +960,7 @@ public function testMethodCreateMigration(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1278,6 +1249,7 @@ public function testMethodCreateUpgrade(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", diff --git a/tests/Appwrite/Services/MysqlTest.php b/tests/Appwrite/Services/MysqlTest.php index 04478bf4..85f067a6 100644 --- a/tests/Appwrite/Services/MysqlTest.php +++ b/tests/Appwrite/Services/MysqlTest.php @@ -40,6 +40,7 @@ public function testMethodList(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -104,6 +105,7 @@ public function testMethodCreate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -205,6 +207,7 @@ public function testMethodGet(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -269,6 +272,7 @@ public function testMethodUpdate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -632,7 +636,7 @@ public function testMethodListBranches(): void "password" => "********", "ssl" => true, "engine" => "postgresql", - "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=disable" + "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=require" ] ] ]; @@ -667,6 +671,7 @@ public function testMethodCreateBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -731,6 +736,7 @@ public function testMethodDeleteBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -784,50 +790,12 @@ public function testMethodUpdateCredentials(): void $data = [ "\$id" => "5e5ea5c16897e", "\$createdAt" => "2020-10-15T06:38:00.000+00:00", - "\$updatedAt" => "2020-10-15T06:38:00.000+00:00", - "projectId" => "5e5ea5c16897e", - "name" => "My Production Database", - "api" => "postgresql", - "engine" => "postgresql", - "version" => "16", - "specification" => "s-2vcpu-2gb", - "backend" => "edge", - "hostname" => "db-myproject-mydb.fra.appwrite.center", - "connectionPort" => 5432, - "connectionUser" => "appwrite_user", - "connectionPassword" => "••••••••", - "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", - "ssl" => true, - "status" => "ready", - "containerStatus" => "active", - "lifecycleState" => "active", - "idleTimeoutMinutes" => 15, - "cpu" => 2000, - "memory" => 4096, - "storage" => 100, - "storageClass" => "ssd", - "storageMaxGb" => 100, - "nodePool" => "db-pool-4vcpu-8gb", - "replicas" => 2, - "syncMode" => "async", - "networkMaxConnections" => 500, - "networkIdleTimeoutSeconds" => 900, - "networkIPAllowlist" => [], - "backupEnabled" => true, - "pitr" => true, - "pitrRetentionDays" => 14, - "storageAutoscaling" => true, - "storageAutoscalingThresholdPercent" => 85, - "storageAutoscalingMaxGb" => 500, - "maintenanceWindowDay" => "sun", - "maintenanceWindowHourUtc" => 3, - "metricsEnabled" => true, - "sqlApiEnabled" => true, - "sqlApiAllowedStatements" => [], - "sqlApiMaxRows" => 10000, - "sqlApiMaxBytes" => 10485760, - "sqlApiTimeoutSeconds" => 30, - "error" => "[ERROR]" + "databaseId" => "5e5ea5c16897e", + "type" => "update", + "status" => "completed", + "attempts" => 1, + "errorCode" => "Interrupted", + "errorMessage" => "[ERRORMESSAGE]" ]; $this->client @@ -841,7 +809,7 @@ public function testMethodUpdateCredentials(): void "" ); - $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabase::class, $response); + $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabaseOperation::class, $response); } public function testMethodCreateExecution(): void { @@ -890,6 +858,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -954,6 +923,7 @@ public function testMethodUpdateMaintenance(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1020,6 +990,7 @@ public function testMethodCreateMigration(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1364,6 +1335,7 @@ public function testMethodCreateUpgrade(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", diff --git a/tests/Appwrite/Services/PostgresqlTest.php b/tests/Appwrite/Services/PostgresqlTest.php index 4c90a6ef..f4204910 100644 --- a/tests/Appwrite/Services/PostgresqlTest.php +++ b/tests/Appwrite/Services/PostgresqlTest.php @@ -40,6 +40,7 @@ public function testMethodList(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -104,6 +105,7 @@ public function testMethodCreate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -205,6 +207,7 @@ public function testMethodGet(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -269,6 +272,7 @@ public function testMethodUpdate(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -632,7 +636,7 @@ public function testMethodListBranches(): void "password" => "********", "ssl" => true, "engine" => "postgresql", - "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=disable" + "connectionString" => "postgresql://appwrite:****@db-myproject-mydb-a1b2c3d4.fra.appwrite.center:5432/db-myproject-mydb-a1b2c3d4?sslmode=require" ] ] ]; @@ -667,6 +671,7 @@ public function testMethodCreateBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -731,6 +736,7 @@ public function testMethodDeleteBranch(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -784,50 +790,12 @@ public function testMethodUpdateCredentials(): void $data = [ "\$id" => "5e5ea5c16897e", "\$createdAt" => "2020-10-15T06:38:00.000+00:00", - "\$updatedAt" => "2020-10-15T06:38:00.000+00:00", - "projectId" => "5e5ea5c16897e", - "name" => "My Production Database", - "api" => "postgresql", - "engine" => "postgresql", - "version" => "16", - "specification" => "s-2vcpu-2gb", - "backend" => "edge", - "hostname" => "db-myproject-mydb.fra.appwrite.center", - "connectionPort" => 5432, - "connectionUser" => "appwrite_user", - "connectionPassword" => "••••••••", - "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", - "ssl" => true, - "status" => "ready", - "containerStatus" => "active", - "lifecycleState" => "active", - "idleTimeoutMinutes" => 15, - "cpu" => 2000, - "memory" => 4096, - "storage" => 100, - "storageClass" => "ssd", - "storageMaxGb" => 100, - "nodePool" => "db-pool-4vcpu-8gb", - "replicas" => 2, - "syncMode" => "async", - "networkMaxConnections" => 500, - "networkIdleTimeoutSeconds" => 900, - "networkIPAllowlist" => [], - "backupEnabled" => true, - "pitr" => true, - "pitrRetentionDays" => 14, - "storageAutoscaling" => true, - "storageAutoscalingThresholdPercent" => 85, - "storageAutoscalingMaxGb" => 500, - "maintenanceWindowDay" => "sun", - "maintenanceWindowHourUtc" => 3, - "metricsEnabled" => true, - "sqlApiEnabled" => true, - "sqlApiAllowedStatements" => [], - "sqlApiMaxRows" => 10000, - "sqlApiMaxBytes" => 10485760, - "sqlApiTimeoutSeconds" => 30, - "error" => "[ERROR]" + "databaseId" => "5e5ea5c16897e", + "type" => "update", + "status" => "completed", + "attempts" => 1, + "errorCode" => "Interrupted", + "errorMessage" => "[ERRORMESSAGE]" ]; $this->client @@ -841,7 +809,7 @@ public function testMethodUpdateCredentials(): void "" ); - $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabase::class, $response); + $this->assertInstanceOf(\Appwrite\Models\DedicatedDatabaseOperation::class, $response); } public function testMethodCreateExecution(): void { @@ -918,6 +886,7 @@ public function testMethodCreateExtension(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -983,6 +952,7 @@ public function testMethodDeleteExtension(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1048,6 +1018,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1112,6 +1083,7 @@ public function testMethodUpdateMaintenance(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1178,6 +1150,7 @@ public function testMethodCreateMigration(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -1522,6 +1495,7 @@ public function testMethodCreateUpgrade(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", diff --git a/tests/Appwrite/Services/ProjectTest.php b/tests/Appwrite/Services/ProjectTest.php index edd1009e..0d0b6cb3 100644 --- a/tests/Appwrite/Services/ProjectTest.php +++ b/tests/Appwrite/Services/ProjectTest.php @@ -817,6 +817,26 @@ public function testMethodUpdateOAuth2Box(): void $this->assertInstanceOf(\Appwrite\Models\OAuth2Box::class, $response); } + public function testMethodUpdateOAuth2Cloudflare(): void + { + $data = [ + "\$id" => "github", + "enabled" => true, + "clientId" => "4b866000000000000000000000c9e4e2", + "clientSecret" => "cfoc_5Q6YRl0000000000000000000000000000000000003d214f" + ]; + + $this->client + ->allows()->call(Mockery::any(), Mockery::any(), Mockery::any(), Mockery::any()) + ->andReturn($data); + $this->client + ->allows()->getConfig(Mockery::any()) + ->andReturn(''); + + $response = $this->project->updateOAuth2Cloudflare(); + + $this->assertInstanceOf(\Appwrite\Models\OAuth2Cloudflare::class, $response); + } public function testMethodUpdateOAuth2Dailymotion(): void { $data = [ @@ -1270,6 +1290,26 @@ public function testMethodUpdateOAuth2Podio(): void $this->assertInstanceOf(\Appwrite\Models\OAuth2Podio::class, $response); } + public function testMethodUpdateOAuth2Resend(): void + { + $data = [ + "\$id" => "github", + "enabled" => true, + "clientId" => "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "clientSecret" => "9c1e4b00000000000000000000000000000000000000000000000000a72d5f4" + ]; + + $this->client + ->allows()->call(Mockery::any(), Mockery::any(), Mockery::any(), Mockery::any()) + ->andReturn($data); + $this->client + ->allows()->getConfig(Mockery::any()) + ->andReturn(''); + + $response = $this->project->updateOAuth2Resend(); + + $this->assertInstanceOf(\Appwrite\Models\OAuth2Resend::class, $response); + } public function testMethodUpdateOAuth2Salesforce(): void { $data = [ diff --git a/tests/Appwrite/Services/SitesTest.php b/tests/Appwrite/Services/SitesTest.php index b80a5549..1dd5e5aa 100644 --- a/tests/Appwrite/Services/SitesTest.php +++ b/tests/Appwrite/Services/SitesTest.php @@ -174,8 +174,7 @@ public function testMethodListFrameworks(): void "key" => "static", "installCommand" => "npm install", "buildCommand" => "npm run build", - "outputDirectory" => "./dist", - "fallbackFile" => "index.html" + "outputDirectory" => "./dist" ] ] ] diff --git a/tests/Appwrite/Services/TablesDBTest.php b/tests/Appwrite/Services/TablesDBTest.php index 010f38c2..51b883a9 100644 --- a/tests/Appwrite/Services/TablesDBTest.php +++ b/tests/Appwrite/Services/TablesDBTest.php @@ -330,6 +330,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready", @@ -507,7 +508,7 @@ public function testMethodDeleteMigration(): void $this->assertSame($data, $response); } - public function testMethodCutoverMigration(): void + public function testMethodCreateCutover(): void { $data = [ "\$id" => "5e5ea5c16897e", @@ -536,7 +537,7 @@ public function testMethodCutoverMigration(): void ->allows()->getConfig(Mockery::any()) ->andReturn(''); - $response = $this->tablesDB->cutoverMigration( + $response = $this->tablesDB->createCutover( "", "" ); diff --git a/tests/Appwrite/Services/VectorsDBTest.php b/tests/Appwrite/Services/VectorsDBTest.php index 5e07adb6..fdb22e76 100644 --- a/tests/Appwrite/Services/VectorsDBTest.php +++ b/tests/Appwrite/Services/VectorsDBTest.php @@ -959,6 +959,7 @@ public function testMethodCreateFailover(): void "connectionPort" => 5432, "connectionUser" => "appwrite_user", "connectionPassword" => "••••••••", + "credentialGeneration" => 1, "connectionString" => "postgresql://user:pass@db-myproject-mydb.fra.appwrite.center:5432/postgres?sslmode=require", "ssl" => true, "status" => "ready",